diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 32bdd4316..da5c4f04e 100755 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -52,3 +52,4 @@ test: imports: - simcoon - simcoon._core + - simcoon.modular diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/damage_mechanism.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/damage_mechanism.hpp new file mode 100644 index 000000000..8fdf14042 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/damage_mechanism.hpp @@ -0,0 +1,194 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file damage_mechanism.hpp + * @brief Scalar damage mechanism for continuum damage mechanics (CDM) + * + * This mechanism implements isotropic scalar damage following the + * effective stress concept: + * sigma_eff = sigma / (1 - D) + * + * Damage evolution is driven by an energy-based criterion: + * Y = (1/2) * sigma : S : sigma (damage driving force) + * + * Damage evolution follows: + * D = f(Y_max) + * + * where Y_max is the maximum damage driving force reached in history. + * + * Supported damage evolution laws: + * - Linear: D = (Y - Y_0) / (Y_c - Y_0) for Y > Y_0 + * Props: Y_0, Y_c + * - Exponential: D = 1 - exp(-A * (Y - Y_0) / (Y_c - Y_0)) for Y > Y_0 + * Props: Y_0, Y_c, A + * - Power law: D = ((Y - Y_0) / (Y_c - Y_0))^n for Y > Y_0 + * Props: Y_0, Y_c, n + * - Weibull: D = 1 - exp(-((Y - Y_0) / A)^n) for Y > Y_0 + * Props: Y_0, Y_c, A (scale), n (shape) + * + * @see StrainMechanism, ModularUMAT + * @version 1.0 + */ + +#pragma once + +#include +#include +#include + +namespace simcoon { + +/** + * @brief Type of damage evolution law + */ +enum class DamageType { + LINEAR = 0, ///< Linear damage evolution + EXPONENTIAL = 1, ///< Exponential damage evolution + POWER_LAW = 2, ///< Power-law damage evolution + WEIBULL = 3 ///< Weibull-based damage +}; + +/** + * @brief Scalar damage mechanism + * + * Implements isotropic damage following continuum damage mechanics. + * The damage variable D reduces the effective stiffness: + * L_damaged = (1 - D) * L + */ +class DamageMechanism final : public StrainMechanism { +private: + DamageType damage_type_; ///< Type of damage evolution law + + // Damage parameters + double Y_0_; ///< Damage threshold (no damage below this) + double Y_c_; ///< Critical damage driving force + double D_c_; ///< Critical damage value (default: 0.99) + double A_; ///< Damage evolution parameter + double n_; ///< Power law exponent + + // Cached values + mutable double Y_current_; ///< Current damage driving force + mutable double D_current_; ///< Current damage + mutable double dD_dY_; ///< Derivative of damage w.r.t. driving force + mutable arma::mat M_cached_; ///< Cached compliance (set from L on first use) + mutable bool M_cached_valid_; ///< Whether M_cached_ is valid + +public: + /** + * @brief Constructor + * @param type Type of damage evolution law (default: LINEAR) + */ + explicit DamageMechanism(DamageType type = DamageType::LINEAR); + + // StrainMechanism interface + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + + [[nodiscard]] int num_constraints() const override { return 1; } + [[nodiscard]] MechanismType type() const override { return MechanismType::DAMAGE; } + + void compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit + ) const override; + + void compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map + ) const override; + + void compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset + ) const override; + + arma::vec inelastic_strain(const InternalVariableCollection& ivc) const override; + + void update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc + ) override; + + void tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt + ) const override; + + void compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d + ) const override; + + // Damage-specific methods + + /** + * @brief Compute damage driving force from stress + * @param sigma Stress (6-component Voigt) + * @param S Compliance tensor (6x6) + * @return Damage driving force Y + */ + double compute_driving_force(const arma::vec& sigma, const arma::mat& S) const; + + /** + * @brief Compute damage from driving force + * @param Y Damage driving force + * @param Y_max Maximum driving force in history + * @return Damage value D + */ + double compute_damage(double Y, double Y_max) const; + + /** + * @brief Get the current damage value + * @param ivc Internal variable collection + * @return Current damage D + */ + double get_damage(const InternalVariableCollection& ivc) const; + + /** + * @brief Get damaged stiffness tensor + * @param L Undamaged stiffness + * @param D Damage value + * @return Damaged stiffness (1-D)*L + */ + static arma::mat damaged_stiffness(const arma::mat& L, double D); + + // Accessors + [[nodiscard]] DamageType damage_type() const noexcept { return damage_type_; } + [[nodiscard]] double Y_0() const noexcept { return Y_0_; } + [[nodiscard]] double Y_c() const noexcept { return Y_c_; } + [[nodiscard]] double D_c() const noexcept { return D_c_; } +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/elasticity_module.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/elasticity_module.hpp new file mode 100644 index 000000000..c9e25762a --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/elasticity_module.hpp @@ -0,0 +1,212 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file elasticity_module.hpp + * @brief Elasticity module for modular UMAT. + * + * This module wraps existing elasticity functions (L_iso, L_ortho, L_isotrans) + * to provide a configurable elasticity component. + * + * @version 1.0 + */ + +#pragma once + +#include + +namespace simcoon { + +/** + * @brief Types of linear elasticity + */ +enum class ElasticityType { + ISOTROPIC = 0, ///< Isotropic: E, nu, alpha + CUBIC = 1, ///< Cubic: E, nu, G, alpha (3 independent elastic constants) + TRANSVERSE_ISOTROPIC = 2, ///< Transverse isotropic: EL, ET, nuTL, nuTT, GLT, alpha_L, alpha_T + ORTHOTROPIC = 3 ///< Orthotropic: E1, E2, E3, nu12, nu13, nu23, G12, G13, G23, alpha1, alpha2, alpha3 +}; + +/** + * @brief Elasticity module for modular UMAT + * + * This class encapsulates the computation of the elastic stiffness tensor + * and coefficient of thermal expansion for different types of linear elasticity. + */ +class ElasticityModule { +private: + ElasticityType type_; + arma::mat L_; ///< 6x6 stiffness tensor + arma::mat M_; ///< 6x6 compliance tensor + arma::vec alpha_; ///< 6-component CTE (Voigt notation) + bool configured_; + +public: + /** + * @brief Default constructor + */ + ElasticityModule(); + + // Default copy/move/destructor + ElasticityModule(const ElasticityModule&) = default; + ElasticityModule(ElasticityModule&&) = default; + ElasticityModule& operator=(const ElasticityModule&) = default; + ElasticityModule& operator=(ElasticityModule&&) = default; + ~ElasticityModule() = default; + + // ========== Configuration ========== + + /** + * @brief Configure as isotropic elasticity + * @param E Young's modulus + * @param nu Poisson's ratio + * @param alpha Coefficient of thermal expansion (scalar, isotropic) + */ + void configure_isotropic(double E, double nu, double alpha); + + /** + * @brief Configure as cubic elasticity + * @param E Young's modulus + * @param nu Poisson's ratio + * @param G Shear modulus (independent from E and nu for cubic symmetry) + * @param alpha Coefficient of thermal expansion (scalar, isotropic CTE) + * + * Cubic symmetry has 3 independent elastic constants. Unlike isotropic + * materials where G = E/(2(1+nu)), G is an independent parameter here. + * The Zener anisotropy ratio A = 2*G*(1+nu)/E quantifies the deviation + * from isotropy (A=1 for isotropic). + * + * Alternative: use configure_cubic_Cii() with C11, C12, C44 directly. + */ + void configure_cubic(double E, double nu, double G, double alpha); + + /** + * @brief Configure as cubic elasticity using Cii convention + * @param C11 Stiffness component C11 + * @param C12 Stiffness component C12 + * @param C44 Stiffness component C44 + * @param alpha Coefficient of thermal expansion (scalar, isotropic CTE) + */ + void configure_cubic_Cii(double C11, double C12, double C44, double alpha); + + /** + * @brief Configure as transversely isotropic elasticity + * @param EL Longitudinal Young's modulus + * @param ET Transverse Young's modulus + * @param nuTL Poisson's ratio (transverse-longitudinal) + * @param nuTT Poisson's ratio (transverse-transverse) + * @param GLT Shear modulus + * @param alpha_L Longitudinal CTE + * @param alpha_T Transverse CTE + * @param axis Axis of symmetry (1=x, 2=y, 3=z) + */ + void configure_transverse_isotropic(double EL, double ET, double nuTL, double nuTT, + double GLT, double alpha_L, double alpha_T, int axis = 3); + + /** + * @brief Configure as orthotropic elasticity + * @param E1 Young's modulus in direction 1 + * @param E2 Young's modulus in direction 2 + * @param E3 Young's modulus in direction 3 + * @param nu12 Poisson's ratio 1-2 + * @param nu13 Poisson's ratio 1-3 + * @param nu23 Poisson's ratio 2-3 + * @param G12 Shear modulus 1-2 + * @param G13 Shear modulus 1-3 + * @param G23 Shear modulus 2-3 + * @param alpha1 CTE in direction 1 + * @param alpha2 CTE in direction 2 + * @param alpha3 CTE in direction 3 + */ + void configure_orthotropic(double E1, double E2, double E3, + double nu12, double nu13, double nu23, + double G12, double G13, double G23, + double alpha1, double alpha2, double alpha3); + + /** + * @brief Configure from props array + * @param type Elasticity type + * @param props Material properties vector + * @param offset Current offset in props (will be updated) + */ + void configure(ElasticityType type, const arma::vec& props, int& offset); + + // ========== Accessors ========== + + /** + * @brief Get the elasticity type + * @return The configured type + */ + [[nodiscard]] ElasticityType type() const noexcept { return type_; } + + /** + * @brief Check if the module is configured + * @return True if configure has been called + */ + [[nodiscard]] bool is_configured() const noexcept { return configured_; } + + /** + * @brief Get the 6x6 stiffness tensor + * @return Const reference to L + */ + [[nodiscard]] const arma::mat& L() const noexcept { return L_; } + + /** + * @brief Get the 6x6 compliance tensor + * @return Const reference to M + */ + [[nodiscard]] const arma::mat& M() const noexcept { return M_; } + + /** + * @brief Get the CTE tensor + * @return Const reference to alpha (6 components) + */ + [[nodiscard]] const arma::vec& alpha() const noexcept { return alpha_; } + + // ========== Derived Quantities ========== + + /** + * @brief Get damaged stiffness tensor + * @param d Damage variable (0 = undamaged, 1 = fully damaged) + * @return Degraded stiffness (1-d)*L + */ + arma::mat damaged_L(double d) const; + + /** + * @brief Get thermal strain + * @param DT Temperature increment + * @return Thermal strain vector (alpha * DT) + */ + arma::vec thermal_strain(double DT) const; + + /** + * @brief Get number of props consumed by this elasticity type + * @param type The elasticity type + * @return Number of properties required + */ + [[nodiscard]] static constexpr int props_count(ElasticityType type) { + switch (type) { + case ElasticityType::ISOTROPIC: return 3; + case ElasticityType::CUBIC: return 4; + case ElasticityType::TRANSVERSE_ISOTROPIC: return 8; + case ElasticityType::ORTHOTROPIC: return 12; + } + return 0; + } +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/hardening.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/hardening.hpp new file mode 100644 index 000000000..360c4640c --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/hardening.hpp @@ -0,0 +1,370 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file hardening.hpp + * @brief Hardening modules for modular UMAT. + * + * Provides isotropic and kinematic hardening models: + * - Isotropic: Linear, Power-law, Voce, Combined Voce + * - Kinematic: Prager (linear), Armstrong-Frederick, Chaboche (multi-AF) + * + * @see PlasticityMechanism, ModularUMAT + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace simcoon { + +// ============================================================================ +// ISOTROPIC HARDENING +// ============================================================================ + +/** + * @brief Types of isotropic hardening + */ +enum class IsoHardType { + NONE = 0, ///< No isotropic hardening + LINEAR = 1, ///< Linear: R = H * p + POWER_LAW = 2, ///< Power law: R = k * p^m + VOCE = 3, ///< Voce saturation: R = Q * (1 - exp(-b*p)) + COMBINED_VOCE = 4 ///< Sum of N Voce terms +}; + +/** + * @brief Base class for isotropic hardening + */ +class IsotropicHardening { +public: + virtual ~IsotropicHardening() = default; + + /** + * @brief Configure from props array + * @param props Material properties vector + * @param offset Current offset in props (will be updated) + */ + virtual void configure(const arma::vec& props, int& offset) = 0; + + /** + * @brief Compute hardening stress R(p) + * @param p Accumulated plastic strain + * @return Hardening stress + */ + [[nodiscard]] virtual double R(double p) const = 0; + + /** + * @brief Compute hardening modulus dR/dp + * @param p Accumulated plastic strain + * @return Derivative of hardening stress + */ + [[nodiscard]] virtual double dR_dp(double p) const = 0; + + /** + * @brief Get the hardening type + * @return Hardening type enum + */ + [[nodiscard]] virtual IsoHardType type() const = 0; + + /** + * @brief Create an isotropic hardening instance + * @param type Hardening type + * @param N Number of terms (for COMBINED_VOCE) + * @return Unique pointer to hardening instance + */ + static std::unique_ptr create(IsoHardType type, int N = 1); + + /** + * @brief Get number of props consumed by this hardening type + * @param type The hardening type + * @param N Number of terms (for COMBINED_VOCE) + * @return Number of properties required + */ + [[nodiscard]] static constexpr int props_count(IsoHardType type, int N = 1) { + switch (type) { + case IsoHardType::NONE: return 0; + case IsoHardType::LINEAR: return 1; + case IsoHardType::POWER_LAW: return 2; + case IsoHardType::VOCE: return 2; + case IsoHardType::COMBINED_VOCE: return 2 * N; + } + return 0; + } +}; + +/** + * @brief No isotropic hardening + */ +class NoIsotropicHardening final : public IsotropicHardening { +public: + void configure(const arma::vec& props, int& offset) override {} + double R(double p) const override { return 0.0; } + double dR_dp(double p) const override { return 0.0; } + IsoHardType type() const override { return IsoHardType::NONE; } +}; + +/** + * @brief Linear isotropic hardening: R = H * p + */ +class LinearHardening final : public IsotropicHardening { +private: + double H_; ///< Hardening modulus +public: + LinearHardening() : H_(0.0) {} + void configure(const arma::vec& props, int& offset) override; + double R(double p) const override { return H_ * p; } + double dR_dp(double p) const override { return H_; } + IsoHardType type() const override { return IsoHardType::LINEAR; } +}; + +/** + * @brief Power-law isotropic hardening: R = k * p^m + */ +class PowerLawHardening final : public IsotropicHardening { +private: + double k_; ///< Hardening coefficient + double m_; ///< Hardening exponent +public: + PowerLawHardening() : k_(0.0), m_(1.0) {} + void configure(const arma::vec& props, int& offset) override; + double R(double p) const override; + double dR_dp(double p) const override; + IsoHardType type() const override { return IsoHardType::POWER_LAW; } +}; + +/** + * @brief Voce saturation hardening: R = Q * (1 - exp(-b*p)) + */ +class VoceHardening final : public IsotropicHardening { +private: + double Q_; ///< Saturation stress + double b_; ///< Hardening rate +public: + VoceHardening() : Q_(0.0), b_(0.0) {} + void configure(const arma::vec& props, int& offset) override; + double R(double p) const override; + double dR_dp(double p) const override; + IsoHardType type() const override { return IsoHardType::VOCE; } +}; + +/** + * @brief Combined Voce hardening: R = sum_i Q_i * (1 - exp(-b_i*p)) + */ +class CombinedVoceHardening final : public IsotropicHardening { +private: + int N_; ///< Number of Voce terms + arma::vec Q_; ///< Saturation stresses + arma::vec b_; ///< Hardening rates +public: + explicit CombinedVoceHardening(int N = 1) : N_(N), Q_(arma::zeros(N)), b_(arma::zeros(N)) {} + void configure(const arma::vec& props, int& offset) override; + double R(double p) const override; + double dR_dp(double p) const override; + IsoHardType type() const override { return IsoHardType::COMBINED_VOCE; } + int num_terms() const { return N_; } +}; + +// ============================================================================ +// KINEMATIC HARDENING +// ============================================================================ + +/** + * @brief Types of kinematic hardening + */ +enum class KinHardType { + NONE = 0, ///< No kinematic hardening + PRAGER = 1, ///< Linear Prager: X = (2/3)*C*alpha + ARMSTRONG_FREDERICK = 2, ///< Single AF: dX = (2/3)*C*dep - D*X*dp + CHABOCHE = 3 ///< Multiple AF terms +}; + +/** + * @brief Base class for kinematic hardening + */ +class KinematicHardening { +public: + virtual ~KinematicHardening() = default; + + /** + * @brief Configure from props array + * @param props Material properties vector + * @param offset Current offset in props (will be updated) + */ + virtual void configure(const arma::vec& props, int& offset) = 0; + + /** + * @brief Register internal variables for this hardening model + * @param ivc Internal variable collection + */ + virtual void register_variables(InternalVariableCollection& ivc) = 0; + + /** + * @brief Get total backstress X = sum(X_i) + * @param ivc Internal variable collection + * @return Total backstress tensor (6 components) + */ + virtual arma::vec total_backstress(const InternalVariableCollection& ivc) const = 0; + + /** + * @brief Compute flow direction for kinematic variable alpha_i + * @param i Index of the backstress (0 for single term) + * @param n Plastic flow direction + * @param ivc Internal variable collection + * @return Flow direction for alpha_i + */ + virtual arma::vec alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const = 0; + + /** + * @brief Compute total kinematic hardening modulus contribution + * @param n Plastic flow direction + * @param ivc Internal variable collection + * @return Hardening modulus contribution to K + */ + virtual double hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const = 0; + + /** + * @brief Update internal variables given plastic multiplier increment + * @param dp Plastic multiplier increment + * @param n Plastic flow direction + * @param ivc Internal variable collection + */ + virtual void update(double dp, const arma::vec& n, InternalVariableCollection& ivc) = 0; + + /** + * @brief Get the hardening type + * @return Hardening type enum + */ + [[nodiscard]] virtual KinHardType type() const = 0; + + /** + * @brief Get number of backstress terms + * @return Number of backstress tensors + */ + [[nodiscard]] virtual int num_backstresses() const = 0; + + /** + * @brief Create a kinematic hardening instance + * @param type Hardening type + * @param N Number of backstress terms (for CHABOCHE) + * @return Unique pointer to hardening instance + */ + static std::unique_ptr create(KinHardType type, int N = 1); + + /** + * @brief Get number of props consumed by this hardening type + * @param type The hardening type + * @param N Number of backstress terms (for CHABOCHE) + * @return Number of properties required + */ + [[nodiscard]] static constexpr int props_count(KinHardType type, int N = 1) { + switch (type) { + case KinHardType::NONE: return 0; + case KinHardType::PRAGER: return 1; + case KinHardType::ARMSTRONG_FREDERICK: return 2; + case KinHardType::CHABOCHE: return 2 * N; + } + return 0; + } +}; + +/** + * @brief No kinematic hardening + */ +class NoKinematicHardening final : public KinematicHardening { +public: + void configure(const arma::vec& props, int& offset) override {} + void register_variables(InternalVariableCollection& ivc) override {} + arma::vec total_backstress(const InternalVariableCollection& ivc) const override { + return arma::zeros(6); + } + arma::vec alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const override { + return arma::zeros(6); + } + double hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const override { + return 0.0; + } + void update(double dp, const arma::vec& n, InternalVariableCollection& ivc) override {} + KinHardType type() const override { return KinHardType::NONE; } + int num_backstresses() const override { return 0; } +}; + +/** + * @brief Linear Prager kinematic hardening: X = (2/3)*C*alpha + */ +class PragerHardening final : public KinematicHardening { +private: + double C_; ///< Kinematic hardening modulus +public: + PragerHardening() : C_(0.0) {} + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + arma::vec total_backstress(const InternalVariableCollection& ivc) const override; + arma::vec alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const override; + double hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const override; + void update(double dp, const arma::vec& n, InternalVariableCollection& ivc) override; + KinHardType type() const override { return KinHardType::PRAGER; } + int num_backstresses() const override { return 1; } +}; + +/** + * @brief Armstrong-Frederick kinematic hardening: dX = (2/3)*C*dep - D*X*dp + */ +class ArmstrongFrederickHardening final : public KinematicHardening { +private: + double C_; ///< Hardening parameter + double D_; ///< Dynamic recovery parameter +public: + ArmstrongFrederickHardening() : C_(0.0), D_(0.0) {} + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + arma::vec total_backstress(const InternalVariableCollection& ivc) const override; + arma::vec alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const override; + double hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const override; + void update(double dp, const arma::vec& n, InternalVariableCollection& ivc) override; + KinHardType type() const override { return KinHardType::ARMSTRONG_FREDERICK; } + int num_backstresses() const override { return 1; } +}; + +/** + * @brief Chaboche kinematic hardening: dX_i = (2/3)*C_i*dep - D_i*X_i*dp + * + * Multiple Armstrong-Frederick terms for capturing different hardening time scales. + */ +class ChabocheHardening final : public KinematicHardening { +private: + int N_; ///< Number of backstress terms + arma::vec C_; ///< Hardening parameters + arma::vec D_; ///< Dynamic recovery parameters +public: + explicit ChabocheHardening(int N = 1) : N_(N), C_(arma::zeros(N)), D_(arma::zeros(N)) {} + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + arma::vec total_backstress(const InternalVariableCollection& ivc) const override; + arma::vec alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const override; + double hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const override; + void update(double dp, const arma::vec& n, InternalVariableCollection& ivc) override; + KinHardType type() const override { return KinHardType::CHABOCHE; } + int num_backstresses() const override { return N_; } +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable.hpp new file mode 100644 index 000000000..790b9b6f1 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable.hpp @@ -0,0 +1,280 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file internal_variable.hpp + * @brief Type-safe internal variable for constitutive models. + * + * This class provides storage for internal variables used in constitutive laws + * (plasticity, viscoelasticity, damage, etc.) with support for: + * - Scalar variables (accumulated plastic strain, damage, etc.) + * - 6-component vectors in Voigt notation (plastic strain, backstress) + * - 6x6 matrices (for complex tensorial internal variables) + * + * @version 1.0 + */ + +#pragma once + +#include +#include +#include + +namespace simcoon { + +/** + * @brief Type enumeration for internal variables + */ +enum class IVarType { + SCALAR, ///< Single scalar value (double) + VECTOR_6, ///< 6-component vector (Voigt notation for symmetric tensors) + MATRIX_6x6 ///< 6x6 matrix (for fourth-order tensors in Voigt notation) +}; + +/** + * @brief Type-safe internal variable with automatic serialization + * + * This class encapsulates internal variables used in constitutive models, + * providing type safety, automatic pack/unpack to statev arrays, and + * support for objectivity through rotation operations. + */ +class InternalVariable { +private: + std::string name_; ///< Identifier for debugging and access + IVarType type_; ///< Storage type + + // Storage (only one active based on type_) + double scalar_value_; ///< Current scalar value + double scalar_start_; ///< Scalar value at start of increment + arma::vec vec_value_; ///< Current vector value (6 components) + arma::vec vec_start_; ///< Vector value at start of increment + arma::mat mat_value_; ///< Current matrix value (6x6) + arma::mat mat_start_; ///< Matrix value at start of increment + + bool requires_rotation_; ///< Whether to apply rotation for objectivity + unsigned int statev_offset_; ///< Position in flat statev array + +public: + /** + * @brief Construct a scalar internal variable + * @param name Identifier for the variable + * @param init Initial value (default: 0.0) + * @param rotate Whether rotation should be applied (default: false for scalars) + */ + InternalVariable(const std::string& name, double init = 0.0, bool rotate = false); + + /** + * @brief Construct a vector internal variable (6 Voigt components) + * @param name Identifier for the variable + * @param init Initial value (default: zeros) + * @param rotate Whether rotation should be applied (default: true for tensors) + */ + InternalVariable(const std::string& name, const arma::vec& init, bool rotate = true); + + /** + * @brief Construct a matrix internal variable (6x6) + * @param name Identifier for the variable + * @param init Initial value (default: zeros) + * @param rotate Whether rotation should be applied (default: true for tensors) + */ + InternalVariable(const std::string& name, const arma::mat& init, bool rotate = true); + + // Default copy/move/destructor + InternalVariable(const InternalVariable&) = default; + InternalVariable(InternalVariable&&) = default; + InternalVariable& operator=(const InternalVariable&) = default; + InternalVariable& operator=(InternalVariable&&) = default; + ~InternalVariable() = default; + + // ========== Type Information ========== + + /** + * @brief Get the variable type + * @return The IVarType of this variable + */ + [[nodiscard]] IVarType type() const noexcept { return type_; } + + /** + * @brief Get the variable name + * @return Reference to the name string + */ + [[nodiscard]] const std::string& name() const noexcept { return name_; } + + /** + * @brief Get the number of scalar values stored + * @return 1 for scalar, 6 for vector, 36 for matrix + */ + unsigned int size() const; + + /** + * @brief Check if this variable requires rotation for objectivity + * @return True if rotation should be applied + */ + [[nodiscard]] bool requires_rotation() const noexcept { return requires_rotation_; } + + // ========== Value Accessors (throw if wrong type) ========== + + /** + * @brief Access scalar value (mutable) + * @return Reference to scalar value + * @throws std::runtime_error if type is not SCALAR + */ + double& scalar(); + + /** + * @brief Access scalar value (const) + * @return Const reference to scalar value + * @throws std::runtime_error if type is not SCALAR + */ + const double& scalar() const; + + /** + * @brief Access vector value (mutable) + * @return Reference to vector value + * @throws std::runtime_error if type is not VECTOR_6 + */ + arma::vec& vec(); + + /** + * @brief Access vector value (const) + * @return Const reference to vector value + * @throws std::runtime_error if type is not VECTOR_6 + */ + const arma::vec& vec() const; + + /** + * @brief Access matrix value (mutable) + * @return Reference to matrix value + * @throws std::runtime_error if type is not MATRIX_6x6 + */ + arma::mat& mat(); + + /** + * @brief Access matrix value (const) + * @return Const reference to matrix value + * @throws std::runtime_error if type is not MATRIX_6x6 + */ + const arma::mat& mat() const; + + // ========== Start Value Accessors ========== + + /** + * @brief Access scalar start value + * @return Reference to scalar start value + */ + double& scalar_start(); + const double& scalar_start() const; + + /** + * @brief Access vector start value + * @return Reference to vector start value + */ + arma::vec& vec_start(); + const arma::vec& vec_start() const; + + /** + * @brief Access matrix start value + * @return Reference to matrix start value + */ + arma::mat& mat_start(); + const arma::mat& mat_start() const; + + // ========== Increment Computation ========== + + /** + * @brief Compute scalar increment (current - start) + * @return The increment value + */ + double delta_scalar() const; + + /** + * @brief Compute vector increment (current - start) + * @return The increment vector + */ + arma::vec delta_vec() const; + + /** + * @brief Compute matrix increment (current - start) + * @return The increment matrix + */ + arma::mat delta_mat() const; + + // ========== State Management ========== + + /** + * @brief Copy current value to start value + * + * Call at the beginning of an increment to save the state. + */ + void to_start(); + + /** + * @brief Copy start value to current value + * + * Call to restore state to beginning of increment. + */ + void set_start(); + + /** + * @brief Apply rotation for objectivity + * @param DR Rotation increment matrix (3x3) + * + * Applies rotation to maintain objectivity during large deformations. + * - Scalar variables: not rotated (scalars are frame-invariant) + * - Vector variables (6 Voigt): rotated via rotate_strain(value, DR) + * - Matrix variables (6x6): not yet supported (placeholder) + * + * Only applied if requires_rotation_ is true. By default, scalars + * have requires_rotation_ = false and tensorial quantities have + * requires_rotation_ = true. + * + * @see rotate_strain() in contimech.hpp + */ + void rotate(const arma::mat& DR); + + // ========== Serialization ========== + + /** + * @brief Set the offset in the statev array + * @param offset Starting index in statev + */ + void set_offset(unsigned int offset) { statev_offset_ = offset; } + + /** + * @brief Get the offset in the statev array + * @return The offset value + */ + [[nodiscard]] unsigned int offset() const noexcept { return statev_offset_; } + + /** + * @brief Pack current value into statev array + * @param statev The state variable vector to pack into + * + * Writes size() values starting at statev_offset_. + */ + void pack(arma::vec& statev) const; + + /** + * @brief Unpack value from statev array + * @param statev The state variable vector to unpack from + * + * Reads size() values starting at statev_offset_. + */ + void unpack(const arma::vec& statev); +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable_collection.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable_collection.hpp new file mode 100644 index 000000000..f275ddab9 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable_collection.hpp @@ -0,0 +1,246 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file internal_variable_collection.hpp + * @brief Collection of internal variables for modular constitutive models. + * + * This class manages multiple InternalVariable instances, providing: + * - Named registration and access + * - Automatic offset computation for statev serialization + * - Bulk operations (pack/unpack, rotate, state management) + * + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace simcoon { + +/** + * @brief Manages a collection of internal variables for constitutive models + * + * This class provides a container for InternalVariable instances with + * named access, automatic offset computation for statev serialization, + * and bulk operations for common tasks. + */ +class InternalVariableCollection { +private: + std::vector> variables_; + std::unordered_map name_to_index_; + unsigned int total_size_; + bool offsets_computed_; + +public: + /** + * @brief Default constructor + */ + InternalVariableCollection(); + + // Disable copy (due to unique_ptr) + InternalVariableCollection(const InternalVariableCollection&) = delete; + InternalVariableCollection& operator=(const InternalVariableCollection&) = delete; + + // Allow move + InternalVariableCollection(InternalVariableCollection&&) = default; + InternalVariableCollection& operator=(InternalVariableCollection&&) = default; + + ~InternalVariableCollection() = default; + + // ========== Registration ========== + + /** + * @brief Add a scalar internal variable + * @param name Unique identifier for the variable + * @param init Initial value (default: 0.0) + * @param rotate Whether rotation should be applied (default: false) + * @return Reference to the created variable + * @throws std::runtime_error if name already exists + */ + InternalVariable& add_scalar(const std::string& name, double init = 0.0, bool rotate = false); + + /** + * @brief Add a 6-component vector internal variable + * @param name Unique identifier for the variable + * @param init Initial value (default: zeros) + * @param rotate Whether rotation should be applied (default: true) + * @return Reference to the created variable + * @throws std::runtime_error if name already exists + */ + InternalVariable& add_vec(const std::string& name, const arma::vec& init = arma::zeros(6), bool rotate = true); + + /** + * @brief Add a 6x6 matrix internal variable + * @param name Unique identifier for the variable + * @param init Initial value (default: zeros) + * @param rotate Whether rotation should be applied (default: true) + * @return Reference to the created variable + * @throws std::runtime_error if name already exists + */ + InternalVariable& add_mat(const std::string& name, const arma::mat& init = arma::zeros(6, 6), bool rotate = true); + + // ========== Access ========== + + /** + * @brief Get a variable by name + * @param name The variable name + * @return Reference to the variable + * @throws std::runtime_error if name not found + */ + InternalVariable& get(const std::string& name); + + /** + * @brief Get a variable by name (const) + * @param name The variable name + * @return Const reference to the variable + * @throws std::runtime_error if name not found + */ + const InternalVariable& get(const std::string& name) const; + + /** + * @brief Check if a variable exists + * @param name The variable name + * @return True if the variable exists + */ + bool has(const std::string& name) const; + + /** + * @brief Get variable by index + * @param i Index in the collection + * @return Reference to the variable + */ + InternalVariable& operator[](size_t i); + + /** + * @brief Get variable by index (const) + * @param i Index in the collection + * @return Const reference to the variable + */ + const InternalVariable& operator[](size_t i) const; + + /** + * @brief Get the number of variables + * @return Number of registered variables + */ + [[nodiscard]] size_t size() const noexcept { return variables_.size(); } + + /** + * @brief Check if collection is empty + * @return True if no variables are registered + */ + [[nodiscard]] bool empty() const noexcept { return variables_.empty(); } + + // ========== Offset Management ========== + + /** + * @brief Get the total size needed in statev + * @return Total number of scalar values across all variables + */ + [[nodiscard]] unsigned int total_statev_size() const noexcept { return total_size_; } + + /** + * @brief Compute offsets for all variables + * @param base_offset Starting offset in statev (default: 0) + * + * This assigns sequential offsets to each variable based on their sizes. + * Must be called before pack_all/unpack_all. + */ + void compute_offsets(unsigned int base_offset = 0); + + /** + * @brief Check if offsets have been computed + * @return True if compute_offsets has been called + */ + [[nodiscard]] bool offsets_computed() const noexcept { return offsets_computed_; } + + // ========== Bulk Operations ========== + + /** + * @brief Pack all variables into statev array + * @param statev The state variable vector to pack into + * + * Requires compute_offsets() to have been called first. + */ + void pack_all(arma::vec& statev) const; + + /** + * @brief Unpack all variables from statev array + * @param statev The state variable vector to unpack from + * + * Requires compute_offsets() to have been called first. + * Also sets start values to current values. + */ + void unpack_all(const arma::vec& statev); + + /** + * @brief Apply rotation to all variables that require it + * @param DR Rotation increment matrix (3x3) + */ + void rotate_all(const arma::mat& DR); + + /** + * @brief Copy current values to start values for all variables + */ + void to_start_all(); + + /** + * @brief Copy start values to current values for all variables + */ + void set_start_all(); + + /** + * @brief Reset all variables to zero + */ + void reset_all(); + + // ========== Iteration ========== + + /** + * @brief Iterator type for range-based for loops + */ + using iterator = std::vector>::iterator; + using const_iterator = std::vector>::const_iterator; + + iterator begin() { return variables_.begin(); } + iterator end() { return variables_.end(); } + const_iterator begin() const { return variables_.begin(); } + const_iterator end() const { return variables_.end(); } + const_iterator cbegin() const { return variables_.cbegin(); } + const_iterator cend() const { return variables_.cend(); } + + // ========== Utility ========== + + /** + * @brief Get a list of all variable names + * @return Vector of variable names in registration order + */ + std::vector names() const; + + /** + * @brief Clear all variables + */ + void clear(); +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/modular_umat.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/modular_umat.hpp new file mode 100644 index 000000000..59e2496d9 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/modular_umat.hpp @@ -0,0 +1,388 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file modular_umat.hpp + * @brief Modular UMAT orchestrator for composing constitutive models. + * + * This class orchestrates the composition of: + * - Elasticity module (isotropic, cubic, transverse isotropic, orthotropic) + * - Multiple strain mechanisms (plasticity, viscoelasticity, damage) + * + * It provides a unified return mapping algorithm that handles all + * coupled constraints from the different mechanisms. + * + * Usage example (isotropic elasticity + von Mises plasticity with Voce hardening): + * @code + * ModularUMAT mumat; + * int offset = 0; + * arma::vec props = {210000, 0.3, 1.2e-5, 300, 100, 10}; + * mumat.set_elasticity(ElasticityType::ISOTROPIC, props, offset); + * mumat.add_plasticity(YieldType::VON_MISES, IsoHardType::VOCE, + * KinHardType::NONE, 1, 1, props, offset); + * mumat.initialize(nstatev, statev); + * mumat.run(...); + * @endcode + * + * @see ElasticityModule, StrainMechanism, PlasticityMechanism, + * ViscoelasticMechanism, DamageMechanism + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace simcoon { + +// Forward declarations +class PlasticityMechanism; +class ViscoelasticMechanism; +class DamageMechanism; +enum class YieldType; +enum class IsoHardType; +enum class KinHardType; +enum class DamageType; + +/** + * @brief Modular UMAT orchestrator + * + * This class provides a composable constitutive model framework where + * different elasticity types and strain mechanisms can be combined. + */ +class ModularUMAT { +private: + // Modules + ElasticityModule elasticity_; + std::vector> mechanisms_; + InternalVariableCollection ivc_; + + // State + double T_init_; ///< Initial temperature + arma::vec sigma_start_; ///< Stress at start of increment + bool initialized_; ///< Whether initialize() has been called + + // Solver parameters + int maxiter_; ///< Maximum iterations for return mapping + double precision_; ///< Convergence tolerance + +public: + /** + * @brief Default constructor + */ + ModularUMAT(); + + // Disable copy, allow move + ModularUMAT(const ModularUMAT&) = delete; + ModularUMAT& operator=(const ModularUMAT&) = delete; + ModularUMAT(ModularUMAT&&) = default; + ModularUMAT& operator=(ModularUMAT&&) = default; + ~ModularUMAT() = default; + + // ========== Configuration ========== + + /** + * @brief Set elasticity module + * @param type Elasticity type + * @param props Material properties + * @param offset Current offset in props (will be updated) + */ + void set_elasticity(ElasticityType type, const arma::vec& props, int& offset); + + /** + * @brief Add a plasticity mechanism + * @param yield_type Type of yield criterion + * @param iso_type Type of isotropic hardening + * @param kin_type Type of kinematic hardening + * @param N_iso Number of isotropic hardening terms + * @param N_kin Number of kinematic hardening terms + * @param props Material properties + * @param offset Current offset in props (will be updated) + * @return Reference to the added mechanism + */ + PlasticityMechanism& add_plasticity( + YieldType yield_type, + IsoHardType iso_type, + KinHardType kin_type, + int N_iso, + int N_kin, + const arma::vec& props, + int& offset + ); + + /** + * @brief Add a viscoelastic mechanism (Prony series) + * @param N_prony Number of Prony terms + * @param props Material properties (g_i, tau_i pairs for each term) + * @param offset Current offset in props (will be updated) + * @return Reference to the added mechanism + */ + ViscoelasticMechanism& add_viscoelasticity( + int N_prony, + const arma::vec& props, + int& offset + ); + + /** + * @brief Add a damage mechanism + * @param damage_type Type of damage evolution law + * @param props Material properties + * @param offset Current offset in props (will be updated) + * @return Reference to the added mechanism + */ + DamageMechanism& add_damage( + DamageType damage_type, + const arma::vec& props, + int& offset + ); + + /** + * @brief Configure from props array + * + * Props format: + * - props[0]: elasticity_type (0=iso, 1=cubic, 2=trans_iso, 3=ortho) + * - props[1..N_el]: elasticity parameters + * - props[N_el+1]: num_mechanisms + * - For each mechanism: + * - props[i]: mechanism_type (0=plasticity, 1=viscoelasticity, 2=damage) + * - For plasticity (type 0): + * - yield_type, iso_type, kin_type, N_iso, N_kin, sigma_Y, [yield params], [iso params], [kin params] + * - For viscoelasticity (type 1): + * - N_prony, then N_prony pairs of (g_i, tau_i) + * - For damage (type 2): + * - damage_type (0=linear, 1=exp, 2=power, 3=weibull), Y_0, Y_c, [type-specific params] + * + * @param props Material properties vector + * @param offset Starting offset (default: 0) + */ + void configure_from_props(const arma::vec& props, int offset = 0); + + /** + * @brief Initialize internal variables + * @param nstatev Number of state variables + * @param statev State variable vector + */ + void initialize(int nstatev, arma::vec& statev); + + // ========== Accessors ========== + + /** + * @brief Get the elasticity module + * @return Const reference to elasticity module + */ + [[nodiscard]] const ElasticityModule& elasticity() const noexcept { return elasticity_; } + + /** + * @brief Get number of mechanisms + * @return Number of strain mechanisms + */ + [[nodiscard]] size_t num_mechanisms() const noexcept { return mechanisms_.size(); } + + /** + * @brief Get mechanism by index + * @param i Index + * @return Reference to mechanism + */ + StrainMechanism& mechanism(size_t i) { return *mechanisms_[i]; } + const StrainMechanism& mechanism(size_t i) const { return *mechanisms_[i]; } + + /** + * @brief Get internal variable collection + * @return Reference to internal variable collection + */ + InternalVariableCollection& internal_variables() { return ivc_; } + const InternalVariableCollection& internal_variables() const { return ivc_; } + + /** + * @brief Get total number of state variables required + * @return nstatev + */ + int required_nstatev() const; + + /** + * @brief Check if initialized + * @return True if initialize() has been called + */ + [[nodiscard]] bool is_initialized() const noexcept { return initialized_; } + + // ========== Solver Parameters ========== + + /** + * @brief Set maximum iterations + * @param maxiter Maximum number of return mapping iterations + */ + void set_max_iterations(int maxiter) { maxiter_ = maxiter; } + + /** + * @brief Set convergence precision + * @param precision Relative tolerance for convergence + */ + void set_precision(double precision) { precision_ = precision; } + + // ========== Main UMAT Entry Point ========== + + /** + * @brief Run the constitutive model update + * + * This is the main entry point that performs: + * 1. Unpack state variables + * 2. Apply rotation for objectivity + * 3. Elastic prediction + * 4. Return mapping (if inelastic) + * 5. Compute consistent tangent + * 6. Compute work quantities + * 7. Pack state variables + * + * @param umat_name UMAT identifier + * @param Etot Total strain at end of increment + * @param DEtot Strain increment + * @param sigma Output: stress at end of increment + * @param Lt Output: consistent tangent modulus + * @param L Output: elastic stiffness + * @param DR Rotation increment matrix + * @param nprops Number of properties + * @param props Material properties + * @param nstatev Number of state variables + * @param statev State variables + * @param T Temperature at end of increment + * @param DT Temperature increment + * @param Time Current time + * @param DTime Time increment + * @param Wm Total mechanical work + * @param Wm_r Recoverable work + * @param Wm_ir Irrecoverable work + * @param Wm_d Dissipated work + * @param ndi Number of direct stress components + * @param nshr Number of shear stress components + * @param start True if first increment + * @param tnew_dt Suggested new time step ratio + */ + void run( + const std::string& umat_name, + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + arma::mat& Lt, + arma::mat& L, + const arma::mat& DR, + int nprops, + const arma::vec& props, + int nstatev, + arma::vec& statev, + double T, + double DT, + double Time, + double DTime, + double& Wm, + double& Wm_r, + double& Wm_ir, + double& Wm_d, + int ndi, + int nshr, + bool start, + double& tnew_dt + ); + +private: + /** + * @brief Perform return mapping algorithm + * + * Uses Newton iteration with Fischer-Burmeister complementarity + * to solve the coupled constraint equations from all mechanisms. + * The algorithm: + * 1. Computes elastic prediction (trial stress) + * 2. Evaluates constraint functions (Phi) from all mechanisms + * 3. Solves for multiplier increments via Fischer-Burmeister + * 4. Updates internal variables and recomputes stress + * 5. Repeats until convergence (error < precision_) + * + * @param Etot Total strain at start of increment + * @param DEtot Strain increment + * @param sigma Output: stress at end of increment + * @param T_init Reference temperature + * @param T Current temperature + * @param DT Temperature increment + * @param DTime Time increment + * @param ndi Number of direct stress components + * @param Ds_total Output: converged multiplier increments + * + * @see Fischer_Burmeister_m() in num_solve.hpp + */ + void return_mapping( + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + double T_init, + double T, + double DT, + double DTime, + int ndi, + arma::vec& Ds_total + ); + + /** + * @brief Compute consistent tangent modulus + * @param sigma Current stress + * @param Ds_total Total multiplier increments + * @param Lt Output: tangent modulus + */ + void compute_tangent( + const arma::vec& sigma, + const arma::vec& Ds_total, + arma::mat& Lt + ); +}; + +/** + * @brief UMAT function for modular constitutive model + * + * Standard UMAT interface for the modular constitutive model. + * Can be registered in umat_smart.cpp for dispatch. + */ +void umat_modular( + const std::string& umat_name, + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + arma::mat& Lt, + arma::mat& L, + const arma::mat& DR, + const int& nprops, + const arma::vec& props, + const int& nstatev, + arma::vec& statev, + const double& T, + const double& DT, + const double& Time, + const double& DTime, + double& Wm, + double& Wm_r, + double& Wm_ir, + double& Wm_d, + const int& ndi, + const int& nshr, + const bool& start, + double& tnew_dt +); + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/plasticity_mechanism.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/plasticity_mechanism.hpp new file mode 100644 index 000000000..09cb9ff12 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/plasticity_mechanism.hpp @@ -0,0 +1,175 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file plasticity_mechanism.hpp + * @brief Plasticity strain mechanism for modular UMAT. + * + * Combines yield criterion, isotropic hardening, and kinematic hardening + * into a complete plasticity model. + * + * @see StrainMechanism, YieldCriterion, IsotropicHardening, KinematicHardening, ModularUMAT + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace simcoon { + +/** + * @brief Plasticity strain mechanism + * + * This class implements rate-independent plasticity by combining: + * - A yield criterion (von Mises, Hill, etc.) + * - Isotropic hardening (power-law, Voce, etc.) + * - Kinematic hardening (Prager, Chaboche, etc.) + * + * Internal variables: + * - "p": accumulated plastic strain (scalar) + * - "EP": plastic strain tensor (6 Voigt) + * - "X_i": backstress tensors from kinematic hardening + */ +class PlasticityMechanism final : public StrainMechanism { +private: + YieldType yield_type_; ///< Yield criterion type (for deferred configuration) + std::unique_ptr yield_; + std::unique_ptr iso_hard_; + std::unique_ptr kin_hard_; + double sigma_Y_; ///< Initial yield stress + + // Cached quantities from last constraint computation + mutable arma::vec flow_dir_; ///< Flow direction + mutable arma::vec kappa_; ///< L * flow_direction + mutable double dPhi_dp_; ///< dPhi/dp + mutable double H_total_; ///< Total hardening modulus + +public: + /** + * @brief Constructor + * @param yield_type Type of yield criterion + * @param iso_type Type of isotropic hardening + * @param kin_type Type of kinematic hardening + * @param N_iso Number of isotropic hardening terms (for COMBINED_VOCE) + * @param N_kin Number of kinematic hardening terms (for CHABOCHE) + */ + PlasticityMechanism( + YieldType yield_type = YieldType::VON_MISES, + IsoHardType iso_type = IsoHardType::POWER_LAW, + KinHardType kin_type = KinHardType::NONE, + int N_iso = 1, + int N_kin = 1 + ); + + // Default move, no copy (due to unique_ptr) + PlasticityMechanism(const PlasticityMechanism&) = delete; + PlasticityMechanism& operator=(const PlasticityMechanism&) = delete; + PlasticityMechanism(PlasticityMechanism&&) = default; + PlasticityMechanism& operator=(PlasticityMechanism&&) = default; + ~PlasticityMechanism() override = default; + + // ========== Configuration ========== + + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + + // ========== Mechanism Properties ========== + + [[nodiscard]] MechanismType type() const override { return MechanismType::PLASTICITY; } + [[nodiscard]] int num_constraints() const override { return 1; } // Single yield surface + + /** + * @brief Get the yield criterion + * @return Reference to yield criterion + */ + [[nodiscard]] const YieldCriterion& yield_criterion() const noexcept { return *yield_; } + + /** + * @brief Get the isotropic hardening + * @return Reference to isotropic hardening + */ + [[nodiscard]] const IsotropicHardening& isotropic_hardening() const noexcept { return *iso_hard_; } + + /** + * @brief Get the kinematic hardening + * @return Reference to kinematic hardening + */ + [[nodiscard]] const KinematicHardening& kinematic_hardening() const noexcept { return *kin_hard_; } + + /** + * @brief Get initial yield stress + * @return sigma_Y + */ + [[nodiscard]] double initial_yield_stress() const noexcept { return sigma_Y_; } + + // ========== Constitutive Computations ========== + + void compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit + ) const override; + + void compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map + ) const override; + + void compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset + ) const override; + + arma::vec inelastic_strain(const InternalVariableCollection& ivc) const override; + + void update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc + ) override; + + void tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt + ) const override; + + void compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d + ) const override; +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/strain_mechanism.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/strain_mechanism.hpp new file mode 100644 index 000000000..2838d60c4 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/strain_mechanism.hpp @@ -0,0 +1,228 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file strain_mechanism.hpp + * @brief Base class for strain mechanisms in modular UMAT. + * + * Strain mechanisms are pluggable components that contribute to the + * total strain decomposition: Etot = Eel + E_plastic + E_viscous + ... + * + * Each mechanism: + * - Registers its internal variables + * - Contributes constraint functions (yield/evolution equations) + * - Contributes to the Jacobian matrix + * - Updates its internal variables during return mapping + * + * @see PlasticityMechanism, ViscoelasticMechanism, DamageMechanism, ModularUMAT + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace simcoon { + +/** + * @brief Types of strain mechanisms + */ +enum class MechanismType { + PLASTICITY = 0, ///< Rate-independent plasticity + VISCOELASTICITY = 1, ///< Viscoelasticity (Prony series) + DAMAGE = 2 ///< Damage mechanics +}; + +/** + * @brief Base class for strain mechanisms + * + * This abstract class defines the interface for strain mechanisms that + * can be composed in a modular UMAT. Each mechanism contributes to the + * total strain decomposition and provides constraint functions for the + * return mapping algorithm. + */ +class StrainMechanism { +protected: + std::string name_; ///< Mechanism name for debugging + bool active_; ///< Whether this mechanism is currently active + +public: + /** + * @brief Constructor + * @param name Mechanism identifier + */ + explicit StrainMechanism(const std::string& name) : name_(name), active_(true) {} + + virtual ~StrainMechanism() = default; + + // ========== Configuration ========== + + /** + * @brief Configure from props array + * @param props Material properties vector + * @param offset Current offset in props (will be updated) + */ + virtual void configure(const arma::vec& props, int& offset) = 0; + + /** + * @brief Register internal variables for this mechanism + * @param ivc Internal variable collection + */ + virtual void register_variables(InternalVariableCollection& ivc) = 0; + + // ========== Mechanism Properties ========== + + /** + * @brief Get mechanism name + * @return Mechanism identifier + */ + [[nodiscard]] const std::string& name() const noexcept { return name_; } + + /** + * @brief Get mechanism type + * @return Mechanism type enum + */ + [[nodiscard]] virtual MechanismType type() const = 0; + + /** + * @brief Check if mechanism is active + * @return True if mechanism contributes to constitutive response + */ + [[nodiscard]] bool is_active() const noexcept { return active_; } + + /** + * @brief Set mechanism active state + * @param active Whether mechanism should be active + */ + void set_active(bool active) noexcept { active_ = active; } + + /** + * @brief Get number of constraint equations + * @return Number of active constraints + */ + [[nodiscard]] virtual int num_constraints() const = 0; + + // ========== Constitutive Computations ========== + + /** + * @brief Compute constraint functions (yield/evolution equations) + * @param sigma Current stress tensor (6 Voigt) + * @param L Elastic stiffness tensor (6x6) + * @param DTime Time increment + * @param ivc Internal variable collection + * @param Phi Output: constraint function values + * @param Y_crit Output: critical values for convergence + */ + virtual void compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit + ) const = 0; + + /** + * @brief Compute flow directions for internal variables + * @param sigma Current stress tensor (6 Voigt) + * @param ivc Internal variable collection + * @param Lambda_map Output: map of variable name to flow direction + */ + virtual void compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map + ) const = 0; + + /** + * @brief Compute contribution to Jacobian matrix B + * @param sigma Current stress tensor (6 Voigt) + * @param L Elastic stiffness tensor (6x6) + * @param ivc Internal variable collection + * @param B Jacobian matrix to update + * @param row_offset Starting row for this mechanism's contributions + */ + virtual void compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset + ) const = 0; + + /** + * @brief Get inelastic strain from this mechanism + * @param ivc Internal variable collection + * @return Inelastic strain tensor (6 Voigt) + */ + virtual arma::vec inelastic_strain(const InternalVariableCollection& ivc) const = 0; + + /** + * @brief Update internal variables given multiplier increments + * @param ds Multiplier increment vector + * @param offset Starting index in ds for this mechanism + * @param ivc Internal variable collection to update + */ + virtual void update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc + ) = 0; + + /** + * @brief Compute contribution to consistent tangent + * @param sigma Current stress tensor + * @param L Elastic stiffness tensor + * @param Ds Total multiplier increments + * @param offset Starting index in Ds for this mechanism + * @param ivc Internal variable collection + * @param Lt Tangent modulus to update + */ + virtual void tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt + ) const = 0; + + // ========== Work Quantities ========== + + /** + * @brief Compute work decomposition + * @param sigma_start Stress at start of increment + * @param sigma Current stress + * @param ivc Internal variable collection + * @param Wm_r Output: recoverable work increment + * @param Wm_ir Output: irrecoverable (stored) work increment + * @param Wm_d Output: dissipated work increment + */ + virtual void compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d + ) const = 0; +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.hpp new file mode 100644 index 000000000..bd2318473 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.hpp @@ -0,0 +1,151 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file viscoelastic_mechanism.hpp + * @brief Viscoelastic strain mechanism using Prony series (generalized Maxwell model) + * + * This mechanism implements a generalized Maxwell model with N Prony terms. + * Each branch i has: + * - E_i: Branch modulus (or g_i relative to E_0) + * - tau_i: Relaxation time + * - EV_i: Viscous strain tensor (internal variable) + * + * The viscoelastic strain evolves according to: + * dEV_i/dt = (1/tau_i) * (C_i^{-1} : sigma - EV_i) + * + * Or equivalently using the hereditary integral formulation. + * + * @see StrainMechanism, ModularUMAT + * @version 1.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace simcoon { + +/** + * @brief Viscoelastic mechanism using Prony series + * + * Implements a generalized Maxwell model with N branches. + * Each branch contributes viscous strain that relaxes over time. + */ +class ViscoelasticMechanism final : public StrainMechanism { +private: + int N_prony_; ///< Number of Prony terms + std::vector g_i_; ///< Relative moduli (g_i = E_i/E_0) + std::vector tau_i_; ///< Relaxation times + arma::mat L_0_; ///< Reference stiffness (for computing branch stiffnesses) + arma::mat M_0_; ///< Reference compliance (inverse of L_0_, stored to avoid recomputation) + bool use_deviatoric_only_; ///< If true, only apply viscoelasticity to deviatoric part + + // Cached values for tangent computation + mutable std::vector EV_n_; ///< Viscous strains at start of increment + mutable std::vector flow_i_; ///< Flow directions for each branch + mutable std::vector factor_i_; ///< Update factors exp(-DTime/tau_i) + +public: + /** + * @brief Constructor + * @param N_prony Number of Prony terms + */ + explicit ViscoelasticMechanism(int N_prony); + + // StrainMechanism interface + void configure(const arma::vec& props, int& offset) override; + void register_variables(InternalVariableCollection& ivc) override; + + [[nodiscard]] int num_constraints() const override { return N_prony_; } + [[nodiscard]] MechanismType type() const override { return MechanismType::VISCOELASTICITY; } + + void compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit + ) const override; + + void compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map + ) const override; + + void compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset + ) const override; + + arma::vec inelastic_strain(const InternalVariableCollection& ivc) const override; + + void update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc + ) override; + + void tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt + ) const override; + + void compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d + ) const override; + + // Accessors + [[nodiscard]] int num_prony_terms() const noexcept { return N_prony_; } + [[nodiscard]] double g(int i) const { return g_i_[i]; } + [[nodiscard]] double tau(int i) const { return tau_i_[i]; } + + /** + * @brief Set reference stiffness for computing branch contributions + * @param L_0 Reference elastic stiffness + */ + void set_reference_stiffness(const arma::mat& L_0) { L_0_ = L_0; M_0_ = arma::inv(L_0); } + + /** + * @brief Get the effective instantaneous stiffness reduction + * + * For a generalized Maxwell model, the instantaneous modulus is E_0, + * and the long-term modulus is E_inf = E_0 * (1 - sum(g_i)). + * + * @return Long-term stiffness factor (1 - sum(g_i)) + */ + double long_term_factor() const; +}; + +} // namespace simcoon diff --git a/include/simcoon/Continuum_mechanics/Umat/Modular/yield_criterion.hpp b/include/simcoon/Continuum_mechanics/Umat/Modular/yield_criterion.hpp new file mode 100644 index 000000000..00c26be04 --- /dev/null +++ b/include/simcoon/Continuum_mechanics/Umat/Modular/yield_criterion.hpp @@ -0,0 +1,234 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file yield_criterion.hpp + * @brief Yield criterion module for modular UMAT. + * + * This module wraps existing yield criterion functions from criteria.hpp + * (Eq_stress, dEq_stress) to provide configurable yield surfaces. + * + * @version 1.0 + */ + +#pragma once + +#include +#include + +namespace simcoon { + +/** + * @brief Types of yield criteria + */ +enum class YieldType { + VON_MISES = 0, ///< Isotropic von Mises (J2) + TRESCA = 1, ///< Tresca (max shear stress) + DRUCKER = 2, ///< Drucker (pressure-dependent) + HILL = 3, ///< Hill 1948 anisotropic + DFA = 4, ///< Deshpande-Fleck-Ashby + ANISOTROPIC = 5 ///< Generic anisotropic (9 params) +}; + +/** + * @brief Yield criterion module for modular UMAT + * + * This class encapsulates yield surface computations, wrapping the + * existing Eq_stress and dEq_stress functions from criteria.hpp. + */ +class YieldCriterion { +private: + YieldType type_; + arma::vec params_; ///< Criterion-specific parameters + bool configured_; + + /** + * @brief Convert YieldType to string for Eq_stress + * @return String identifier for the criterion + */ + std::string type_string() const; + +public: + /** + * @brief Default constructor (von Mises) + */ + YieldCriterion(); + + // Default copy/move/destructor + YieldCriterion(const YieldCriterion&) = default; + YieldCriterion(YieldCriterion&&) = default; + YieldCriterion& operator=(const YieldCriterion&) = default; + YieldCriterion& operator=(YieldCriterion&&) = default; + ~YieldCriterion() = default; + + // ========== Configuration ========== + + /** + * @brief Configure as von Mises criterion + */ + void configure_von_mises(); + + /** + * @brief Configure as Tresca criterion + */ + void configure_tresca(); + + /** + * @brief Configure as Drucker criterion + * @param b J3 influence parameter + * @param n Exponent parameter + */ + void configure_drucker(double b, double n); + + /** + * @brief Configure as Hill 1948 criterion + * @param F Hill parameter F + * @param G Hill parameter G + * @param H Hill parameter H + * @param L Hill parameter L + * @param M Hill parameter M + * @param N Hill parameter N + */ + void configure_hill(double F, double G, double H, double L, double M, double N); + + /** + * @brief Configure as DFA criterion + * @param F DFA parameter F + * @param G DFA parameter G + * @param H DFA parameter H + * @param L DFA parameter L + * @param M DFA parameter M + * @param N DFA parameter N + * @param K Hydrostatic sensitivity parameter + */ + void configure_dfa(double F, double G, double H, double L, double M, double N, double K); + + /** + * @brief Configure as generic anisotropic criterion + * @param P11 Anisotropic tensor component (1,1) + * @param P22 Anisotropic tensor component (2,2) + * @param P33 Anisotropic tensor component (3,3) + * @param P12 Anisotropic tensor component (1,2) + * @param P13 Anisotropic tensor component (1,3) + * @param P23 Anisotropic tensor component (2,3) + * @param P44 Anisotropic tensor component (4,4) + * @param P55 Anisotropic tensor component (5,5) + * @param P66 Anisotropic tensor component (6,6) + */ + void configure_anisotropic(double P11, double P22, double P33, + double P12, double P13, double P23, + double P44, double P55, double P66); + + /** + * @brief Configure from props array + * @param type Yield criterion type + * @param props Material properties vector + * @param offset Current offset in props (will be updated) + */ + void configure(YieldType type, const arma::vec& props, int& offset); + + // ========== Accessors ========== + + /** + * @brief Get the yield criterion type + * @return The configured type + */ + [[nodiscard]] YieldType type() const noexcept { return type_; } + + /** + * @brief Check if the criterion is configured + * @return True if configure has been called + */ + [[nodiscard]] bool is_configured() const noexcept { return configured_; } + + /** + * @brief Get the criterion parameters + * @return Const reference to parameters + */ + [[nodiscard]] const arma::vec& params() const noexcept { return params_; } + + // ========== Yield Function Computations ========== + + /** + * @brief Compute equivalent stress + * @param sigma Stress tensor (6 Voigt components) + * @return Equivalent stress value + */ + double equivalent_stress(const arma::vec& sigma) const; + + /** + * @brief Compute equivalent stress with backstress shift + * @param sigma Stress tensor (6 Voigt components) + * @param X Backstress tensor (6 Voigt components) + * @return Equivalent stress of (sigma - X) + */ + double equivalent_stress(const arma::vec& sigma, const arma::vec& X) const; + + /** + * @brief Compute flow direction (derivative of equivalent stress) + * @param sigma Stress tensor (6 Voigt components) + * @return Flow direction (6 Voigt components) + */ + arma::vec flow_direction(const arma::vec& sigma) const; + + /** + * @brief Compute flow direction with backstress shift + * @param sigma Stress tensor (6 Voigt components) + * @param X Backstress tensor (6 Voigt components) + * @return Flow direction at (sigma - X) + */ + arma::vec flow_direction(const arma::vec& sigma, const arma::vec& X) const; + + /** + * @brief Compute plastic flow direction (for non-associated flow) + * @param sigma Stress tensor (6 Voigt components) + * @return Plastic flow direction + * + * For associated flow rules, this is the same as flow_direction. + * Override in subclasses for non-associated flow. + */ + arma::vec plastic_flow(const arma::vec& sigma) const; + + /** + * @brief Compute plastic flow direction with backstress shift + * @param sigma Stress tensor (6 Voigt components) + * @param X Backstress tensor (6 Voigt components) + * @return Plastic flow direction at (sigma - X) + */ + arma::vec plastic_flow(const arma::vec& sigma, const arma::vec& X) const; + + // ========== Utility ========== + + /** + * @brief Get number of props consumed by this yield criterion type + * @param type The yield criterion type + * @return Number of properties required + */ + [[nodiscard]] static constexpr int props_count(YieldType type) { + switch (type) { + case YieldType::VON_MISES: return 0; + case YieldType::TRESCA: return 0; + case YieldType::DRUCKER: return 2; + case YieldType::HILL: return 6; + case YieldType::DFA: return 7; + case YieldType::ANISOTROPIC: return 9; + } + return 0; + } +}; + +} // namespace simcoon diff --git a/python-setup/simcoon/__init__.py b/python-setup/simcoon/__init__.py index 266fd2118..c9c29d596 100755 --- a/python-setup/simcoon/__init__.py +++ b/python-setup/simcoon/__init__.py @@ -1,4 +1,5 @@ from simcoon._core import * +from simcoon import modular from simcoon.__version__ import __version__ # Backward compatibility alias - simmit was the legacy module name diff --git a/python-setup/simcoon/modular.py b/python-setup/simcoon/modular.py new file mode 100644 index 000000000..599deb2c7 --- /dev/null +++ b/python-setup/simcoon/modular.py @@ -0,0 +1,1109 @@ +"""Modular UMAT configuration for composable constitutive models. + +This module provides a Pythonic interface to the modular UMAT system. +It allows users to compose constitutive models from building blocks: +- Elasticity (isotropic, cubic, transversely isotropic, orthotropic) +- Plasticity (yield criteria + isotropic/kinematic hardening) +- Viscoelasticity (Prony series) +- Damage (linear, exponential, power-law, Weibull) + +Example +------- +>>> from simcoon.modular import ( +... ModularMaterial, IsotropicElasticity, +... Plasticity, VonMisesYield, VoceHardening +... ) +>>> mat = ModularMaterial( +... elasticity=IsotropicElasticity(E=210000., nu=0.3, alpha=1.2e-5), +... mechanisms=[ +... Plasticity( +... sigma_Y=300., +... yield_criterion=VonMisesYield(), +... isotropic_hardening=VoceHardening(Q=100., b=10.), +... ) +... ] +... ) +>>> props = mat.props # numpy array for sim.umat("MODUL", ...) +>>> nstatev = mat.nstatev # number of state variables required +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import IntEnum +from typing import List, Sequence, Tuple, Union + +import numpy as np +from numpy.typing import NDArray + + +__all__ = [ + # Enums + "ElasticityType", "YieldType", "IsoHardType", "KinHardType", + "DamageType", "MechanismType", + # Elasticity + "IsotropicElasticity", "CubicElasticity", + "TransverseIsotropicElasticity", "OrthotropicElasticity", + # Yield criteria + "VonMisesYield", "TrescaYield", "DruckerYield", + "HillYield", "DFAYield", "AnisotropicYield", + # Isotropic hardening + "NoIsotropicHardening", "LinearIsotropicHardening", + "PowerLawHardening", "VoceHardening", "CombinedVoceHardening", + # Kinematic hardening + "NoKinematicHardening", "PragerHardening", + "ArmstrongFrederickHardening", "ChabocheHardening", + # Mechanisms + "Plasticity", "Viscoelasticity", "Damage", + # Type aliases + "Elasticity", "YieldCriterion", "IsotropicHardening", + "KinematicHardening", "Mechanism", + # Orchestrator + "ModularMaterial", + # Factory functions + "elastic_model", "elastoplastic_model", "viscoelastic_model", +] + + +# ============================================================================ +# Enums (mirrors C++ enums in the Modular UMAT headers) +# ============================================================================ + +class ElasticityType(IntEnum): + """Type of linear elasticity.""" + ISOTROPIC = 0 + CUBIC = 1 + TRANSVERSE_ISOTROPIC = 2 + ORTHOTROPIC = 3 + + +class YieldType(IntEnum): + """Type of yield criterion.""" + VON_MISES = 0 + TRESCA = 1 + DRUCKER = 2 + HILL = 3 + DFA = 4 + ANISOTROPIC = 5 + + +class IsoHardType(IntEnum): + """Type of isotropic hardening.""" + NONE = 0 + LINEAR = 1 + POWER_LAW = 2 + VOCE = 3 + COMBINED_VOCE = 4 + + +class KinHardType(IntEnum): + """Type of kinematic hardening.""" + NONE = 0 + PRAGER = 1 + ARMSTRONG_FREDERICK = 2 + CHABOCHE = 3 + + +class DamageType(IntEnum): + """Type of damage evolution law.""" + LINEAR = 0 + EXPONENTIAL = 1 + POWER_LAW = 2 + WEIBULL = 3 + + +class MechanismType(IntEnum): + """Type of strain mechanism.""" + PLASTICITY = 0 + VISCOELASTICITY = 1 + DAMAGE = 2 + + +# ============================================================================ +# Elasticity configurations +# ============================================================================ + +@dataclass(frozen=True) +class IsotropicElasticity: + """Isotropic elasticity. + + Parameters + ---------- + E : float + Young's modulus. + nu : float + Poisson's ratio. + alpha : float + Coefficient of thermal expansion. + """ + E: float + nu: float + alpha: float = 0.0 + + @property + def elasticity_type(self) -> ElasticityType: + return ElasticityType.ISOTROPIC + + def to_props(self) -> List[float]: + """Return the props values for this elasticity.""" + return [self.E, self.nu, self.alpha] + + @property + def nprops(self) -> int: + return 3 + + +@dataclass(frozen=True) +class CubicElasticity: + """Cubic elasticity (3 independent constants). + + Parameters + ---------- + E : float + Young's modulus. + nu : float + Poisson's ratio. + G : float + Shear modulus (independent from E and nu for cubic symmetry). + alpha : float + Coefficient of thermal expansion. + """ + E: float + nu: float + G: float + alpha: float = 0.0 + + @property + def elasticity_type(self) -> ElasticityType: + return ElasticityType.CUBIC + + def to_props(self) -> List[float]: + return [self.E, self.nu, self.G, self.alpha] + + @property + def nprops(self) -> int: + return 4 + + +@dataclass(frozen=True) +class TransverseIsotropicElasticity: + """Transversely isotropic elasticity. + + Parameters + ---------- + EL : float + Longitudinal Young's modulus. + ET : float + Transverse Young's modulus. + nuTL : float + Poisson's ratio (transverse-longitudinal). + nuTT : float + Poisson's ratio (transverse-transverse). + GLT : float + Shear modulus. + alpha_L : float + Longitudinal CTE. + alpha_T : float + Transverse CTE. + axis : int + Axis of symmetry (1=x, 2=y, 3=z). + """ + EL: float + ET: float + nuTL: float + nuTT: float + GLT: float + alpha_L: float = 0.0 + alpha_T: float = 0.0 + axis: int = 3 + + @property + def elasticity_type(self) -> ElasticityType: + return ElasticityType.TRANSVERSE_ISOTROPIC + + def to_props(self) -> List[float]: + return [self.EL, self.ET, self.nuTL, self.nuTT, + self.GLT, self.alpha_L, self.alpha_T, float(self.axis)] + + @property + def nprops(self) -> int: + return 8 + + +@dataclass(frozen=True) +class OrthotropicElasticity: + """Orthotropic elasticity (9 independent elastic constants). + + Parameters + ---------- + E1, E2, E3 : float + Young's moduli in directions 1, 2, 3. + nu12, nu13, nu23 : float + Poisson's ratios. + G12, G13, G23 : float + Shear moduli. + alpha1, alpha2, alpha3 : float + Coefficients of thermal expansion. + """ + E1: float + E2: float + E3: float + nu12: float + nu13: float + nu23: float + G12: float + G13: float + G23: float + alpha1: float = 0.0 + alpha2: float = 0.0 + alpha3: float = 0.0 + + @property + def elasticity_type(self) -> ElasticityType: + return ElasticityType.ORTHOTROPIC + + def to_props(self) -> List[float]: + return [self.E1, self.E2, self.E3, + self.nu12, self.nu13, self.nu23, + self.G12, self.G13, self.G23, + self.alpha1, self.alpha2, self.alpha3] + + @property + def nprops(self) -> int: + return 12 + + +Elasticity = Union[IsotropicElasticity, CubicElasticity, + TransverseIsotropicElasticity, OrthotropicElasticity] + +# ============================================================================ +# Yield criteria +# ============================================================================ + +@dataclass(frozen=True) +class VonMisesYield: + """Von Mises (J2) yield criterion. No additional parameters.""" + + @property + def yield_type(self) -> YieldType: + return YieldType.VON_MISES + + def to_props(self) -> List[float]: + return [] + + @property + def nprops(self) -> int: + return 0 + + +@dataclass(frozen=True) +class TrescaYield: + """Tresca yield criterion. No additional parameters.""" + + @property + def yield_type(self) -> YieldType: + return YieldType.TRESCA + + def to_props(self) -> List[float]: + return [] + + @property + def nprops(self) -> int: + return 0 + + +@dataclass(frozen=True) +class DruckerYield: + """Drucker yield criterion (J2/J3-based). + + Parameters + ---------- + b : float + J3 influence parameter. + n : float + Exponent parameter. + """ + b: float + n: float + + @property + def yield_type(self) -> YieldType: + return YieldType.DRUCKER + + def to_props(self) -> List[float]: + return [self.b, self.n] + + @property + def nprops(self) -> int: + return 2 + + +@dataclass(frozen=True) +class HillYield: + """Hill 1948 anisotropic yield criterion. + + Parameters + ---------- + F, G, H, L, M, N : float + Hill anisotropy parameters. + """ + F: float + G: float + H: float + L: float + M: float + N: float + + @property + def yield_type(self) -> YieldType: + return YieldType.HILL + + def to_props(self) -> List[float]: + return [self.F, self.G, self.H, self.L, self.M, self.N] + + @property + def nprops(self) -> int: + return 6 + + +@dataclass(frozen=True) +class DFAYield: + """Deshpande-Fleck-Ashby yield criterion. + + Parameters + ---------- + F, G, H, L, M, N : float + Anisotropy parameters. + K : float + Hydrostatic sensitivity parameter. + """ + F: float + G: float + H: float + L: float + M: float + N: float + K: float + + @property + def yield_type(self) -> YieldType: + return YieldType.DFA + + def to_props(self) -> List[float]: + return [self.F, self.G, self.H, self.L, self.M, self.N, self.K] + + @property + def nprops(self) -> int: + return 7 + + +@dataclass(frozen=True) +class AnisotropicYield: + """Generic anisotropic yield criterion (9 parameters). + + Parameters + ---------- + P11, P22, P33 : float + Normal components of the anisotropy tensor. + P12, P13, P23 : float + Off-diagonal components. + P44, P55, P66 : float + Shear components. + """ + P11: float + P22: float + P33: float + P12: float + P13: float + P23: float + P44: float + P55: float + P66: float + + @property + def yield_type(self) -> YieldType: + return YieldType.ANISOTROPIC + + def to_props(self) -> List[float]: + return [self.P11, self.P22, self.P33, + self.P12, self.P13, self.P23, + self.P44, self.P55, self.P66] + + @property + def nprops(self) -> int: + return 9 + + +YieldCriterion = Union[VonMisesYield, TrescaYield, DruckerYield, + HillYield, DFAYield, AnisotropicYield] + +# ============================================================================ +# Isotropic hardening +# ============================================================================ + +@dataclass(frozen=True) +class NoIsotropicHardening: + """No isotropic hardening.""" + + @property + def iso_hard_type(self) -> IsoHardType: + return IsoHardType.NONE + + def to_props(self) -> List[float]: + return [] + + @property + def nprops(self) -> int: + return 0 + + @property + def N(self) -> int: + return 1 + + +@dataclass(frozen=True) +class LinearIsotropicHardening: + """Linear isotropic hardening: R = H * p. + + Parameters + ---------- + H : float + Hardening modulus. + """ + H: float + + @property + def iso_hard_type(self) -> IsoHardType: + return IsoHardType.LINEAR + + def to_props(self) -> List[float]: + return [self.H] + + @property + def nprops(self) -> int: + return 1 + + @property + def N(self) -> int: + return 1 + + +@dataclass(frozen=True) +class PowerLawHardening: + """Power-law isotropic hardening: R = k * p^m. + + Parameters + ---------- + k : float + Hardening coefficient. + m : float + Hardening exponent. + """ + k: float + m: float + + @property + def iso_hard_type(self) -> IsoHardType: + return IsoHardType.POWER_LAW + + def to_props(self) -> List[float]: + return [self.k, self.m] + + @property + def nprops(self) -> int: + return 2 + + @property + def N(self) -> int: + return 1 + + +@dataclass(frozen=True) +class VoceHardening: + """Voce saturation hardening: R = Q * (1 - exp(-b*p)). + + Parameters + ---------- + Q : float + Saturation stress. + b : float + Hardening rate. + """ + Q: float + b: float + + @property + def iso_hard_type(self) -> IsoHardType: + return IsoHardType.VOCE + + def to_props(self) -> List[float]: + return [self.Q, self.b] + + @property + def nprops(self) -> int: + return 2 + + @property + def N(self) -> int: + return 1 + + +@dataclass(frozen=True) +class CombinedVoceHardening: + """Combined Voce hardening: R = sum_i Q_i * (1 - exp(-b_i*p)). + + Parameters + ---------- + terms : tuple of (Q, b) tuples + Each tuple is (saturation stress, hardening rate) for one Voce term. + """ + terms: Tuple[Tuple[float, float], ...] = () + + @property + def iso_hard_type(self) -> IsoHardType: + return IsoHardType.COMBINED_VOCE + + def to_props(self) -> List[float]: + props = [] + for Q, b in self.terms: + props.extend([Q, b]) + return props + + @property + def nprops(self) -> int: + return 2 * len(self.terms) + + @property + def N(self) -> int: + return len(self.terms) + + +IsotropicHardening = Union[NoIsotropicHardening, LinearIsotropicHardening, + PowerLawHardening, VoceHardening, CombinedVoceHardening] + +# ============================================================================ +# Kinematic hardening +# ============================================================================ + +@dataclass(frozen=True) +class NoKinematicHardening: + """No kinematic hardening.""" + + @property + def kin_hard_type(self) -> KinHardType: + return KinHardType.NONE + + def to_props(self) -> List[float]: + return [] + + @property + def nprops(self) -> int: + return 0 + + @property + def N(self) -> int: + return 1 + + @property + def num_backstresses(self) -> int: + return 0 + + +@dataclass(frozen=True) +class PragerHardening: + """Linear Prager kinematic hardening: X = (2/3)*C*alpha. + + Parameters + ---------- + C : float + Kinematic hardening modulus. + """ + C: float + + @property + def kin_hard_type(self) -> KinHardType: + return KinHardType.PRAGER + + def to_props(self) -> List[float]: + return [self.C] + + @property + def nprops(self) -> int: + return 1 + + @property + def N(self) -> int: + return 1 + + @property + def num_backstresses(self) -> int: + return 1 + + +@dataclass(frozen=True) +class ArmstrongFrederickHardening: + """Armstrong-Frederick kinematic hardening: dX = (2/3)*C*dep - D*X*dp. + + Parameters + ---------- + C : float + Hardening parameter. + D : float + Dynamic recovery parameter. + """ + C: float + D: float + + @property + def kin_hard_type(self) -> KinHardType: + return KinHardType.ARMSTRONG_FREDERICK + + def to_props(self) -> List[float]: + return [self.C, self.D] + + @property + def nprops(self) -> int: + return 2 + + @property + def N(self) -> int: + return 1 + + @property + def num_backstresses(self) -> int: + return 1 + + +@dataclass(frozen=True) +class ChabocheHardening: + """Chaboche kinematic hardening with multiple backstress terms. + + Each term i follows: dX_i = (2/3)*C_i*dep - D_i*X_i*dp. + + Parameters + ---------- + terms : tuple of (C, D) tuples + Each tuple is (hardening parameter, dynamic recovery parameter) + for one Armstrong-Frederick backstress term. + """ + terms: Tuple[Tuple[float, float], ...] = () + + @property + def kin_hard_type(self) -> KinHardType: + return KinHardType.CHABOCHE + + def to_props(self) -> List[float]: + props = [] + for C, D in self.terms: + props.extend([C, D]) + return props + + @property + def nprops(self) -> int: + return 2 * len(self.terms) + + @property + def N(self) -> int: + return len(self.terms) + + @property + def num_backstresses(self) -> int: + return len(self.terms) + + +KinematicHardening = Union[NoKinematicHardening, PragerHardening, + ArmstrongFrederickHardening, ChabocheHardening] + +# ============================================================================ +# Strain mechanisms +# ============================================================================ + +@dataclass(frozen=True) +class Plasticity: + """Plasticity mechanism combining yield + isotropic + kinematic hardening. + + Parameters + ---------- + sigma_Y : float + Initial yield stress. + yield_criterion : YieldCriterion + Yield criterion (default: VonMisesYield). + isotropic_hardening : IsotropicHardening + Isotropic hardening law (default: NoIsotropicHardening). + kinematic_hardening : KinematicHardening + Kinematic hardening law (default: NoKinematicHardening). + """ + sigma_Y: float + yield_criterion: YieldCriterion = field(default_factory=VonMisesYield) + isotropic_hardening: IsotropicHardening = field(default_factory=NoIsotropicHardening) + kinematic_hardening: KinematicHardening = field(default_factory=NoKinematicHardening) + + @property + def mechanism_type(self) -> MechanismType: + return MechanismType.PLASTICITY + + def to_props(self) -> List[float]: + """Return props values for this mechanism (excluding mechanism_type header).""" + yc = self.yield_criterion + ih = self.isotropic_hardening + kh = self.kinematic_hardening + # Header: yield_type, iso_type, kin_type, N_iso, N_kin + header = [ + float(yc.yield_type), + float(ih.iso_hard_type), + float(kh.kin_hard_type), + float(ih.N), + float(kh.N), + ] + # sigma_Y + yield params + iso params + kin params + params = [self.sigma_Y] + yc.to_props() + ih.to_props() + kh.to_props() + return header + params + + @property + def nstatev(self) -> int: + """Number of state variables for this mechanism: p(1) + EP(6) + backstresses(6*N).""" + return 7 + 6 * self.kinematic_hardening.num_backstresses + + +@dataclass(frozen=True) +class Viscoelasticity: + """Viscoelastic mechanism using Prony series (generalized Maxwell model). + + Parameters + ---------- + terms : tuple of (g, tau) tuples + Each tuple is (relative modulus, relaxation time) for one Prony term. + """ + terms: Tuple[Tuple[float, float], ...] = () + + @property + def mechanism_type(self) -> MechanismType: + return MechanismType.VISCOELASTICITY + + def to_props(self) -> List[float]: + """Return props values for this mechanism (excluding mechanism_type header).""" + # Header: N_prony + header = [float(len(self.terms))] + params = [] + for g, tau in self.terms: + params.extend([g, tau]) + return header + params + + @property + def nstatev(self) -> int: + """Number of state variables: EV_i(6) per Prony term.""" + return 6 * len(self.terms) + + +@dataclass(frozen=True) +class Damage: + """Scalar damage mechanism. + + Parameters + ---------- + Y_0 : float + Damage threshold (no damage below this energy). + Y_c : float + Critical damage driving force. + damage_type : DamageType + Type of damage evolution law (default: LINEAR). + A : float, optional + Scale parameter (for EXPONENTIAL and WEIBULL). + n : float, optional + Exponent parameter (for POWER_LAW and WEIBULL). + """ + Y_0: float + Y_c: float + damage_type: DamageType = DamageType.LINEAR + A: float = 0.0 + n: float = 1.0 + + @property + def mechanism_type(self) -> MechanismType: + return MechanismType.DAMAGE + + def to_props(self) -> List[float]: + """Return props values for this mechanism (excluding mechanism_type header).""" + # Header: damage_type + header = [float(self.damage_type)] + # Common params: Y_0, Y_c + params = [self.Y_0, self.Y_c] + # Type-specific params + if self.damage_type == DamageType.EXPONENTIAL: + params.append(self.A) + elif self.damage_type == DamageType.POWER_LAW: + params.append(self.n) + elif self.damage_type == DamageType.WEIBULL: + params.extend([self.A, self.n]) + return header + params + + @property + def nstatev(self) -> int: + """Number of state variables: D(1) + Y_max(1).""" + return 2 + + +Mechanism = Union[Plasticity, Viscoelasticity, Damage] + +# ============================================================================ +# Modular material orchestrator +# ============================================================================ + +class ModularMaterial: + """Composable constitutive model using the modular UMAT system. + + This class assembles an elasticity module and one or more strain mechanisms + into a complete constitutive model. It builds the flat props array expected + by the C++ ``umat_modular`` function, which can be called via + ``simcoon.umat("MODUL", ...)``. + + Parameters + ---------- + elasticity : Elasticity + Elasticity configuration. + mechanisms : list of Mechanism, optional + List of strain mechanisms (plasticity, viscoelasticity, damage). + + Examples + -------- + Pure elasticity: + + >>> mat = ModularMaterial(elasticity=IsotropicElasticity(E=210000., nu=0.3)) + >>> mat.props # array([0., 210000., 0.3, 0., 0.]) + + Elastoplastic with Voce hardening: + + >>> mat = ModularMaterial( + ... elasticity=IsotropicElasticity(E=210000., nu=0.3, alpha=1.2e-5), + ... mechanisms=[ + ... Plasticity( + ... sigma_Y=300., + ... yield_criterion=VonMisesYield(), + ... isotropic_hardening=VoceHardening(Q=100., b=10.), + ... ) + ... ] + ... ) + + Coupled plasticity + viscoelasticity: + + >>> mat = ModularMaterial( + ... elasticity=IsotropicElasticity(E=70000., nu=0.33, alpha=2.3e-5), + ... mechanisms=[ + ... Plasticity(sigma_Y=200., isotropic_hardening=PowerLawHardening(k=500., m=0.3)), + ... Viscoelasticity(terms=((0.3, 1.0), (0.2, 10.0))), + ... ] + ... ) + """ + + def __init__( + self, + elasticity: Elasticity, + mechanisms: Sequence[Mechanism] = (), + ): + self._elasticity = elasticity + self._mechanisms = list(mechanisms) + self._props: NDArray[np.float64] | None = None + + @property + def elasticity(self) -> Elasticity: + """The elasticity configuration.""" + return self._elasticity + + @property + def mechanisms(self) -> List[Mechanism]: + """The list of strain mechanisms.""" + return list(self._mechanisms) + + @property + def umat_name(self) -> str: + """UMAT name string for use with ``simcoon.umat()``.""" + return "MODUL" + + @property + def props(self) -> NDArray[np.float64]: + """Build and return the flat props array for the C++ modular UMAT. + + The props format follows the ``configure_from_props()`` convention: + + - ``props[0]``: elasticity type + - ``props[1..N_el]``: elasticity parameters + - ``props[N_el+1]``: number of mechanisms + - For each mechanism: type code + mechanism-specific parameters + """ + if self._props is not None: + return self._props + + values: List[float] = [] + + # Elasticity type + parameters + values.append(float(self._elasticity.elasticity_type)) + values.extend(self._elasticity.to_props()) + + # Number of mechanisms + values.append(float(len(self._mechanisms))) + + # Each mechanism + for mech in self._mechanisms: + values.append(float(mech.mechanism_type)) + values.extend(mech.to_props()) + + self._props = np.array(values, dtype=np.float64) + self._props.flags.writeable = False + return self._props + + @property + def nstatev(self) -> int: + """Compute the total number of state variables required. + + Accounts for: + - T_init (1 scalar) + - Each mechanism's internal variables + """ + count = 1 # T_init + for mech in self._mechanisms: + count += mech.nstatev + return count + + @property + def nprops(self) -> int: + """Total number of material properties.""" + return len(self.props) + + def __repr__(self) -> str: + mechs = ", ".join(m.__class__.__name__ for m in self._mechanisms) + return (f"ModularMaterial(elasticity={self._elasticity.__class__.__name__}, " + f"mechanisms=[{mechs}], nprops={self.nprops}, nstatev={self.nstatev})") + + def summary(self) -> str: + """Return a human-readable summary of the material configuration.""" + lines = ["ModularMaterial:"] + el = self._elasticity + lines.append(f" Elasticity: {el.__class__.__name__}") + if isinstance(el, IsotropicElasticity): + lines.append(f" E={el.E}, nu={el.nu}, alpha={el.alpha}") + elif isinstance(el, CubicElasticity): + lines.append(f" E={el.E}, nu={el.nu}, G={el.G}, alpha={el.alpha}") + elif isinstance(el, TransverseIsotropicElasticity): + lines.append(f" EL={el.EL}, ET={el.ET}, nuTL={el.nuTL}, nuTT={el.nuTT}") + lines.append(f" GLT={el.GLT}, alpha_L={el.alpha_L}, alpha_T={el.alpha_T}, axis={el.axis}") + elif isinstance(el, OrthotropicElasticity): + lines.append(f" E1={el.E1}, E2={el.E2}, E3={el.E3}") + lines.append(f" nu12={el.nu12}, nu13={el.nu13}, nu23={el.nu23}") + lines.append(f" G12={el.G12}, G13={el.G13}, G23={el.G23}") + lines.append(f" alpha1={el.alpha1}, alpha2={el.alpha2}, alpha3={el.alpha3}") + + if not self._mechanisms: + lines.append(" Mechanisms: (none - pure elastic)") + else: + lines.append(f" Mechanisms ({len(self._mechanisms)}):") + for i, mech in enumerate(self._mechanisms): + lines.append(f" [{i}] {mech.__class__.__name__}") + if isinstance(mech, Plasticity): + lines.append(f" sigma_Y={mech.sigma_Y}") + lines.append(f" yield: {mech.yield_criterion.__class__.__name__}") + lines.append(f" iso_hard: {mech.isotropic_hardening.__class__.__name__}") + lines.append(f" kin_hard: {mech.kinematic_hardening.__class__.__name__}") + elif isinstance(mech, Viscoelasticity): + lines.append(f" {len(mech.terms)} Prony terms") + elif isinstance(mech, Damage): + lines.append(f" type: {mech.damage_type.name}") + lines.append(f" Y_0={mech.Y_0}, Y_c={mech.Y_c}") + + lines.append(f" nprops={self.nprops}, nstatev={self.nstatev}") + return "\n".join(lines) + + +# ============================================================================ +# Factory functions for common material models +# ============================================================================ + +def elastic_model(E: float, nu: float, alpha: float = 0.0) -> ModularMaterial: + """Create an isotropic elastic material. + + Parameters + ---------- + E : float + Young's modulus. + nu : float + Poisson's ratio. + alpha : float + Coefficient of thermal expansion (default: 0). + + Returns + ------- + ModularMaterial + """ + return ModularMaterial( + elasticity=IsotropicElasticity(E=E, nu=nu, alpha=alpha) + ) + + +def elastoplastic_model( + E: float, + nu: float, + sigma_Y: float, + k: float = 0.0, + m: float = 1.0, + alpha: float = 0.0, +) -> ModularMaterial: + """Create an isotropic elastoplastic material with power-law hardening. + + Parameters + ---------- + E : float + Young's modulus. + nu : float + Poisson's ratio. + sigma_Y : float + Initial yield stress. + k : float + Power-law hardening coefficient. + m : float + Power-law hardening exponent. + alpha : float + Coefficient of thermal expansion. + + Returns + ------- + ModularMaterial + """ + return ModularMaterial( + elasticity=IsotropicElasticity(E=E, nu=nu, alpha=alpha), + mechanisms=[ + Plasticity( + sigma_Y=sigma_Y, + isotropic_hardening=PowerLawHardening(k=k, m=m), + ) + ], + ) + + +def viscoelastic_model( + E: float, + nu: float, + prony_terms: Sequence[Tuple[float, float]], + alpha: float = 0.0, +) -> ModularMaterial: + """Create an isotropic viscoelastic material with Prony series. + + Parameters + ---------- + E : float + Young's modulus (instantaneous). + nu : float + Poisson's ratio. + prony_terms : sequence of (g, tau) tuples + Prony series terms: relative modulus and relaxation time. + alpha : float + Coefficient of thermal expansion. + + Returns + ------- + ModularMaterial + """ + return ModularMaterial( + elasticity=IsotropicElasticity(E=E, nu=nu, alpha=alpha), + mechanisms=[ + Viscoelasticity(terms=tuple(prony_terms)), + ], + ) diff --git a/simcoon-python-builder/src/python_wrappers/Libraries/Continuum_mechanics/umat.cpp b/simcoon-python-builder/src/python_wrappers/Libraries/Continuum_mechanics/umat.cpp index 8641b994e..5acc30252 100644 --- a/simcoon-python-builder/src/python_wrappers/Libraries/Continuum_mechanics/umat.cpp +++ b/simcoon-python-builder/src/python_wrappers/Libraries/Continuum_mechanics/umat.cpp @@ -37,6 +37,8 @@ #include +#include + #include //for rotate_strain #include @@ -69,7 +71,7 @@ namespace simpy { //Get the id of umat std::map list_umat; - list_umat = { {"UMEXT",0},{"UMABA",1},{"ELISO",2},{"ELIST",3},{"ELORT",4},{"EPICP",5},{"EPKCP",6},{"EPCHA",7},{"EPHIL",8},{"EPHAC",9},{"EPANI",10},{"EPDFA",11},{"EPHIN",12},{"SMAUT",13},{"SMANI",14},{"LLDM0",15},{"ZENER",16},{"ZENNK",17},{"PRONK",18},{"SMAMO",19},{"SMAMC",20},{"NEOHC",21},{"MOORI",22},{"YEOHH",23},{"ISHAH",24},{"GETHH",25},{"SWANH",26},{"MIHEN",100},{"MIMTN",101},{"MISCN",103},{"MIPLN",104} }; + list_umat = { {"UMEXT",0},{"UMABA",1},{"ELISO",2},{"ELIST",3},{"ELORT",4},{"EPICP",5},{"EPKCP",6},{"EPCHA",7},{"EPHIL",8},{"EPHAC",9},{"EPANI",10},{"EPDFA",11},{"EPHIN",12},{"SMAUT",13},{"SMANI",14},{"LLDM0",15},{"ZENER",16},{"ZENNK",17},{"PRONK",18},{"SMAMO",19},{"SMAMC",20},{"NEOHC",21},{"MOORI",22},{"YEOHH",23},{"ISHAH",24},{"GETHH",25},{"SWANH",26},{"MODUL",30},{"MIHEN",100},{"MIMTN",101},{"MISCN",103},{"MIPLN",104} }; int id_umat = list_umat[umat_name_py]; int arguments_type; //depends on the argument used in the umat @@ -253,6 +255,11 @@ namespace simpy { //simcoon::umat_sma_mono_cubic(umat_name, etot, Detot, F0, F1, sigma, Lt, L, DR, nprops, props, nstatev, statev, T, DT, Time, DTime, Wm, Wm_r, Wm_ir, Wm_d, ndi, nshr, start, tnew_dt); break; } + case 30: { + umat_function = &simcoon::umat_modular; + arguments_type = 1; + break; + } case 21: case 22: case 23: case 24: case 26: { F0 = carma::arr_to_cube_view(F0_py); F1 = carma::arr_to_cube_view(F1_py); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 439b5125f..1ac8e74e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,17 @@ target_sources(simcoon PRIVATE Continuum_mechanics/Umat/Thermomechanical/Viscoelasticity/Zener_fast.cpp Continuum_mechanics/Umat/Thermomechanical/Viscoelasticity/Zener_Nfast.cpp + # Continuum Mechanics - UMAT Modular + Continuum_mechanics/Umat/Modular/internal_variable.cpp + Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp + Continuum_mechanics/Umat/Modular/elasticity_module.cpp + Continuum_mechanics/Umat/Modular/yield_criterion.cpp + Continuum_mechanics/Umat/Modular/hardening.cpp + Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp + Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp + Continuum_mechanics/Umat/Modular/damage_mechanism.cpp + Continuum_mechanics/Umat/Modular/modular_umat.cpp + # Continuum Mechanics - UMAT Core Continuum_mechanics/Umat/fea_transfer.cpp Continuum_mechanics/Umat/umat_L_elastic.cpp diff --git a/src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp new file mode 100644 index 000000000..f88364a7e --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp @@ -0,0 +1,327 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file damage_mechanism.cpp + * @brief Implementation of DamageMechanism class + */ + +#include +#include +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +DamageMechanism::DamageMechanism(DamageType type) + : StrainMechanism("damage") + , damage_type_(type) + , Y_0_(0.0) + , Y_c_(1.0) + , D_c_(0.99) + , A_(1.0) + , n_(1.0) + , Y_current_(0.0) + , D_current_(0.0) + , dD_dY_(0.0) + , M_cached_(arma::zeros(6, 6)) + , M_cached_valid_(false) +{ +} + +// ========== Configuration ========== + +void DamageMechanism::configure(const arma::vec& props, int& offset) { + // Props layout for damage: + // props[offset]: damage_type (if not set in constructor) + // props[offset+1]: Y_0 (damage threshold) + // props[offset+2]: Y_c (critical damage driving force) + // props[offset+3]: D_c (critical damage value, optional, default 0.99) + // props[offset+4]: A or n (damage parameter, depending on type) + + // Read parameters + Y_0_ = props(offset); + Y_c_ = props(offset + 1); + offset += 2; + + // Read type-specific parameters + switch (damage_type_) { + case DamageType::LINEAR: + // No additional parameters needed + // D = (Y - Y_0) / (Y_c - Y_0) + break; + + case DamageType::EXPONENTIAL: + // A: exponential rate parameter + A_ = props(offset); + offset += 1; + break; + + case DamageType::POWER_LAW: + // n: power law exponent + n_ = props(offset); + offset += 1; + break; + + case DamageType::WEIBULL: + // A: scale parameter, n: shape parameter + A_ = props(offset); + n_ = props(offset + 1); + offset += 2; + break; + } + + // Validate parameters + if (Y_c_ <= Y_0_) { + throw std::runtime_error("DamageMechanism: Y_c must be greater than Y_0"); + } +} + +void DamageMechanism::register_variables(InternalVariableCollection& ivc) { + // Register damage variable + ivc.add_scalar("D", 0.0, false); + + // Register maximum damage driving force (history variable) + ivc.add_scalar("Y_max", 0.0, false); +} + +// ========== Constitutive Computations ========== + +double DamageMechanism::compute_driving_force(const arma::vec& sigma, const arma::mat& S) const { + // Damage driving force: Y = (1/2) * sigma : S : sigma + // This is the strain energy release rate + return 0.5 * arma::dot(sigma, S * sigma); +} + +double DamageMechanism::compute_damage(double Y, double Y_max) const { + // Use maximum of current and historical driving force + double Y_eff = std::max(Y, Y_max); + + // No damage below threshold + if (Y_eff <= Y_0_) { + return 0.0; + } + + double D = 0.0; + + switch (damage_type_) { + case DamageType::LINEAR: + // Linear damage evolution + D = (Y_eff - Y_0_) / (Y_c_ - Y_0_); + break; + + case DamageType::EXPONENTIAL: + // Exponential damage evolution + D = 1.0 - std::exp(-A_ * (Y_eff - Y_0_) / (Y_c_ - Y_0_)); + break; + + case DamageType::POWER_LAW: + // Power law damage evolution + D = std::pow((Y_eff - Y_0_) / (Y_c_ - Y_0_), n_); + break; + + case DamageType::WEIBULL: + // Weibull distribution-based damage + D = 1.0 - std::exp(-std::pow((Y_eff - Y_0_) / A_, n_)); + break; + } + + // Limit damage to critical value + return std::min(D, D_c_); +} + +double DamageMechanism::get_damage(const InternalVariableCollection& ivc) const { + return ivc.get("D").scalar(); +} + +arma::mat DamageMechanism::damaged_stiffness(const arma::mat& L, double D) { + return (1.0 - D) * L; +} + +void DamageMechanism::compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit +) const { + Phi.set_size(1); + Y_crit.set_size(1); + + // Get current damage and history + D_current_ = ivc.get("D").scalar(); + double Y_max = ivc.get("Y_max").scalar(); + + // Compute and cache compliance (only on first use or if L changes) + if (!M_cached_valid_) { + M_cached_ = arma::inv(L); + M_cached_valid_ = true; + } + + // Compute current driving force + Y_current_ = compute_driving_force(sigma, M_cached_); + + // Update Y_max if necessary + double Y_eff = std::max(Y_current_, Y_max); + + // Compute damage based on driving force + double D_new = compute_damage(Y_current_, Y_max); + + // Constraint: D - D_computed = 0 when loading, D - D_old = 0 when unloading + // Use Fischer-Burmeister formulation: Phi <= 0 and dD >= 0 + // + // For damage, the constraint is: + // Phi = Y - Y_max (loading condition) + // If Phi > 0, damage evolves; otherwise, elastic unloading + + Phi(0) = Y_current_ - Y_max; + + // Critical value for convergence + Y_crit(0) = std::max(Y_0_, 1e-6); + + // Compute derivative dD/dY for tangent + if (Y_eff > Y_0_ && D_current_ < D_c_) { + switch (damage_type_) { + case DamageType::LINEAR: + dD_dY_ = 1.0 / (Y_c_ - Y_0_); + break; + + case DamageType::EXPONENTIAL: + dD_dY_ = (A_ / (Y_c_ - Y_0_)) * std::exp(-A_ * (Y_eff - Y_0_) / (Y_c_ - Y_0_)); + break; + + case DamageType::POWER_LAW: + dD_dY_ = (n_ / (Y_c_ - Y_0_)) * std::pow((Y_eff - Y_0_) / (Y_c_ - Y_0_), n_ - 1.0); + break; + + case DamageType::WEIBULL: + dD_dY_ = (n_ / A_) * std::pow((Y_eff - Y_0_) / A_, n_ - 1.0) * + std::exp(-std::pow((Y_eff - Y_0_) / A_, n_)); + break; + } + } else { + dD_dY_ = 0.0; + } +} + +void DamageMechanism::compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map +) const { + // Damage doesn't have a strain-like flow direction + // The "direction" is the derivative of the driving force w.r.t. stress + // dY/dsigma = S : sigma (for quadratic Y) + + // Not applicable for scalar damage - leave empty +} + +void DamageMechanism::compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset +) const { + // The Jacobian contribution for damage + // dPhi/dD = 0 (Phi is independent of D directly) + // The coupling comes through the stress + + // For the loading condition Phi = Y - Y_max: + // B = dPhi/dDs = dY/dsigma * dsigma/dDs + + // Simplified: assume a unit stiffness for the constraint + B(row_offset, row_offset) = 1.0; +} + +arma::vec DamageMechanism::inelastic_strain(const InternalVariableCollection& ivc) const { + // Damage doesn't contribute a separate inelastic strain + // It affects the stiffness instead + return arma::zeros(6); +} + +void DamageMechanism::update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc +) { + double dY = ds(offset); + + // Update Y_max if we're loading + double& Y_max = ivc.get("Y_max").scalar(); + if (Y_current_ > Y_max) { + Y_max = Y_current_; + } + + // Compute and update damage + double& D = ivc.get("D").scalar(); + D = compute_damage(Y_current_, Y_max); +} + +void DamageMechanism::tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt +) const { + double D = ivc.get("D").scalar(); + + // Apply damage to tangent + // L_damaged = (1 - D) * L + Lt = (1.0 - D) * Lt; + + // Additional contribution from damage evolution + // If damage is evolving, there's a coupling term + if (dD_dY_ > sim_iota && D < D_c_) { + // Compute dY/dsigma = S : sigma (using cached compliance) + arma::vec dY_dsigma = M_cached_ * sigma; + + // Contribution: -dD/dY * sigma ⊗ dY/dsigma + // This represents the softening due to damage evolution + Lt -= dD_dY_ * (sigma * dY_dsigma.t()); + } +} + +void DamageMechanism::compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d +) const { + // Get damage increment + double dD = ivc.get("D").delta_scalar(); + + Wm_r = 0.0; + Wm_ir = 0.0; + Wm_d = 0.0; + + if (dD > sim_iota) { + // Energy dissipated by damage + // W_d = Y * dD (approximately) + Wm_d = Y_current_ * dD; + } +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp b/src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp new file mode 100644 index 000000000..0b336e71c --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp @@ -0,0 +1,228 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file elasticity_module.cpp + * @brief Implementation of ElasticityModule class + */ + +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +ElasticityModule::ElasticityModule() + : type_(ElasticityType::ISOTROPIC) + , L_(arma::zeros(6, 6)) + , M_(arma::zeros(6, 6)) + , alpha_(arma::zeros(6)) + , configured_(false) +{ +} + +// ========== Configuration ========== + +void ElasticityModule::configure_isotropic(double E, double nu, double alpha_scalar) { + type_ = ElasticityType::ISOTROPIC; + + // Compute stiffness using existing function + L_ = L_iso(E, nu, "Enu"); + + // Compute compliance + M_ = M_iso(E, nu, "Enu"); + + // Isotropic CTE + alpha_ = arma::zeros(6); + alpha_(0) = alpha_scalar; + alpha_(1) = alpha_scalar; + alpha_(2) = alpha_scalar; + // Shear components are zero for thermal expansion + + configured_ = true; +} + +void ElasticityModule::configure_cubic(double E, double nu, double G, double alpha_scalar) { + type_ = ElasticityType::CUBIC; + + // Compute stiffness using existing function (EnuG convention) + L_ = L_cubic(E, nu, G, "EnuG"); + + // Compute compliance + M_ = M_cubic(E, nu, G, "EnuG"); + + // Cubic CTE is isotropic + alpha_ = arma::zeros(6); + alpha_(0) = alpha_scalar; + alpha_(1) = alpha_scalar; + alpha_(2) = alpha_scalar; + + configured_ = true; +} + +void ElasticityModule::configure_cubic_Cii(double C11, double C12, double C44, double alpha_scalar) { + type_ = ElasticityType::CUBIC; + + // Compute stiffness using existing function (Cii convention) + L_ = L_cubic(C11, C12, C44, "Cii"); + + // Compute compliance + M_ = M_cubic(C11, C12, C44, "Cii"); + + // Cubic CTE is isotropic + alpha_ = arma::zeros(6); + alpha_(0) = alpha_scalar; + alpha_(1) = alpha_scalar; + alpha_(2) = alpha_scalar; + + configured_ = true; +} + +void ElasticityModule::configure_transverse_isotropic(double EL, double ET, double nuTL, double nuTT, + double GLT, double alpha_L, double alpha_T, int axis) { + type_ = ElasticityType::TRANSVERSE_ISOTROPIC; + + // Compute stiffness using existing function + L_ = L_isotrans(EL, ET, nuTL, nuTT, GLT, axis); + + // Compute compliance + M_ = M_isotrans(EL, ET, nuTL, nuTT, GLT, axis); + + // Transversely isotropic CTE + alpha_ = arma::zeros(6); + switch (axis) { + case 1: // x-axis is longitudinal + alpha_(0) = alpha_L; + alpha_(1) = alpha_T; + alpha_(2) = alpha_T; + break; + case 2: // y-axis is longitudinal + alpha_(0) = alpha_T; + alpha_(1) = alpha_L; + alpha_(2) = alpha_T; + break; + case 3: // z-axis is longitudinal (default) + default: + alpha_(0) = alpha_T; + alpha_(1) = alpha_T; + alpha_(2) = alpha_L; + break; + } + + configured_ = true; +} + +void ElasticityModule::configure_orthotropic(double E1, double E2, double E3, + double nu12, double nu13, double nu23, + double G12, double G13, double G23, + double alpha1, double alpha2, double alpha3) { + type_ = ElasticityType::ORTHOTROPIC; + + // Compute stiffness using existing function + // Note: L_ortho expects EnuG convention by default + L_ = L_ortho(E1, nu12, nu13, E2, nu23, E3, G12, G13, G23, "EnuG"); + + // Compute compliance + M_ = M_ortho(E1, nu12, nu13, E2, nu23, E3, G12, G13, G23, "EnuG"); + + // Orthotropic CTE + alpha_ = arma::zeros(6); + alpha_(0) = alpha1; + alpha_(1) = alpha2; + alpha_(2) = alpha3; + + configured_ = true; +} + +void ElasticityModule::configure(ElasticityType type, const arma::vec& props, int& offset) { + switch (type) { + case ElasticityType::ISOTROPIC: { + // props: E, nu, alpha + double E = props(offset); + double nu = props(offset + 1); + double alpha = props(offset + 2); + configure_isotropic(E, nu, alpha); + offset += 3; + break; + } + case ElasticityType::CUBIC: { + // props: E, nu, G, alpha + double E = props(offset); + double nu = props(offset + 1); + double G = props(offset + 2); + double alpha = props(offset + 3); + configure_cubic(E, nu, G, alpha); + offset += 4; + break; + } + case ElasticityType::TRANSVERSE_ISOTROPIC: { + // props: EL, ET, nuTL, nuTT, GLT, alpha_L, alpha_T, axis + double EL = props(offset); + double ET = props(offset + 1); + double nuTL = props(offset + 2); + double nuTT = props(offset + 3); + double GLT = props(offset + 4); + double alpha_L = props(offset + 5); + double alpha_T = props(offset + 6); + int axis = static_cast(props(offset + 7)); + configure_transverse_isotropic(EL, ET, nuTL, nuTT, GLT, alpha_L, alpha_T, axis); + offset += 8; + break; + } + case ElasticityType::ORTHOTROPIC: { + // props: E1, E2, E3, nu12, nu13, nu23, G12, G13, G23, alpha1, alpha2, alpha3 + double E1 = props(offset); + double E2 = props(offset + 1); + double E3 = props(offset + 2); + double nu12 = props(offset + 3); + double nu13 = props(offset + 4); + double nu23 = props(offset + 5); + double G12 = props(offset + 6); + double G13 = props(offset + 7); + double G23 = props(offset + 8); + double alpha1 = props(offset + 9); + double alpha2 = props(offset + 10); + double alpha3 = props(offset + 11); + configure_orthotropic(E1, E2, E3, nu12, nu13, nu23, G12, G13, G23, + alpha1, alpha2, alpha3); + offset += 12; + break; + } + default: + throw std::runtime_error("ElasticityModule: unknown elasticity type"); + } +} + +// ========== Derived Quantities ========== + +arma::mat ElasticityModule::damaged_L(double d) const { + if (!configured_) { + throw std::runtime_error("ElasticityModule: not configured"); + } + return (1.0 - d) * L_; +} + +arma::vec ElasticityModule::thermal_strain(double DT) const { + if (!configured_) { + throw std::runtime_error("ElasticityModule: not configured"); + } + return alpha_ * DT; +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/hardening.cpp b/src/Continuum_mechanics/Umat/Modular/hardening.cpp new file mode 100644 index 000000000..9632a9139 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/hardening.cpp @@ -0,0 +1,257 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file hardening.cpp + * @brief Implementation of hardening classes + */ + +#include +#include +#include +#include +#include + +namespace simcoon { + +// ============================================================================ +// ISOTROPIC HARDENING IMPLEMENTATIONS +// ============================================================================ + +// Factory method +std::unique_ptr IsotropicHardening::create(IsoHardType type, int N) { + switch (type) { + case IsoHardType::NONE: + return std::make_unique(); + case IsoHardType::LINEAR: + return std::make_unique(); + case IsoHardType::POWER_LAW: + return std::make_unique(); + case IsoHardType::VOCE: + return std::make_unique(); + case IsoHardType::COMBINED_VOCE: + return std::make_unique(N); + default: + return std::make_unique(); + } +} + +// LinearHardening +void LinearHardening::configure(const arma::vec& props, int& offset) { + H_ = props(offset); + offset += 1; +} + +// PowerLawHardening +void PowerLawHardening::configure(const arma::vec& props, int& offset) { + k_ = props(offset); + m_ = props(offset + 1); + offset += 2; +} + +double PowerLawHardening::R(double p) const { + if (p > sim_iota) { + return k_ * std::pow(p, m_); + } + return 0.0; +} + +double PowerLawHardening::dR_dp(double p) const { + if (p > sim_iota) { + return k_ * m_ * std::pow(p, m_ - 1.0); + } + return 0.0; +} + +// VoceHardening +void VoceHardening::configure(const arma::vec& props, int& offset) { + Q_ = props(offset); + b_ = props(offset + 1); + offset += 2; +} + +double VoceHardening::R(double p) const { + return Q_ * (1.0 - std::exp(-b_ * p)); +} + +double VoceHardening::dR_dp(double p) const { + return Q_ * b_ * std::exp(-b_ * p); +} + +// CombinedVoceHardening +void CombinedVoceHardening::configure(const arma::vec& props, int& offset) { + for (int i = 0; i < N_; ++i) { + Q_(i) = props(offset + 2 * i); + b_(i) = props(offset + 2 * i + 1); + } + offset += 2 * N_; +} + +double CombinedVoceHardening::R(double p) const { + double result = 0.0; + for (int i = 0; i < N_; ++i) { + result += Q_(i) * (1.0 - std::exp(-b_(i) * p)); + } + return result; +} + +double CombinedVoceHardening::dR_dp(double p) const { + double result = 0.0; + for (int i = 0; i < N_; ++i) { + result += Q_(i) * b_(i) * std::exp(-b_(i) * p); + } + return result; +} + +// ============================================================================ +// KINEMATIC HARDENING IMPLEMENTATIONS +// ============================================================================ + +// Factory method +std::unique_ptr KinematicHardening::create(KinHardType type, int N) { + switch (type) { + case KinHardType::NONE: + return std::make_unique(); + case KinHardType::PRAGER: + return std::make_unique(); + case KinHardType::ARMSTRONG_FREDERICK: + return std::make_unique(); + case KinHardType::CHABOCHE: + return std::make_unique(N); + default: + return std::make_unique(); + } +} + +// PragerHardening +void PragerHardening::configure(const arma::vec& props, int& offset) { + C_ = props(offset); + offset += 1; +} + +void PragerHardening::register_variables(InternalVariableCollection& ivc) { + ivc.add_vec("X", arma::zeros(6), true); // Backstress tensor +} + +arma::vec PragerHardening::total_backstress(const InternalVariableCollection& ivc) const { + return ivc.get("X").vec(); +} + +arma::vec PragerHardening::alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const { + // Linear Prager: dalpha = n (no recovery) + return n; +} + +double PragerHardening::hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const { + // H_kin = (2/3) * C + return (2.0 / 3.0) * C_; +} + +void PragerHardening::update(double dp, const arma::vec& n, InternalVariableCollection& ivc) { + // dX = (2/3) * C * n * dp + arma::vec& X = ivc.get("X").vec(); + X += (2.0 / 3.0) * C_ * n * dp; +} + +// ArmstrongFrederickHardening +void ArmstrongFrederickHardening::configure(const arma::vec& props, int& offset) { + C_ = props(offset); + D_ = props(offset + 1); + offset += 2; +} + +void ArmstrongFrederickHardening::register_variables(InternalVariableCollection& ivc) { + ivc.add_vec("X", arma::zeros(6), true); // Backstress tensor +} + +arma::vec ArmstrongFrederickHardening::total_backstress(const InternalVariableCollection& ivc) const { + return ivc.get("X").vec(); +} + +arma::vec ArmstrongFrederickHardening::alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const { + // AF: dalpha = n - (3/2) * D * X / C + const arma::vec& X = ivc.get("X").vec(); + if (C_ > sim_iota) { + return n - (1.5 * D_ / C_) * X; + } + return n; +} + +double ArmstrongFrederickHardening::hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const { + // H_kin = (2/3) * C - D * sum(X % n) + const arma::vec& X = ivc.get("X").vec(); + return (2.0 / 3.0) * C_ - D_ * arma::dot(X, n); +} + +void ArmstrongFrederickHardening::update(double dp, const arma::vec& n, InternalVariableCollection& ivc) { + // dX = (2/3) * C * n * dp - D * X * dp + arma::vec& X = ivc.get("X").vec(); + X += ((2.0 / 3.0) * C_ * n - D_ * X) * dp; +} + +// ChabocheHardening +void ChabocheHardening::configure(const arma::vec& props, int& offset) { + for (int i = 0; i < N_; ++i) { + C_(i) = props(offset + 2 * i); + D_(i) = props(offset + 2 * i + 1); + } + offset += 2 * N_; +} + +void ChabocheHardening::register_variables(InternalVariableCollection& ivc) { + // Register N backstress tensors + for (int i = 0; i < N_; ++i) { + ivc.add_vec("X_" + std::to_string(i), arma::zeros(6), true); + } +} + +arma::vec ChabocheHardening::total_backstress(const InternalVariableCollection& ivc) const { + arma::vec X_total = arma::zeros(6); + for (int i = 0; i < N_; ++i) { + X_total += ivc.get("X_" + std::to_string(i)).vec(); + } + return X_total; +} + +arma::vec ChabocheHardening::alpha_flow(int i, const arma::vec& n, const InternalVariableCollection& ivc) const { + // AF for term i: dalpha_i = n - (3/2) * D_i * X_i / C_i + const arma::vec& X_i = ivc.get("X_" + std::to_string(i)).vec(); + if (C_(i) > sim_iota) { + return n - (1.5 * D_(i) / C_(i)) * X_i; + } + return n; +} + +double ChabocheHardening::hardening_modulus(const arma::vec& n, const InternalVariableCollection& ivc) const { + // H_kin = sum_i [ (2/3) * C_i - D_i * (X_i . n) ] + double H_kin = 0.0; + for (int i = 0; i < N_; ++i) { + const arma::vec& X_i = ivc.get("X_" + std::to_string(i)).vec(); + H_kin += (2.0 / 3.0) * C_(i) - D_(i) * arma::dot(X_i, n); + } + return H_kin; +} + +void ChabocheHardening::update(double dp, const arma::vec& n, InternalVariableCollection& ivc) { + // dX_i = (2/3) * C_i * n * dp - D_i * X_i * dp + for (int i = 0; i < N_; ++i) { + arma::vec& X_i = ivc.get("X_" + std::to_string(i)).vec(); + X_i += ((2.0 / 3.0) * C_(i) * n - D_(i) * X_i) * dp; + } +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/internal_variable.cpp b/src/Continuum_mechanics/Umat/Modular/internal_variable.cpp new file mode 100644 index 000000000..742586469 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/internal_variable.cpp @@ -0,0 +1,315 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file internal_variable.cpp + * @brief Implementation of InternalVariable class + */ + +#include +#include + +namespace simcoon { + +// ========== Constructors ========== + +InternalVariable::InternalVariable(const std::string& name, double init, bool rotate) + : name_(name) + , type_(IVarType::SCALAR) + , scalar_value_(init) + , scalar_start_(init) + , vec_value_() + , vec_start_() + , mat_value_() + , mat_start_() + , requires_rotation_(rotate) + , statev_offset_(0) +{ +} + +InternalVariable::InternalVariable(const std::string& name, const arma::vec& init, bool rotate) + : name_(name) + , type_(IVarType::VECTOR_6) + , scalar_value_(0.0) + , scalar_start_(0.0) + , vec_value_(init.n_elem == 6 ? init : arma::zeros(6)) + , vec_start_(init.n_elem == 6 ? init : arma::zeros(6)) + , mat_value_() + , mat_start_() + , requires_rotation_(rotate) + , statev_offset_(0) +{ + if (init.n_elem != 6) { + throw std::runtime_error("InternalVariable: vector must have 6 components, got " + + std::to_string(init.n_elem)); + } +} + +InternalVariable::InternalVariable(const std::string& name, const arma::mat& init, bool rotate) + : name_(name) + , type_(IVarType::MATRIX_6x6) + , scalar_value_(0.0) + , scalar_start_(0.0) + , vec_value_() + , vec_start_() + , mat_value_(init.n_rows == 6 && init.n_cols == 6 ? init : arma::zeros(6, 6)) + , mat_start_(init.n_rows == 6 && init.n_cols == 6 ? init : arma::zeros(6, 6)) + , requires_rotation_(rotate) + , statev_offset_(0) +{ + if (init.n_rows != 6 || init.n_cols != 6) { + throw std::runtime_error("InternalVariable: matrix must be 6x6, got " + + std::to_string(init.n_rows) + "x" + std::to_string(init.n_cols)); + } +} + +// ========== Type Information ========== + +unsigned int InternalVariable::size() const { + switch (type_) { + case IVarType::SCALAR: + return 1; + case IVarType::VECTOR_6: + return 6; + case IVarType::MATRIX_6x6: + return 36; + } + return 0; // Should never reach here +} + +// ========== Value Accessors ========== + +double& InternalVariable::scalar() { + if (type_ != IVarType::SCALAR) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access scalar on non-scalar type"); + } + return scalar_value_; +} + +const double& InternalVariable::scalar() const { + if (type_ != IVarType::SCALAR) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access scalar on non-scalar type"); + } + return scalar_value_; +} + +arma::vec& InternalVariable::vec() { + if (type_ != IVarType::VECTOR_6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access vec on non-vector type"); + } + return vec_value_; +} + +const arma::vec& InternalVariable::vec() const { + if (type_ != IVarType::VECTOR_6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access vec on non-vector type"); + } + return vec_value_; +} + +arma::mat& InternalVariable::mat() { + if (type_ != IVarType::MATRIX_6x6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access mat on non-matrix type"); + } + return mat_value_; +} + +const arma::mat& InternalVariable::mat() const { + if (type_ != IVarType::MATRIX_6x6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access mat on non-matrix type"); + } + return mat_value_; +} + +// ========== Start Value Accessors ========== + +double& InternalVariable::scalar_start() { + if (type_ != IVarType::SCALAR) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access scalar_start on non-scalar type"); + } + return scalar_start_; +} + +const double& InternalVariable::scalar_start() const { + if (type_ != IVarType::SCALAR) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access scalar_start on non-scalar type"); + } + return scalar_start_; +} + +arma::vec& InternalVariable::vec_start() { + if (type_ != IVarType::VECTOR_6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access vec_start on non-vector type"); + } + return vec_start_; +} + +const arma::vec& InternalVariable::vec_start() const { + if (type_ != IVarType::VECTOR_6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access vec_start on non-vector type"); + } + return vec_start_; +} + +arma::mat& InternalVariable::mat_start() { + if (type_ != IVarType::MATRIX_6x6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access mat_start on non-matrix type"); + } + return mat_start_; +} + +const arma::mat& InternalVariable::mat_start() const { + if (type_ != IVarType::MATRIX_6x6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted to access mat_start on non-matrix type"); + } + return mat_start_; +} + +// ========== Increment Computation ========== + +double InternalVariable::delta_scalar() const { + if (type_ != IVarType::SCALAR) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted delta_scalar on non-scalar type"); + } + return scalar_value_ - scalar_start_; +} + +arma::vec InternalVariable::delta_vec() const { + if (type_ != IVarType::VECTOR_6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted delta_vec on non-vector type"); + } + return vec_value_ - vec_start_; +} + +arma::mat InternalVariable::delta_mat() const { + if (type_ != IVarType::MATRIX_6x6) { + throw std::runtime_error("InternalVariable '" + name_ + "': attempted delta_mat on non-matrix type"); + } + return mat_value_ - mat_start_; +} + +// ========== State Management ========== + +void InternalVariable::to_start() { + switch (type_) { + case IVarType::SCALAR: + scalar_start_ = scalar_value_; + break; + case IVarType::VECTOR_6: + vec_start_ = vec_value_; + break; + case IVarType::MATRIX_6x6: + mat_start_ = mat_value_; + break; + } +} + +void InternalVariable::set_start() { + switch (type_) { + case IVarType::SCALAR: + scalar_value_ = scalar_start_; + break; + case IVarType::VECTOR_6: + vec_value_ = vec_start_; + break; + case IVarType::MATRIX_6x6: + mat_value_ = mat_start_; + break; + } +} + +void InternalVariable::rotate(const arma::mat& DR) { + if (!requires_rotation_) { + return; + } + + switch (type_) { + case IVarType::SCALAR: + // Scalars don't rotate + break; + case IVarType::VECTOR_6: + // Use rotate_strain for strain-like tensors + // This applies to plastic strain, backstress, etc. + vec_value_ = rotate_strain(vec_value_, DR); + break; + case IVarType::MATRIX_6x6: + // Use rotateL for 6x6 matrices (stiffness-like) + mat_value_ = rotateL(mat_value_, DR); + break; + } +} + +// ========== Serialization ========== + +void InternalVariable::pack(arma::vec& statev) const { + unsigned int offset = statev_offset_; + + switch (type_) { + case IVarType::SCALAR: + if (offset < statev.n_elem) { + statev(offset) = scalar_value_; + } + break; + case IVarType::VECTOR_6: + for (unsigned int i = 0; i < 6 && (offset + i) < statev.n_elem; ++i) { + statev(offset + i) = vec_value_(i); + } + break; + case IVarType::MATRIX_6x6: + for (unsigned int i = 0; i < 6; ++i) { + for (unsigned int j = 0; j < 6; ++j) { + unsigned int idx = offset + i * 6 + j; + if (idx < statev.n_elem) { + statev(idx) = mat_value_(i, j); + } + } + } + break; + } +} + +void InternalVariable::unpack(const arma::vec& statev) { + unsigned int offset = statev_offset_; + + switch (type_) { + case IVarType::SCALAR: + if (offset < statev.n_elem) { + scalar_value_ = statev(offset); + scalar_start_ = statev(offset); + } + break; + case IVarType::VECTOR_6: + for (unsigned int i = 0; i < 6 && (offset + i) < statev.n_elem; ++i) { + vec_value_(i) = statev(offset + i); + vec_start_(i) = statev(offset + i); + } + break; + case IVarType::MATRIX_6x6: + for (unsigned int i = 0; i < 6; ++i) { + for (unsigned int j = 0; j < 6; ++j) { + unsigned int idx = offset + i * 6 + j; + if (idx < statev.n_elem) { + mat_value_(i, j) = statev(idx); + mat_start_(i, j) = statev(idx); + } + } + } + break; + } +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp b/src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp new file mode 100644 index 000000000..9b609bde0 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp @@ -0,0 +1,209 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file internal_variable_collection.cpp + * @brief Implementation of InternalVariableCollection class + */ + +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +InternalVariableCollection::InternalVariableCollection() + : variables_() + , name_to_index_() + , total_size_(0) + , offsets_computed_(false) +{ +} + +// ========== Registration ========== + +InternalVariable& InternalVariableCollection::add_scalar(const std::string& name, double init, bool rotate) { + if (has(name)) { + throw std::runtime_error("InternalVariableCollection: variable '" + name + "' already exists"); + } + + variables_.push_back(std::make_unique(name, init, rotate)); + name_to_index_[name] = variables_.size() - 1; + total_size_ += 1; + offsets_computed_ = false; + + return *variables_.back(); +} + +InternalVariable& InternalVariableCollection::add_vec(const std::string& name, const arma::vec& init, bool rotate) { + if (has(name)) { + throw std::runtime_error("InternalVariableCollection: variable '" + name + "' already exists"); + } + + variables_.push_back(std::make_unique(name, init, rotate)); + name_to_index_[name] = variables_.size() - 1; + total_size_ += 6; + offsets_computed_ = false; + + return *variables_.back(); +} + +InternalVariable& InternalVariableCollection::add_mat(const std::string& name, const arma::mat& init, bool rotate) { + if (has(name)) { + throw std::runtime_error("InternalVariableCollection: variable '" + name + "' already exists"); + } + + variables_.push_back(std::make_unique(name, init, rotate)); + name_to_index_[name] = variables_.size() - 1; + total_size_ += 36; + offsets_computed_ = false; + + return *variables_.back(); +} + +// ========== Access ========== + +InternalVariable& InternalVariableCollection::get(const std::string& name) { + auto it = name_to_index_.find(name); + if (it == name_to_index_.end()) { + throw std::runtime_error("InternalVariableCollection: variable '" + name + "' not found"); + } + return *variables_[it->second]; +} + +const InternalVariable& InternalVariableCollection::get(const std::string& name) const { + auto it = name_to_index_.find(name); + if (it == name_to_index_.end()) { + throw std::runtime_error("InternalVariableCollection: variable '" + name + "' not found"); + } + return *variables_[it->second]; +} + +bool InternalVariableCollection::has(const std::string& name) const { + return name_to_index_.find(name) != name_to_index_.end(); +} + +InternalVariable& InternalVariableCollection::operator[](size_t i) { + if (i >= variables_.size()) { + throw std::out_of_range("InternalVariableCollection: index " + std::to_string(i) + + " out of range (size: " + std::to_string(variables_.size()) + ")"); + } + return *variables_[i]; +} + +const InternalVariable& InternalVariableCollection::operator[](size_t i) const { + if (i >= variables_.size()) { + throw std::out_of_range("InternalVariableCollection: index " + std::to_string(i) + + " out of range (size: " + std::to_string(variables_.size()) + ")"); + } + return *variables_[i]; +} + +// ========== Offset Management ========== + +void InternalVariableCollection::compute_offsets(unsigned int base_offset) { + unsigned int current_offset = base_offset; + + for (auto& var : variables_) { + var->set_offset(current_offset); + current_offset += var->size(); + } + + offsets_computed_ = true; +} + +// ========== Bulk Operations ========== + +void InternalVariableCollection::pack_all(arma::vec& statev) const { + if (!offsets_computed_) { + throw std::runtime_error("InternalVariableCollection: compute_offsets must be called before pack_all"); + } + + for (const auto& var : variables_) { + var->pack(statev); + } +} + +void InternalVariableCollection::unpack_all(const arma::vec& statev) { + if (!offsets_computed_) { + throw std::runtime_error("InternalVariableCollection: compute_offsets must be called before unpack_all"); + } + + for (auto& var : variables_) { + var->unpack(statev); + } +} + +void InternalVariableCollection::rotate_all(const arma::mat& DR) { + for (auto& var : variables_) { + var->rotate(DR); + } +} + +void InternalVariableCollection::to_start_all() { + for (auto& var : variables_) { + var->to_start(); + } +} + +void InternalVariableCollection::set_start_all() { + for (auto& var : variables_) { + var->set_start(); + } +} + +void InternalVariableCollection::reset_all() { + for (auto& var : variables_) { + switch (var->type()) { + case IVarType::SCALAR: + var->scalar() = 0.0; + var->scalar_start() = 0.0; + break; + case IVarType::VECTOR_6: + var->vec().zeros(); + var->vec_start().zeros(); + break; + case IVarType::MATRIX_6x6: + var->mat().zeros(); + var->mat_start().zeros(); + break; + } + } +} + +// ========== Utility ========== + +std::vector InternalVariableCollection::names() const { + std::vector result; + result.reserve(variables_.size()); + + for (const auto& var : variables_) { + result.push_back(var->name()); + } + + return result; +} + +void InternalVariableCollection::clear() { + variables_.clear(); + name_to_index_.clear(); + total_size_ = 0; + offsets_computed_ = false; +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/modular_umat.cpp b/src/Continuum_mechanics/Umat/Modular/modular_umat.cpp new file mode 100644 index 000000000..4efb8b170 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/modular_umat.cpp @@ -0,0 +1,453 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file modular_umat.cpp + * @brief Implementation of ModularUMAT class + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +ModularUMAT::ModularUMAT() + : elasticity_() + , mechanisms_() + , ivc_() + , T_init_(0.0) + , sigma_start_(arma::zeros(6)) + , initialized_(false) + , maxiter_(100) + , precision_(1e-9) +{ +} + +// ========== Configuration ========== + +void ModularUMAT::set_elasticity(ElasticityType type, const arma::vec& props, int& offset) { + elasticity_.configure(type, props, offset); +} + +PlasticityMechanism& ModularUMAT::add_plasticity( + YieldType yield_type, + IsoHardType iso_type, + KinHardType kin_type, + int N_iso, + int N_kin, + const arma::vec& props, + int& offset +) { + auto mech = std::make_unique(yield_type, iso_type, kin_type, N_iso, N_kin); + mech->configure(props, offset); + mechanisms_.push_back(std::move(mech)); + return static_cast(*mechanisms_.back()); +} + +ViscoelasticMechanism& ModularUMAT::add_viscoelasticity( + int N_prony, + const arma::vec& props, + int& offset +) { + auto mech = std::make_unique(N_prony); + mech->configure(props, offset); + mech->set_reference_stiffness(elasticity_.L()); + mechanisms_.push_back(std::move(mech)); + return static_cast(*mechanisms_.back()); +} + +DamageMechanism& ModularUMAT::add_damage( + DamageType damage_type, + const arma::vec& props, + int& offset +) { + auto mech = std::make_unique(damage_type); + mech->configure(props, offset); + mechanisms_.push_back(std::move(mech)); + return static_cast(*mechanisms_.back()); +} + +void ModularUMAT::configure_from_props(const arma::vec& props, int offset) { + // Read elasticity type + int el_type = static_cast(props(offset)); + offset += 1; + + // Configure elasticity + set_elasticity(static_cast(el_type), props, offset); + + // Read number of mechanisms + int num_mech = static_cast(props(offset)); + offset += 1; + + // Configure each mechanism + for (int i = 0; i < num_mech; ++i) { + int mech_type = static_cast(props(offset)); + offset += 1; + + switch (static_cast(mech_type)) { + case MechanismType::PLASTICITY: { + // Read plasticity configuration + int yield_type = static_cast(props(offset)); + int iso_type = static_cast(props(offset + 1)); + int kin_type = static_cast(props(offset + 2)); + int N_iso = static_cast(props(offset + 3)); + int N_kin = static_cast(props(offset + 4)); + offset += 5; + + add_plasticity( + static_cast(yield_type), + static_cast(iso_type), + static_cast(kin_type), + N_iso, + N_kin, + props, + offset + ); + break; + } + case MechanismType::VISCOELASTICITY: { + int N_prony = static_cast(props(offset)); + offset += 1; + add_viscoelasticity(N_prony, props, offset); + break; + } + case MechanismType::DAMAGE: { + int dmg_type = static_cast(props(offset)); + offset += 1; + add_damage(static_cast(dmg_type), props, offset); + break; + } + default: + throw std::runtime_error("ModularUMAT: unknown mechanism type " + + std::to_string(mech_type)); + } + } +} + +void ModularUMAT::initialize(int nstatev, arma::vec& statev) { + // Register all internal variables + // First: T_init (temperature reference) + ivc_.add_scalar("T_init", 0.0, false); + + // Register variables from each mechanism + for (auto& mech : mechanisms_) { + mech->register_variables(ivc_); + } + + // Compute offsets (T_init is at offset 0) + ivc_.compute_offsets(0); + + // Check that we have enough state variables + int required = ivc_.total_statev_size(); + if (nstatev < required) { + throw std::runtime_error("ModularUMAT: nstatev (" + std::to_string(nstatev) + + ") < required (" + std::to_string(required) + ")"); + } + + // Unpack initial values from statev + ivc_.unpack_all(statev); + + initialized_ = true; +} + +int ModularUMAT::required_nstatev() const { + // T_init + all mechanism variables + int count = 1; // T_init + for (const auto& mech : mechanisms_) { + switch (mech->type()) { + case MechanismType::PLASTICITY: { + count += 7; // p(1) + EP(6) + auto* pm = dynamic_cast(mech.get()); + if (pm) { + count += 6 * pm->kinematic_hardening().num_backstresses(); + } + break; + } + case MechanismType::VISCOELASTICITY: { + auto* vm = dynamic_cast(mech.get()); + if (vm) { + count += 6 * vm->num_prony_terms(); // EV_i(6) per Prony term + } + break; + } + case MechanismType::DAMAGE: { + count += 2; // D(1) + Y_max(1) + break; + } + } + } + return count; +} + +// ========== Main UMAT Entry Point ========== + +void ModularUMAT::run( + const std::string& umat_name, + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + arma::mat& Lt, + arma::mat& L, + const arma::mat& DR, + int nprops, + const arma::vec& props, + int nstatev, + arma::vec& statev, + double T, + double DT, + double Time, + double DTime, + double& Wm, + double& Wm_r, + double& Wm_ir, + double& Wm_d, + int ndi, + int nshr, + bool start, + double& tnew_dt +) { + // Initialize if first call + if (!initialized_ || start) { + // On first call, set up internal variables from statev + if (!initialized_) { + // Configure from props if not already done + if (!elasticity_.is_configured()) { + int offset = 0; + configure_from_props(props, offset); + } + initialize(nstatev, statev); + } + + // Store initial temperature + T_init_ = statev(0); + if (start) { + T_init_ = T; + ivc_.get("T_init").scalar() = T_init_; + } + } else { + // Unpack state variables + ivc_.unpack_all(statev); + T_init_ = ivc_.get("T_init").scalar(); + } + + // Apply rotation for objectivity + ivc_.rotate_all(DR); + + // Save start values + sigma_start_ = sigma; + ivc_.to_start_all(); + + // Set elastic stiffness + L = elasticity_.L(); + + // Total number of constraints + int n_total = 0; + for (const auto& mech : mechanisms_) { + n_total += mech->num_constraints(); + } + + // Perform return mapping + arma::vec Ds_total = arma::zeros(n_total); + return_mapping(Etot, DEtot, sigma, T_init_, T, DT, DTime, ndi, Ds_total); + + // Compute consistent tangent + Lt = L; // Start with elastic stiffness + compute_tangent(sigma, Ds_total, Lt); + + // Compute work quantities + // Elastic strain + arma::vec E_inel = arma::zeros(6); + for (const auto& mech : mechanisms_) { + E_inel += mech->inelastic_strain(ivc_); + } + arma::vec Eel = Etot + DEtot - elasticity_.alpha() * (T - T_init_) - E_inel; + + // Elastic work (recoverable) + double Wm_r_new = 0.5 * arma::dot(sigma, Eel); + Wm_r = Wm_r_new - 0.5 * arma::dot(sigma_start_, Eel); + + // Dissipated and stored work from mechanisms + Wm_ir = 0.0; + Wm_d = 0.0; + for (const auto& mech : mechanisms_) { + double Wm_r_m = 0.0, Wm_ir_m = 0.0, Wm_d_m = 0.0; + mech->compute_work(sigma_start_, sigma, ivc_, Wm_r_m, Wm_ir_m, Wm_d_m); + Wm_ir += Wm_ir_m; + Wm_d += Wm_d_m; + } + + // Total mechanical work + Wm = Wm_r + Wm_ir + Wm_d; + + // Pack state variables + ivc_.pack_all(statev); +} + +void ModularUMAT::return_mapping( + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + double T_init, + double T, + double DT, + double DTime, + int ndi, + arma::vec& Ds_total +) { + // Total number of constraints + int n_total = 0; + for (const auto& mech : mechanisms_) { + n_total += mech->num_constraints(); + } + + if (n_total == 0) { + // Pure elastic: just compute stress + arma::vec Eel = Etot + DEtot - elasticity_.alpha() * (T - T_init); + sigma = el_pred(elasticity_.L(), Eel, ndi); + return; + } + + // Elastic prediction + arma::vec E_inel = arma::zeros(6); + for (const auto& mech : mechanisms_) { + E_inel += mech->inelastic_strain(ivc_); + } + arma::vec Eel = Etot + DEtot - elasticity_.alpha() * (T - T_init) - E_inel; + sigma = el_pred(elasticity_.L(), Eel, ndi); + + // Allocate constraint arrays + arma::vec Phi = arma::zeros(n_total); + arma::vec Y_crit = arma::zeros(n_total); + arma::mat B = arma::zeros(n_total, n_total); + arma::vec ds = arma::zeros(n_total); + Ds_total.zeros(n_total); + + // Iteration loop + double error = 1.0; + int iter = 0; + + while (iter < maxiter_ && error > precision_) { + int offset = 0; + + // Compute constraints and Jacobian from all mechanisms + for (const auto& mech : mechanisms_) { + int n = mech->num_constraints(); + + // Constraint functions + arma::vec Phi_m, Y_crit_m; + mech->compute_constraints(sigma, elasticity_.L(), DTime, ivc_, Phi_m, Y_crit_m); + + Phi.subvec(offset, offset + n - 1) = Phi_m; + Y_crit.subvec(offset, offset + n - 1) = Y_crit_m; + + // Jacobian contribution + mech->compute_jacobian_contribution(sigma, elasticity_.L(), ivc_, B, offset); + + offset += n; + } + + // Solve using Fischer-Burmeister + Fischer_Burmeister_m(Phi, Y_crit, B, Ds_total, ds, error); + + // Update all mechanisms + offset = 0; + for (auto& mech : mechanisms_) { + mech->update(ds, offset, ivc_); + offset += mech->num_constraints(); + } + + // Recompute stress + E_inel.zeros(); + for (const auto& mech : mechanisms_) { + E_inel += mech->inelastic_strain(ivc_); + } + Eel = Etot + DEtot - elasticity_.alpha() * (T - T_init) - E_inel; + sigma = el_pred(elasticity_.L(), Eel, ndi); + + ++iter; + } +} + +void ModularUMAT::compute_tangent( + const arma::vec& sigma, + const arma::vec& Ds_total, + arma::mat& Lt +) { + // Start with elastic stiffness + Lt = elasticity_.L(); + + // Subtract plastic/viscous/damage contributions + int offset = 0; + for (const auto& mech : mechanisms_) { + mech->tangent_contribution(sigma, elasticity_.L(), Ds_total, offset, ivc_, Lt); + offset += mech->num_constraints(); + } +} + +// ========== Standalone UMAT Function ========== + +void umat_modular( + const std::string& umat_name, + const arma::vec& Etot, + const arma::vec& DEtot, + arma::vec& sigma, + arma::mat& Lt, + arma::mat& L, + const arma::mat& DR, + const int& nprops, + const arma::vec& props, + const int& nstatev, + arma::vec& statev, + const double& T, + const double& DT, + const double& Time, + const double& DTime, + double& Wm, + double& Wm_r, + double& Wm_ir, + double& Wm_d, + const int& ndi, + const int& nshr, + const bool& start, + double& tnew_dt +) { + // Create a fresh instance each call. Configuration is re-parsed from props + // and state is restored from statev, ensuring correctness for multi-point + // simulations. The cost of re-parsing props is negligible compared to the + // Newton iteration in return_mapping. + ModularUMAT mumat; + + mumat.run( + umat_name, Etot, DEtot, sigma, Lt, L, DR, + nprops, props, nstatev, statev, + T, DT, Time, DTime, + Wm, Wm_r, Wm_ir, Wm_d, + ndi, nshr, start, tnew_dt + ); +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp new file mode 100644 index 000000000..7c25accd5 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp @@ -0,0 +1,259 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file plasticity_mechanism.cpp + * @brief Implementation of PlasticityMechanism class + */ + +#include +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +PlasticityMechanism::PlasticityMechanism( + YieldType yield_type, + IsoHardType iso_type, + KinHardType kin_type, + int N_iso, + int N_kin +) + : StrainMechanism("plasticity") + , yield_type_(yield_type) + , yield_(std::make_unique()) + , iso_hard_(IsotropicHardening::create(iso_type, N_iso)) + , kin_hard_(KinematicHardening::create(kin_type, N_kin)) + , sigma_Y_(0.0) + , flow_dir_(arma::zeros(6)) + , kappa_(arma::zeros(6)) + , dPhi_dp_(0.0) + , H_total_(0.0) +{ + // Configure parameter-free yield criteria immediately. + // Others will be configured in configure() when props are available. + switch (yield_type) { + case YieldType::VON_MISES: + yield_->configure_von_mises(); + break; + case YieldType::TRESCA: + yield_->configure_tresca(); + break; + default: + break; + } +} + +// ========== Configuration ========== + +void PlasticityMechanism::configure(const arma::vec& props, int& offset) { + // Props layout: + // 1. sigma_Y (initial yield stress) + // 2. Yield criterion parameters (if needed) + // 3. Isotropic hardening parameters + // 4. Kinematic hardening parameters + + sigma_Y_ = props(offset); + offset += 1; + + // Configure yield criterion from props if not already done + // (von Mises and Tresca are configured in the constructor since they + // need no parameters; others read their parameters from props here) + if (!yield_->is_configured()) { + yield_->configure(yield_type_, props, offset); + } + + // Configure hardening models + iso_hard_->configure(props, offset); + kin_hard_->configure(props, offset); +} + +void PlasticityMechanism::register_variables(InternalVariableCollection& ivc) { + // Register accumulated plastic strain (scalar) + ivc.add_scalar("p", 0.0, false); + + // Register plastic strain tensor + ivc.add_vec("EP", arma::zeros(6), true); + + // Register kinematic hardening variables + kin_hard_->register_variables(ivc); +} + +// ========== Constitutive Computations ========== + +void PlasticityMechanism::compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit +) const { + // Get internal variables + double p = ivc.get("p").scalar(); + arma::vec X = kin_hard_->total_backstress(ivc); + + // Compute shifted stress (sigma - X) + arma::vec sigma_eff = sigma - X; + + // Compute equivalent stress and flow direction + double sigma_eq = yield_->equivalent_stress(sigma_eff); + flow_dir_ = yield_->flow_direction(sigma_eff); + + // Compute isotropic hardening + double R = iso_hard_->R(p); + double dR_dp = iso_hard_->dR_dp(p); + + // Yield function: Phi = sigma_eq - R - sigma_Y + Phi.set_size(1); + Phi(0) = sigma_eq - R - sigma_Y_; + + // Critical value for convergence (relative to yield stress) + Y_crit.set_size(1); + Y_crit(0) = std::max(sigma_Y_, 1.0); + + // Cache values for Jacobian computation + kappa_ = L * flow_dir_; + dPhi_dp_ = -dR_dp; + + // Total hardening modulus: H = dR/dp + kinematic contribution + H_total_ = dR_dp + kin_hard_->hardening_modulus(flow_dir_, ivc); +} + +void PlasticityMechanism::compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map +) const { + arma::vec X = kin_hard_->total_backstress(ivc); + arma::vec n = yield_->flow_direction(sigma - X); + + // Plastic strain flow direction + Lambda_map["EP"] = n; + + // Backstress flow directions + for (int i = 0; i < kin_hard_->num_backstresses(); ++i) { + Lambda_map["X_" + std::to_string(i)] = kin_hard_->alpha_flow(i, n, ivc); + } +} + +void PlasticityMechanism::compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset +) const { + // B = -dPhi/dsigma * L * dPhi/dsigma + K + // where K includes isotropic and kinematic hardening contributions + + // dPhi/dsigma * L * dPhi/dsigma + double dPhi_L_dPhi = arma::dot(flow_dir_, kappa_); + + // B(0,0) = -dPhi_L_dPhi + H_total + // Note: H_total already includes both iso and kin contributions + B(row_offset, row_offset) = -dPhi_L_dPhi + H_total_; +} + +arma::vec PlasticityMechanism::inelastic_strain(const InternalVariableCollection& ivc) const { + return ivc.get("EP").vec(); +} + +void PlasticityMechanism::update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc +) { + double dp = ds(offset); + + if (dp > sim_iota) { + // Update accumulated plastic strain + double& p = ivc.get("p").scalar(); + p += dp; + + // Update plastic strain + arma::vec& EP = ivc.get("EP").vec(); + EP += dp * flow_dir_; + + // Update kinematic hardening variables + kin_hard_->update(dp, flow_dir_, ivc); + } +} + +void PlasticityMechanism::tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt +) const { + double Dp = Ds(offset); + + if (Dp > sim_iota) { + // Consistent tangent for plasticity: + // Lt = L - (L * n) ⊗ (L * n) / H_eff + // where H_eff = n : L : n + H_total + + double n_L_n = arma::dot(flow_dir_, kappa_); + double H_eff = n_L_n + H_total_; + + if (std::abs(H_eff) > sim_iota) { + // Lt -= (kappa ⊗ kappa) / H_eff + Lt -= (kappa_ * kappa_.t()) / H_eff; + } + } +} + +void PlasticityMechanism::compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d +) const { + // Get increments + double Dp = ivc.get("p").delta_scalar(); + arma::vec DEP = ivc.get("EP").delta_vec(); + + // Average stress during increment + arma::vec sigma_avg = 0.5 * (sigma_start + sigma); + + // Get backstress (if any) + arma::vec X = kin_hard_->total_backstress(ivc); + + // Dissipated work: sigma : dEP + Wm_d = arma::dot(sigma_avg, DEP); + + // For kinematic hardening, part of the work is stored + // Stored work in backstress: X : dEP (approximately) + if (kin_hard_->num_backstresses() > 0) { + Wm_ir = arma::dot(X, DEP); + Wm_d -= Wm_ir; + } else { + Wm_ir = 0.0; + } + + // Recoverable work (elastic) - handled by ModularUMAT + Wm_r = 0.0; +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp new file mode 100644 index 000000000..d1aac0c17 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp @@ -0,0 +1,281 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file viscoelastic_mechanism.cpp + * @brief Implementation of ViscoelasticMechanism class + */ + +#include +#include +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +ViscoelasticMechanism::ViscoelasticMechanism(int N_prony) + : StrainMechanism("viscoelastic") + , N_prony_(N_prony) + , g_i_(N_prony, 0.0) + , tau_i_(N_prony, 1.0) + , L_0_(arma::eye(6, 6)) + , M_0_(arma::eye(6, 6)) + , use_deviatoric_only_(false) + , EV_n_(N_prony) + , flow_i_(N_prony) + , factor_i_(N_prony, 0.0) +{ +} + +// ========== Configuration ========== + +void ViscoelasticMechanism::configure(const arma::vec& props, int& offset) { + // Props layout for viscoelasticity: + // For each Prony term i: + // props[offset + 2*i]: g_i (relative modulus) + // props[offset + 2*i + 1]: tau_i (relaxation time) + + for (int i = 0; i < N_prony_; ++i) { + g_i_[i] = props(offset + 2 * i); + tau_i_[i] = props(offset + 2 * i + 1); + + if (tau_i_[i] <= 0.0) { + throw std::runtime_error("ViscoelasticMechanism: relaxation time must be positive"); + } + } + offset += 2 * N_prony_; + + // Initialize cached vectors + for (int i = 0; i < N_prony_; ++i) { + EV_n_[i] = arma::zeros(6); + flow_i_[i] = arma::zeros(6); + } +} + +void ViscoelasticMechanism::register_variables(InternalVariableCollection& ivc) { + // Register viscous strain tensor for each Prony term + for (int i = 0; i < N_prony_; ++i) { + ivc.add_vec("EV_" + std::to_string(i), arma::zeros(6), true); + } +} + +// ========== Constitutive Computations ========== + +void ViscoelasticMechanism::compute_constraints( + const arma::vec& sigma, + const arma::mat& L, + double DTime, + const InternalVariableCollection& ivc, + arma::vec& Phi, + arma::vec& Y_crit +) const { + Phi.set_size(N_prony_); + Y_crit.set_size(N_prony_); + + for (int i = 0; i < N_prony_; ++i) { + // Get current viscous strain + arma::vec EV_i = ivc.get("EV_" + std::to_string(i)).vec(); + EV_n_[i] = ivc.get("EV_" + std::to_string(i)).vec_start(); + + // Compute exponential factor + double exp_factor = std::exp(-DTime / tau_i_[i]); + factor_i_[i] = exp_factor; + + // For generalized Maxwell model with implicit integration: + // EV_i^{n+1} = exp(-dt/tau) * EV_i^n + (1 - exp(-dt/tau)) * C_i^{-1} : sigma + // + // The constraint equation is: + // Phi_i = EV_i - [exp(-dt/tau) * EV_i^n + (1 - exp(-dt/tau)) * g_i * S^{-1} : sigma] + // + // where S^{-1} is the compliance. For isotropic elasticity with only deviatoric + // viscoelasticity, C_i^{-1} : sigma = (1/2G) * dev(sigma) + + // Compliance (inverse of L_0) + arma::mat S = M_0_; + + // Target viscous strain (equilibrium value scaled by g_i) + arma::vec EV_target = g_i_[i] * (S * sigma); + + // Predicted viscous strain using implicit integration + arma::vec EV_pred = exp_factor * EV_n_[i] + (1.0 - exp_factor) * EV_target; + + // Constraint: actual - predicted should be zero + // We use a relaxation formulation where ds represents the strain rate + Phi(i) = arma::norm(EV_i - EV_pred); + + // Flow direction (normalized difference) + if (Phi(i) > sim_iota) { + flow_i_[i] = (EV_target - EV_n_[i]) / (Phi(i) + sim_iota); + } else { + flow_i_[i] = arma::zeros(6); + } + + // Critical value for convergence + Y_crit(i) = std::max(arma::norm(EV_target), 1e-6); + } +} + +void ViscoelasticMechanism::compute_flow_directions( + const arma::vec& sigma, + const InternalVariableCollection& ivc, + std::map& Lambda_map +) const { + // Compliance + arma::mat S = M_0_; + + for (int i = 0; i < N_prony_; ++i) { + // Flow direction for viscous strain is towards the target + arma::vec EV_target = g_i_[i] * (S * sigma); + arma::vec EV_i = ivc.get("EV_" + std::to_string(i)).vec(); + + arma::vec direction = EV_target - EV_i; + double norm = arma::norm(direction); + if (norm > sim_iota) { + Lambda_map["EV_" + std::to_string(i)] = direction / norm; + } else { + Lambda_map["EV_" + std::to_string(i)] = arma::zeros(6); + } + } +} + +void ViscoelasticMechanism::compute_jacobian_contribution( + const arma::vec& sigma, + const arma::mat& L, + const InternalVariableCollection& ivc, + arma::mat& B, + int row_offset +) const { + // For viscoelasticity, the Jacobian contribution comes from the + // coupling between stress and viscous strain evolution + + for (int i = 0; i < N_prony_; ++i) { + // The diagonal term represents the "stiffness" of the constraint + // For implicit integration: dPhi/dDs = 1 (approximately) + B(row_offset + i, row_offset + i) = 1.0; + } +} + +arma::vec ViscoelasticMechanism::inelastic_strain(const InternalVariableCollection& ivc) const { + arma::vec EV_total = arma::zeros(6); + + for (int i = 0; i < N_prony_; ++i) { + EV_total += ivc.get("EV_" + std::to_string(i)).vec(); + } + + return EV_total; +} + +void ViscoelasticMechanism::update( + const arma::vec& ds, + int offset, + InternalVariableCollection& ivc +) { + // For viscoelasticity, we update each branch using implicit integration + // The viscous strain is computed directly from the implicit formula + + // Note: In the standard return mapping, ds represents multiplier increments. + // For viscoelasticity with implicit integration, the update is typically + // done directly based on the stress and time increment. + + // This update function is called during the Newton iteration. + // Since viscoelasticity is often treated differently (exponential integrator), + // we may need to adapt this approach. + + // For now, use a simple update based on the flow direction + for (int i = 0; i < N_prony_; ++i) { + double ds_i = ds(offset + i); + + if (std::abs(ds_i) > sim_iota) { + arma::vec& EV_i = ivc.get("EV_" + std::to_string(i)).vec(); + EV_i += ds_i * flow_i_[i]; + } + } +} + +void ViscoelasticMechanism::tangent_contribution( + const arma::vec& sigma, + const arma::mat& L, + const arma::vec& Ds, + int offset, + const InternalVariableCollection& ivc, + arma::mat& Lt +) const { + // The viscoelastic tangent modification + // For a generalized Maxwell model, the tangent at a given time increment is: + // + // L_t = L_inf + sum_i [ g_i * (1 - exp(-dt/tau_i)) / (dt/tau_i) * L_0 ] + // + // However, for the algorithmic tangent, we need to account for the + // implicit integration scheme. + + // For a generalized Maxwell model, the algorithmic tangent accounts + // for the viscous strain evolution over the time increment. + // The effective tangent reduction is: + // Lt = L * (1 - sum_i [ g_i * (1 - exp(-dt/tau_i)) ]) + // + // This reduces to L_inf = (1 - sum(g_i)) * L for dt -> infinity (fully relaxed) + // and to L for dt -> 0 (instantaneous response). + + double reduction = 0.0; + for (int i = 0; i < N_prony_; ++i) { + reduction += g_i_[i] * (1.0 - factor_i_[i]); + } + + // Apply reduction to the tangent + Lt *= (1.0 - reduction); +} + +void ViscoelasticMechanism::compute_work( + const arma::vec& sigma_start, + const arma::vec& sigma, + const InternalVariableCollection& ivc, + double& Wm_r, + double& Wm_ir, + double& Wm_d +) const { + Wm_r = 0.0; + Wm_ir = 0.0; + Wm_d = 0.0; + + // Average stress + arma::vec sigma_avg = 0.5 * (sigma_start + sigma); + + for (int i = 0; i < N_prony_; ++i) { + // Get viscous strain increment + arma::vec DEV_i = ivc.get("EV_" + std::to_string(i)).delta_vec(); + + // Dissipated work: sigma : dEV + Wm_d += arma::dot(sigma_avg, DEV_i); + } + + // For linear viscoelasticity, there's also stored energy in the springs + // This is a simplified computation + Wm_ir = 0.0; // Stored in Maxwell elements (approximation) +} + +double ViscoelasticMechanism::long_term_factor() const { + double sum_g = 0.0; + for (int i = 0; i < N_prony_; ++i) { + sum_g += g_i_[i]; + } + return 1.0 - sum_g; +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp b/src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp new file mode 100644 index 000000000..9abb99801 --- /dev/null +++ b/src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp @@ -0,0 +1,198 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +/** + * @file yield_criterion.cpp + * @brief Implementation of YieldCriterion class + */ + +#include +#include +#include + +namespace simcoon { + +// ========== Constructor ========== + +YieldCriterion::YieldCriterion() + : type_(YieldType::VON_MISES) + , params_() + , configured_(false) +{ +} + +// ========== Private Helpers ========== + +std::string YieldCriterion::type_string() const { + switch (type_) { + case YieldType::VON_MISES: + return "Mises"; + case YieldType::TRESCA: + return "Tresca"; + case YieldType::DRUCKER: + return "Drucker"; + case YieldType::HILL: + return "Hill"; + case YieldType::DFA: + return "DFA"; + case YieldType::ANISOTROPIC: + return "Ani"; + default: + return "Mises"; + } +} + +// ========== Configuration ========== + +void YieldCriterion::configure_von_mises() { + type_ = YieldType::VON_MISES; + params_ = arma::zeros(1); // No parameters needed + configured_ = true; +} + +void YieldCriterion::configure_tresca() { + type_ = YieldType::TRESCA; + params_ = arma::zeros(1); // No parameters needed + configured_ = true; +} + +void YieldCriterion::configure_drucker(double b, double n) { + type_ = YieldType::DRUCKER; + params_ = arma::vec({b, n}); + configured_ = true; +} + +void YieldCriterion::configure_hill(double F, double G, double H, double L, double M, double N) { + type_ = YieldType::HILL; + params_ = arma::vec({F, G, H, L, M, N}); + configured_ = true; +} + +void YieldCriterion::configure_dfa(double F, double G, double H, double L, double M, double N, double K) { + type_ = YieldType::DFA; + params_ = arma::vec({F, G, H, L, M, N, K}); + configured_ = true; +} + +void YieldCriterion::configure_anisotropic(double P11, double P22, double P33, + double P12, double P13, double P23, + double P44, double P55, double P66) { + type_ = YieldType::ANISOTROPIC; + params_ = arma::vec({P11, P22, P33, P12, P13, P23, P44, P55, P66}); + configured_ = true; +} + +void YieldCriterion::configure(YieldType type, const arma::vec& props, int& offset) { + switch (type) { + case YieldType::VON_MISES: + configure_von_mises(); + // No additional props consumed + break; + case YieldType::TRESCA: + configure_tresca(); + // No additional props consumed + break; + case YieldType::DRUCKER: { + double b = props(offset); + double n = props(offset + 1); + configure_drucker(b, n); + offset += 2; + break; + } + case YieldType::HILL: { + double F = props(offset); + double G = props(offset + 1); + double H = props(offset + 2); + double L = props(offset + 3); + double M = props(offset + 4); + double N = props(offset + 5); + configure_hill(F, G, H, L, M, N); + offset += 6; + break; + } + case YieldType::DFA: { + double F = props(offset); + double G = props(offset + 1); + double H = props(offset + 2); + double L = props(offset + 3); + double M = props(offset + 4); + double N = props(offset + 5); + double K = props(offset + 6); + configure_dfa(F, G, H, L, M, N, K); + offset += 7; + break; + } + case YieldType::ANISOTROPIC: { + double P11 = props(offset); + double P22 = props(offset + 1); + double P33 = props(offset + 2); + double P12 = props(offset + 3); + double P13 = props(offset + 4); + double P23 = props(offset + 5); + double P44 = props(offset + 6); + double P55 = props(offset + 7); + double P66 = props(offset + 8); + configure_anisotropic(P11, P22, P33, P12, P13, P23, P44, P55, P66); + offset += 9; + break; + } + default: + throw std::runtime_error("YieldCriterion: unknown yield type"); + } +} + +// ========== Yield Function Computations ========== + +double YieldCriterion::equivalent_stress(const arma::vec& sigma) const { + if (!configured_) { + throw std::runtime_error("YieldCriterion: not configured"); + } + return Eq_stress(sigma, type_string(), params_); +} + +double YieldCriterion::equivalent_stress(const arma::vec& sigma, const arma::vec& X) const { + if (!configured_) { + throw std::runtime_error("YieldCriterion: not configured"); + } + return Eq_stress(sigma - X, type_string(), params_); +} + +arma::vec YieldCriterion::flow_direction(const arma::vec& sigma) const { + if (!configured_) { + throw std::runtime_error("YieldCriterion: not configured"); + } + return dEq_stress(sigma, type_string(), params_); +} + +arma::vec YieldCriterion::flow_direction(const arma::vec& sigma, const arma::vec& X) const { + if (!configured_) { + throw std::runtime_error("YieldCriterion: not configured"); + } + return dEq_stress(sigma - X, type_string(), params_); +} + +arma::vec YieldCriterion::plastic_flow(const arma::vec& sigma) const { + // For associated flow rules, plastic flow equals flow direction + return flow_direction(sigma); +} + +arma::vec YieldCriterion::plastic_flow(const arma::vec& sigma, const arma::vec& X) const { + // For associated flow rules, plastic flow equals flow direction + return flow_direction(sigma, X); +} + +} // namespace simcoon diff --git a/src/Continuum_mechanics/Umat/umat_smart.cpp b/src/Continuum_mechanics/Umat/umat_smart.cpp index 1a5974c0b..e5b9a3428 100644 --- a/src/Continuum_mechanics/Umat/umat_smart.cpp +++ b/src/Continuum_mechanics/Umat/umat_smart.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -314,7 +315,7 @@ void select_umat_M(phase_characteristics &rve, const mat &DR,const double &Time, std::map list_umat; - list_umat = {{"UMEXT",0},{"UMABA",1},{"ELISO",2},{"ELIST",3},{"ELORT",4},{"EPICP",5},{"EPKCP",6},{"EPCHA",7},{"SMAUT",8},{"SMANI",9},{"LLDM0",10},{"ZENER",11},{"ZENNK",12},{"PRONK",13},{"EPHIL",17},{"EPHAC",18},{"EPANI",19},{"EPDFA",20},{"EPCHG",21},{"EPHIN",22},{"SMAMO",23},{"SMAMC",24},{"MIHEN",100},{"MIMTN",101},{"MISCN",103},{"MIPLN",104}}; + list_umat = {{"UMEXT",0},{"UMABA",1},{"ELISO",2},{"ELIST",3},{"ELORT",4},{"EPICP",5},{"EPKCP",6},{"EPCHA",7},{"SMAUT",8},{"SMANI",9},{"LLDM0",10},{"ZENER",11},{"ZENNK",12},{"PRONK",13},{"EPHIL",17},{"EPHAC",18},{"EPANI",19},{"EPDFA",20},{"EPCHG",21},{"EPHIN",22},{"SMAMO",23},{"SMAMC",24},{"MIHEN",100},{"MIMTN",101},{"MISCN",103},{"MIPLN",104},{"MODUL",200}}; rve.global2local(); auto umat_M = std::dynamic_pointer_cast(rve.sptr_sv_local); @@ -439,6 +440,11 @@ void select_umat_M(phase_characteristics &rve, const mat &DR,const double &Time, umat_sma_mono_cubic(umat_M->Etot, umat_M->DEtot, umat_M->sigma, umat_M->Lt, umat_M->L, DR, rve.sptr_matprops->nprops, rve.sptr_matprops->props, umat_M->nstatev, umat_M->statev, umat_M->T, umat_M->DT, Time, DTime, umat_M->Wm(0), umat_M->Wm(1), umat_M->Wm(2), umat_M->Wm(3), ndi, nshr, start, tnew_dt); break; } + case 200: { + // Modular UMAT - composable constitutive model + umat_modular(rve.sptr_matprops->umat_name, umat_M->Etot, umat_M->DEtot, umat_M->sigma, umat_M->Lt, umat_M->L, DR, rve.sptr_matprops->nprops, rve.sptr_matprops->props, umat_M->nstatev, umat_M->statev, umat_M->T, umat_M->DT, Time, DTime, umat_M->Wm(0), umat_M->Wm(1), umat_M->Wm(2), umat_M->Wm(3), ndi, nshr, start, tnew_dt); + break; + } case 100: case 101: case 103: case 104: { umat_multi(rve, DR, Time, DTime, ndi, nshr, start, solver_type, tnew_dt, list_umat[rve.sptr_matprops->umat_name]); break; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 155f10026..cf9b294f3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ set(TEST_SRCS # Libraries - Umat test/Libraries/Umat/TAba2sim.cpp + test/Libraries/Umat/Modular/Tmodular_umat.cpp # UMATs test/Umats/ELISO/TELISO.cpp diff --git a/test/Libraries/Umat/Modular/Tmodular_umat.cpp b/test/Libraries/Umat/Modular/Tmodular_umat.cpp new file mode 100644 index 000000000..bb3729e39 --- /dev/null +++ b/test/Libraries/Umat/Modular/Tmodular_umat.cpp @@ -0,0 +1,601 @@ +/* This file is part of simcoon. + +simcoon is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +simcoon is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with simcoon. If not, see . + +*/ + +///@file Tmodular_umat.cpp +///@brief Unit tests for Modular UMAT components +///@version 1.0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace arma; +using namespace simcoon; + +// ========== InternalVariable Tests ========== + +class InternalVariableTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(InternalVariableTest, ScalarConstruction) { + InternalVariable iv("test_scalar", 1.5, false); + + EXPECT_EQ(iv.name(), "test_scalar"); + EXPECT_EQ(iv.type(), IVarType::SCALAR); + EXPECT_DOUBLE_EQ(iv.scalar(), 1.5); + EXPECT_EQ(iv.size(), 1u); + EXPECT_FALSE(iv.requires_rotation()); +} + +TEST_F(InternalVariableTest, VectorConstruction) { + vec init = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; + InternalVariable iv("test_vec", init, true); + + EXPECT_EQ(iv.name(), "test_vec"); + EXPECT_EQ(iv.type(), IVarType::VECTOR_6); + EXPECT_EQ(iv.size(), 6u); + EXPECT_TRUE(iv.requires_rotation()); + + for (int i = 0; i < 6; i++) { + EXPECT_DOUBLE_EQ(iv.vec()(i), init(i)); + } +} + +TEST_F(InternalVariableTest, MatrixConstruction) { + mat init = eye(6, 6); + InternalVariable iv("test_mat", init, true); + + EXPECT_EQ(iv.name(), "test_mat"); + EXPECT_EQ(iv.type(), IVarType::MATRIX_6x6); + EXPECT_EQ(iv.size(), 36u); + EXPECT_TRUE(iv.requires_rotation()); +} + +TEST_F(InternalVariableTest, ScalarPackUnpack) { + InternalVariable iv("test", 3.14, false); + iv.set_offset(5); + + vec statev = zeros(10); + iv.pack(statev); + EXPECT_DOUBLE_EQ(statev(5), 3.14); + + // Modify and unpack + statev(5) = 2.71; + iv.unpack(statev); + EXPECT_DOUBLE_EQ(iv.scalar(), 2.71); +} + +TEST_F(InternalVariableTest, VectorPackUnpack) { + vec init = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; + InternalVariable iv("test", init, false); + iv.set_offset(2); + + vec statev = zeros(10); + iv.pack(statev); + + for (int i = 0; i < 6; i++) { + EXPECT_DOUBLE_EQ(statev(2 + i), init(i)); + } + + // Modify and unpack + statev.subvec(2, 7).fill(10.0); + iv.unpack(statev); + for (int i = 0; i < 6; i++) { + EXPECT_DOUBLE_EQ(iv.vec()(i), 10.0); + } +} + +TEST_F(InternalVariableTest, DeltaComputation) { + InternalVariable iv("test", 1.0, false); + iv.to_start(); // Save current as start + + iv.scalar() = 3.0; // Modify + EXPECT_DOUBLE_EQ(iv.delta_scalar(), 2.0); +} + +// ========== InternalVariableCollection Tests ========== + +class InternalVariableCollectionTest : public ::testing::Test { +protected: + void SetUp() override { + ivc_.add_scalar("p", 0.0, false); + ivc_.add_vec("EP", zeros(6), true); + ivc_.add_scalar("D", 0.0, false); + } + + InternalVariableCollection ivc_; +}; + +TEST_F(InternalVariableCollectionTest, Registration) { + EXPECT_TRUE(ivc_.has("p")); + EXPECT_TRUE(ivc_.has("EP")); + EXPECT_TRUE(ivc_.has("D")); + EXPECT_FALSE(ivc_.has("nonexistent")); +} + +TEST_F(InternalVariableCollectionTest, Access) { + ivc_.get("p").scalar() = 0.5; + EXPECT_DOUBLE_EQ(ivc_.get("p").scalar(), 0.5); + + ivc_.get("EP").vec()(0) = 1.0; + EXPECT_DOUBLE_EQ(ivc_.get("EP").vec()(0), 1.0); +} + +TEST_F(InternalVariableCollectionTest, OffsetComputation) { + ivc_.compute_offsets(0); + + // p: offset 0, size 1 + // EP: offset 1, size 6 + // D: offset 7, size 1 + // Total: 8 + EXPECT_EQ(ivc_.total_statev_size(), 8); +} + +TEST_F(InternalVariableCollectionTest, PackUnpackAll) { + ivc_.compute_offsets(0); + + ivc_.get("p").scalar() = 0.1; + ivc_.get("EP").vec().fill(0.01); + ivc_.get("D").scalar() = 0.05; + + vec statev = zeros(10); + ivc_.pack_all(statev); + + EXPECT_DOUBLE_EQ(statev(0), 0.1); + for (int i = 0; i < 6; i++) { + EXPECT_DOUBLE_EQ(statev(1 + i), 0.01); + } + EXPECT_DOUBLE_EQ(statev(7), 0.05); + + // Modify and unpack + statev.fill(0.5); + ivc_.unpack_all(statev); + + EXPECT_DOUBLE_EQ(ivc_.get("p").scalar(), 0.5); + EXPECT_DOUBLE_EQ(ivc_.get("D").scalar(), 0.5); +} + +// ========== ElasticityModule Tests ========== + +class ElasticityModuleTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(ElasticityModuleTest, IsotropicConfiguration) { + ElasticityModule em; + + // E = 210000 MPa, nu = 0.3, alpha = 1.2e-5 + vec props = {210000.0, 0.3, 1.2e-5}; + int offset = 0; + + em.configure(ElasticityType::ISOTROPIC, props, offset); + + EXPECT_TRUE(em.is_configured()); + EXPECT_EQ(offset, 3); + + // Check stiffness properties + mat L = em.L(); + EXPECT_EQ(L.n_rows, 6u); + EXPECT_EQ(L.n_cols, 6u); + + // C11 should be positive + EXPECT_GT(L(0, 0), 0.0); + + // Symmetry check + for (unsigned int i = 0; i < 6; i++) { + for (unsigned int j = 0; j < 6; j++) { + EXPECT_NEAR(L(i, j), L(j, i), 1e-10); + } + } +} + +TEST_F(ElasticityModuleTest, CubicConfiguration) { + ElasticityModule em; + + // Cubic: E = 185000, nu = 0.28, G = 39700, alpha = 1.2e-5 + // Note: for cubic, G is independent from E and nu + vec props = {185000.0, 0.28, 39700.0, 1.2e-5}; + int offset = 0; + + em.configure(ElasticityType::CUBIC, props, offset); + + EXPECT_TRUE(em.is_configured()); + EXPECT_EQ(offset, 4); + + mat L = em.L(); + EXPECT_EQ(L.n_rows, 6u); + EXPECT_EQ(L.n_cols, 6u); + + // C11 should be positive + EXPECT_GT(L(0, 0), 0.0); + + // Cubic symmetry: C11 = C22 = C33 + EXPECT_NEAR(L(0, 0), L(1, 1), 1e-10); + EXPECT_NEAR(L(1, 1), L(2, 2), 1e-10); + + // C12 = C13 = C23 + EXPECT_NEAR(L(0, 1), L(0, 2), 1e-10); + EXPECT_NEAR(L(0, 1), L(1, 2), 1e-10); + + // C44 = C55 = C66 = G + EXPECT_NEAR(L(3, 3), 39700.0, 1e-6); + EXPECT_NEAR(L(3, 3), L(4, 4), 1e-10); + EXPECT_NEAR(L(4, 4), L(5, 5), 1e-10); + + // For cubic, C44 != (C11-C12)/2 in general (anisotropy) + double C11 = L(0, 0); + double C12 = L(0, 1); + double C44 = L(3, 3); + double iso_check = (C11 - C12) / 2.0; + // They should NOT be equal unless the material is isotropic + // (i.e., Zener ratio A = 2*C44/(C11-C12) != 1 in general) + + // CTE isotropic for cubic + vec alpha = em.alpha(); + EXPECT_DOUBLE_EQ(alpha(0), 1.2e-5); + EXPECT_DOUBLE_EQ(alpha(1), 1.2e-5); + EXPECT_DOUBLE_EQ(alpha(2), 1.2e-5); +} + +TEST_F(ElasticityModuleTest, CubicCiiConfiguration) { + ElasticityModule em; + + // Typical values for an FCC metal (e.g., copper-like) + // C11 = 185000, C12 = 158000, C44 = 39700 + em.configure_cubic_Cii(185000.0, 158000.0, 39700.0, 0.0); + + EXPECT_TRUE(em.is_configured()); + + mat L = em.L(); + + // Direct check of Cii values + EXPECT_NEAR(L(0, 0), 185000.0, 1e-6); + EXPECT_NEAR(L(0, 1), 158000.0, 1e-6); + EXPECT_NEAR(L(3, 3), 39700.0, 1e-6); +} + +TEST_F(ElasticityModuleTest, ThermalExpansion) { + ElasticityModule em; + + vec props = {210000.0, 0.3, 1.2e-5}; + int offset = 0; + + em.configure(ElasticityType::ISOTROPIC, props, offset); + + vec alpha = em.alpha(); + EXPECT_DOUBLE_EQ(alpha(0), 1.2e-5); + EXPECT_DOUBLE_EQ(alpha(1), 1.2e-5); + EXPECT_DOUBLE_EQ(alpha(2), 1.2e-5); + EXPECT_DOUBLE_EQ(alpha(3), 0.0); + EXPECT_DOUBLE_EQ(alpha(4), 0.0); + EXPECT_DOUBLE_EQ(alpha(5), 0.0); +} + +// ========== YieldCriterion Tests ========== + +class YieldCriterionTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(YieldCriterionTest, VonMisesUniaxial) { + YieldCriterion yc; + yc.configure_von_mises(); + + // Uniaxial stress: sigma = [100, 0, 0, 0, 0, 0] + vec sigma = zeros(6); + sigma(0) = 100.0; + + double sigma_eq = yc.equivalent_stress(sigma); + + // For uniaxial tension, von Mises = sigma_11 + EXPECT_NEAR(sigma_eq, 100.0, 1e-6); +} + +TEST_F(YieldCriterionTest, VonMisesPureShear) { + YieldCriterion yc; + yc.configure_von_mises(); + + // Pure shear: sigma = [0, 0, 0, tau, 0, 0] + vec sigma = zeros(6); + sigma(3) = 100.0; // tau_xy + + double sigma_eq = yc.equivalent_stress(sigma); + + // For pure shear, von Mises = sqrt(3) * tau + EXPECT_NEAR(sigma_eq, sqrt(3.0) * 100.0, 1e-6); +} + +TEST_F(YieldCriterionTest, FlowDirectionNormalized) { + YieldCriterion yc; + yc.configure_von_mises(); + + vec sigma = {100.0, 50.0, -30.0, 20.0, 10.0, 5.0}; + + vec n = yc.flow_direction(sigma); + + // Flow direction should be normalized (for deviatoric stress) + // Check it's not zero + double norm_n = norm(n); + EXPECT_GT(norm_n, 0.0); +} + +// ========== IsotropicHardening Tests ========== + +class IsotropicHardeningTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(IsotropicHardeningTest, PowerLawHardening) { + auto hard = IsotropicHardening::create(IsoHardType::POWER_LAW, 1); + + // k = 500, m = 0.2 + vec props = {500.0, 0.2}; + int offset = 0; + hard->configure(props, offset); + + // R(p) = k * p^m + double p = 0.1; + double R = hard->R(p); + double expected = 500.0 * pow(0.1, 0.2); + + EXPECT_NEAR(R, expected, 1e-6); + + // dR/dp = k * m * p^(m-1) + double dR = hard->dR_dp(p); + double expected_dR = 500.0 * 0.2 * pow(0.1, 0.2 - 1.0); + EXPECT_NEAR(dR, expected_dR, 1e-6); +} + +TEST_F(IsotropicHardeningTest, VoceHardening) { + auto hard = IsotropicHardening::create(IsoHardType::VOCE, 1); + + // Q = 200, b = 10 + vec props = {200.0, 10.0}; + int offset = 0; + hard->configure(props, offset); + + // R(p) = Q * (1 - exp(-b*p)) + double p = 0.1; + double R = hard->R(p); + double expected = 200.0 * (1.0 - exp(-10.0 * 0.1)); + + EXPECT_NEAR(R, expected, 1e-6); +} + +// ========== KinematicHardening Tests ========== + +class KinematicHardeningTest : public ::testing::Test { +protected: + InternalVariableCollection ivc_; +}; + +TEST_F(KinematicHardeningTest, PragerHardening) { + auto hard = KinematicHardening::create(KinHardType::PRAGER, 1); + + // H = 10000 + vec props = {10000.0}; + int offset = 0; + hard->configure(props, offset); + + hard->register_variables(ivc_); + + // Check initial backstress is zero + vec X = hard->total_backstress(ivc_); + EXPECT_DOUBLE_EQ(norm(X), 0.0); + + // Hardening modulus should be (2/3)*C for Prager kinematic hardening + vec n = {1.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + double H = hard->hardening_modulus(n, ivc_); + EXPECT_NEAR(H, (2.0 / 3.0) * 10000.0, 1e-6); +} + +// ========== PlasticityMechanism Tests ========== + +class PlasticityMechanismTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(PlasticityMechanismTest, Construction) { + PlasticityMechanism pm(YieldType::VON_MISES, IsoHardType::VOCE, KinHardType::NONE, 1, 0); + + EXPECT_EQ(pm.type(), MechanismType::PLASTICITY); + EXPECT_EQ(pm.num_constraints(), 1); +} + +TEST_F(PlasticityMechanismTest, InelasticStrainInitiallyZero) { + PlasticityMechanism pm(YieldType::VON_MISES, IsoHardType::NONE, KinHardType::NONE, 0, 0); + + InternalVariableCollection ivc; + pm.register_variables(ivc); + ivc.compute_offsets(0); + + vec statev = zeros(ivc.total_statev_size()); + ivc.unpack_all(statev); + + vec EP = pm.inelastic_strain(ivc); + EXPECT_DOUBLE_EQ(norm(EP), 0.0); +} + +// ========== ModularUMAT Tests ========== + +class ModularUMATTest : public ::testing::Test { +protected: + void SetUp() override {} +}; + +TEST_F(ModularUMATTest, Construction) { + ModularUMAT mumat; + + EXPECT_FALSE(mumat.is_initialized()); + EXPECT_EQ(mumat.num_mechanisms(), 0u); +} + +TEST_F(ModularUMATTest, ElasticConfiguration) { + ModularUMAT mumat; + + // E = 210000 MPa, nu = 0.3, alpha = 0 + vec props = {210000.0, 0.3, 0.0}; + int offset = 0; + + mumat.set_elasticity(ElasticityType::ISOTROPIC, props, offset); + + const mat& L = mumat.elasticity().L(); + EXPECT_GT(L(0, 0), 0.0); +} + +TEST_F(ModularUMATTest, AddPlasticity) { + ModularUMAT mumat; + + // Elasticity: E = 210000, nu = 0.3, alpha = 0 + vec props_el = {210000.0, 0.3, 0.0}; + int offset_el = 0; + mumat.set_elasticity(ElasticityType::ISOTROPIC, props_el, offset_el); + + // Plasticity: sigma_Y = 300, Q = 200, b = 10 + vec props_pl = {300.0, 200.0, 10.0}; + int offset_pl = 0; + mumat.add_plasticity(YieldType::VON_MISES, IsoHardType::VOCE, KinHardType::NONE, 1, 0, props_pl, offset_pl); + + EXPECT_EQ(mumat.num_mechanisms(), 1u); +} + +TEST_F(ModularUMATTest, ElasticResponse) { + ModularUMAT mumat; + + // Very high yield stress to ensure elastic response + vec props_el = {210000.0, 0.3, 0.0}; + int offset_el = 0; + mumat.set_elasticity(ElasticityType::ISOTROPIC, props_el, offset_el); + + vec props_pl = {1e10, 0.0, 1.0}; // Very high yield stress + int offset_pl = 0; + mumat.add_plasticity(YieldType::VON_MISES, IsoHardType::VOCE, KinHardType::NONE, 1, 0, props_pl, offset_pl); + + // Initialize + int nstatev = 20; + vec statev = zeros(nstatev); + mumat.initialize(nstatev, statev); + + // Run a small elastic step + vec Etot = zeros(6); + vec DEtot = {0.001, -0.0003, -0.0003, 0.0, 0.0, 0.0}; // Uniaxial strain + vec sigma = zeros(6); + mat Lt = zeros(6, 6); + mat L = zeros(6, 6); + mat DR = eye(3, 3); + vec props = zeros(10); + double T = 293.0, DT = 0.0, Time = 0.0, DTime = 1.0; + double Wm = 0.0, Wm_r = 0.0, Wm_ir = 0.0, Wm_d = 0.0; + double tnew_dt = 1.0; + + mumat.run("MODUL", Etot, DEtot, sigma, Lt, L, DR, 10, props, nstatev, statev, + T, DT, Time, DTime, Wm, Wm_r, Wm_ir, Wm_d, 3, 3, true, tnew_dt); + + // Check stress is non-zero + EXPECT_GT(norm(sigma), 0.0); + + // Check tangent is symmetric and positive definite + for (unsigned int i = 0; i < 6; i++) { + for (unsigned int j = 0; j < 6; j++) { + EXPECT_NEAR(Lt(i, j), Lt(j, i), 1e-6); + } + } +} + +// ========== Integration Test ========== + +TEST(ModularUMATIntegration, UniaxialTension) { + ModularUMAT mumat; + + // Isotropic elasticity: E = 210000, nu = 0.3, alpha = 0 + vec props_el = {210000.0, 0.3, 0.0}; + int offset_el = 0; + mumat.set_elasticity(ElasticityType::ISOTROPIC, props_el, offset_el); + + // Von Mises plasticity with Voce hardening: sigma_Y = 300, Q = 200, b = 10 + vec props_pl = {300.0, 200.0, 10.0}; + int offset_pl = 0; + mumat.add_plasticity(YieldType::VON_MISES, IsoHardType::VOCE, KinHardType::NONE, 1, 0, props_pl, offset_pl); + + // Initialize + int nstatev = 20; + vec statev = zeros(nstatev); + mumat.initialize(nstatev, statev); + + // Apply strain incrementally + vec Etot = zeros(6); + vec sigma = zeros(6); + mat Lt = zeros(6, 6); + mat L = zeros(6, 6); + mat DR = eye(3, 3); + vec props = zeros(10); + double T = 293.0, DT = 0.0; + double Wm = 0.0, Wm_r = 0.0, Wm_ir = 0.0, Wm_d = 0.0; + double tnew_dt = 1.0; + + double E = 210000.0; + double nu = 0.3; + double sigma_Y = 300.0; + + // First increment: elastic + vec DEtot = {0.001, -0.0003, -0.0003, 0.0, 0.0, 0.0}; + double DTime = 1.0; + double Time = 0.0; + + mumat.run("MODUL", Etot, DEtot, sigma, Lt, L, DR, 10, props, nstatev, statev, + T, DT, Time, DTime, Wm, Wm_r, Wm_ir, Wm_d, 3, 3, true, tnew_dt); + + // Stress should be approximately E * strain = 210000 * 0.001 = 210 MPa + // (Below yield) + EXPECT_NEAR(sigma(0), E * 0.001, 10.0); // Allow some tolerance due to Poisson effect + + // Multiple increments to go into plasticity + for (int i = 0; i < 10; i++) { + Etot += DEtot; + Time += DTime; + + mumat.run("MODUL", Etot, DEtot, sigma, Lt, L, DR, 10, props, nstatev, statev, + T, DT, Time, DTime, Wm, Wm_r, Wm_ir, Wm_d, 3, 3, false, tnew_dt); + } + + // After significant straining, stress should be above initial yield + // but following hardening curve + double sigma_11 = sigma(0); + EXPECT_GT(sigma_11, sigma_Y - 50.0); // Should be near or above yield + + // Accumulated plastic strain should be positive + double p = mumat.internal_variables().get("p").scalar(); + // If we strained enough to yield, p should be positive + if (sigma_11 > sigma_Y - 10.0) { + EXPECT_GT(p, 0.0); + } +} diff --git a/testBin/Umats/UMEXT/results/results_job_global-0.txt b/testBin/Umats/UMEXT/results/results_job_global-0.txt deleted file mode 100755 index 3547bc6ef..000000000 --- a/testBin/Umats/UMEXT/results/results_job_global-0.txt +++ /dev/null @@ -1,200 +0,0 @@ -1 1 1 1 0.01 290 0 0 0.0002 -6e-05 -6e-05 0 0 0 14 2.37903e-16 1.00924e-15 0 0 0 0.0014 0.0014 0 0 -1 1 1 2 0.02 290 0 0 0.0004 -0.00012 -0.00012 0 0 0 28 4.75806e-16 2.01848e-15 0 0 0 0.0056 0.0056 0 0 -1 1 1 3 0.03 290 0 0 0.0006 -0.00018 -0.00018 0 0 0 42 3.48147e-15 6.96932e-15 0 0 0 0.0126 0.0126 0 0 -1 1 1 4 0.04 290 0 0 0.0008 -0.00024 -0.00024 0 0 0 56 9.51613e-16 4.03696e-15 0 0 0 0.0224 0.0224 0 0 -1 1 1 5 0.05 290 0 0 0.001 -0.0003 -0.0003 0 0 0 70 7.30354e-15 8.21002e-15 0 0 0 0.035 0.035 0 0 -1 1 1 6 0.06 290 0 0 0.0012 -0.00036 -0.00036 0 0 0 84 4.77368e-15 8.83037e-15 0 0 0 0.0504 0.0504 0 0 -1 1 1 7 0.07 290 0 0 0.0014 -0.00042 -0.00042 0 0 0 98 9.34926e-15 1.65562e-14 0 0 0 0.0686 0.0686 0 0 -1 1 1 8 0.08 290 0 0 0.0016 -0.00048 -0.00048 0 0 0 112 1.39248e-14 1.71765e-14 0 0 0 0.0896 0.0896 0 0 -1 1 1 9 0.09 290 0 0 0.0018 -0.00054 -0.00054 0 0 0 126 2.56058e-14 2.49023e-14 0 0 0 0.1134 0.1134 0 0 -1 1 1 10 0.1 290 0 0 0.002 -0.0006 -0.0006 0 0 0 140 3.94761e-14 3.77363e-14 0 0 0 0.14 0.14 0 0 -1 1 1 11 0.11 290 0 0 0.0022 -0.00066 -0.00066 0 0 0 154 4.97936e-14 5.05704e-14 0 0 0 0.1694 0.1694 0 0 -1 1 1 12 0.12 290 0 0 0.0024 -0.00072 -0.00072 0 0 0 168 6.36639e-14 5.6299e-14 0 0 0 0.2016 0.2016 0 0 -1 1 1 13 0.13 290 0 0 0.0026 -0.00078 -0.00078 0 0 0 182 5.97706e-14 5.49222e-14 0 0 0 0.2366 0.2366 0 0 -1 1 1 14 0.14 290 0 0 0.0028 -0.00084 -0.00084 0 0 0 196 7.71935e-14 6.77562e-14 0 0 0 0.2744 0.2744 0 0 -1 1 1 15 0.15 290 0 0 0.003 -0.0009 -0.0009 0 0 0 210 8.39583e-14 8.05902e-14 0 0 0 0.315 0.315 0 0 -1 1 1 16 0.16 290 0 0 0.0032 -0.00096 -0.00096 0 0 0 224 1.12039e-13 1.07635e-13 0 0 0 0.3584 0.3584 0 0 -1 1 1 17 0.17 290 0 0 0.0034 -0.00102 -0.00102 0 0 0 238 1.11699e-13 1.06258e-13 0 0 0 0.4046 0.4046 0 0 -1 1 1 18 0.18 290 0 0 0.0036 -0.00108 -0.00108 0 0 0 252 1.11358e-13 1.04882e-13 0 0 0 0.4536 0.4536 0 0 -1 1 1 19 0.19 290 0 0 0.0038 -0.00114 -0.00114 0 0 0 266 1.39439e-13 1.31926e-13 0 0 0 0.5054 0.5054 0 0 -1 1 1 20 0.2 290 0 0 0.004 -0.0012 -0.0012 0 0 0 280 1.31993e-13 1.3055e-13 0 0 0 0.56 0.56 0 0 -1 1 1 21 0.21 290 0 0 0.0042 -0.00126 -0.00126 0 0 0 294 1.31653e-13 1.29173e-13 0 0 0 0.6174 0.6174 0 0 -1 1 1 22 0.22 290 0 0 0.0044 -0.00132 -0.00132 0 0 0 308 1.0289e-13 9.93743e-14 0 0 0 0.6776 0.6776 0 0 -1 1 1 23 0.23 290 0 0 0.0046 -0.00138 -0.00138 0 0 0 322 1.0255e-13 1.12208e-13 0 0 0 0.7406 0.7406 0 0 -1 1 1 24 0.24 290 0 0 0.0048 -0.00144 -0.00144 0 0 0 336 1.02209e-13 9.66206e-14 0 0 0 0.8064 0.8064 0 0 -1 1 1 25 0.25 290 0 0 0.005 -0.0015 -0.0015 0 0 0 350 9.47632e-14 1.09455e-13 0 0 0 0.875 0.875 0 0 -1 1 1 26 0.26 290 0 0 0.0052 -0.00156 -0.00156 0 0 0 364 9.44226e-14 9.3867e-14 0 0 0 0.9464 0.9464 0 0 -1 1 1 27 0.27 290 0 0 0.0054 -0.00162 -0.00162 0 0 0 378 1.01187e-13 1.06701e-13 0 0 0 1.0206 1.0206 0 0 -1 1 1 28 0.28 290 0 0 0.0056 -0.00168 -0.00168 0 0 0 392 5.82142e-14 6.26917e-14 0 0 0 1.0976 1.0976 0 0 -1 1 1 29 0.29 290 0 0 0.0058 -0.00174 -0.00174 0 0 0 406 5.78736e-14 7.55257e-14 0 0 0 1.1774 1.1774 0 0 -1 1 1 30 0.3 290 0 0 0.006 -0.0018 -0.0018 0 0 0 420 5.7533e-14 5.9938e-14 0 0 0 1.26 1.26 0 0 -1 1 1 31 0.31 290 0 0 0.0062 -0.00186 -0.00186 0 0 0 434 5.71924e-14 7.27721e-14 0 0 0 1.3454 1.3454 0 0 -1 1 1 32 0.32 290 0 0 0.0064 -0.00192 -0.00192 0 0 0 448 5.68518e-14 5.71844e-14 0 0 0 1.4336 1.4336 0 0 -1 1 1 33 0.33 290 0 0 0.0066 -0.00198 -0.00198 0 0 0 462 -9.08921e-15 -7.25802e-15 0 0 0 1.5246 1.5246 0 0 -1 1 1 34 0.34 290 0 0 0.0068 -0.00204 -0.00204 0 0 0 476 -3.97598e-15 -1.4857e-14 0 0 0 1.6184 1.6184 0 0 -1 1 1 35 0.35 290 0 0 0.007 -0.0021 -0.0021 0 0 0 490 -6.9917e-14 -5.08777e-14 0 0 0 1.715 1.715 0 0 -1 1 1 36 0.36 290 0 0 0.0072 -0.00216 -0.00216 0 0 0 504 -6.48038e-14 -5.84767e-14 0 0 0 1.8144 1.8144 0 0 -1 1 1 37 0.37 290 0 0 0.0074 -0.00222 -0.00222 0 0 0 518 -1.30745e-13 -1.22919e-13 0 0 0 1.9166 1.9166 0 0 -1 1 1 38 0.38 290 0 0 0.0076 -0.00228 -0.00228 0 0 0 532 -1.82475e-13 -1.87362e-13 0 0 0 2.0216 2.0216 0 0 -1 1 1 39 0.39 290 0 0 0.0078 -0.00234 -0.00234 0 0 0 546 -1.91573e-13 -1.94961e-13 0 0 0 2.1294 2.1294 0 0 -1 1 1 40 0.4 290 0 0 0.008 -0.0024 -0.0024 0 0 0 560 -1.86459e-13 -2.0256e-13 0 0 0 2.24 2.24 0 0 -1 1 1 41 0.41 290 0 0 0.0082 -0.00246 -0.00246 0 0 0 574 -1.95557e-13 -1.81737e-13 0 0 0 2.3534 2.3534 0 0 -1 1 1 42 0.42 290 0 0 0.0084 -0.00252 -0.00252 0 0 0 588 -1.90444e-13 -1.89336e-13 0 0 0 2.4696 2.4696 0 0 -1 1 1 43 0.43 290 0 0 0.0086 -0.00258 -0.00258 0 0 0 602 -1.99541e-13 -1.96935e-13 0 0 0 2.5886 2.5886 0 0 -1 1 1 44 0.44 290 0 0 0.0088 -0.00264 -0.00264 0 0 0 616 -1.94428e-13 -2.04534e-13 0 0 0 2.7104 2.7104 0 0 -1 1 1 45 0.45 290 0 0 0.009 -0.0027 -0.0027 0 0 0 630 -2.03526e-13 -2.12133e-13 0 0 0 2.835 2.835 0 0 -1 1 1 46 0.46 290 0 0 0.0092 -0.00276 -0.00276 0 0 0 644 -1.98413e-13 -1.9131e-13 0 0 0 2.9624 2.9624 0 0 -1 1 1 47 0.47 290 0 0 0.0094 -0.00282 -0.00282 0 0 0 658 -2.0751e-13 -2.27331e-13 0 0 0 3.0926 3.0926 0 0 -1 1 1 48 0.48 290 0 0 0.0096 -0.00288 -0.00288 0 0 0 672 -2.16608e-13 -2.06508e-13 0 0 0 3.2256 3.2256 0 0 -1 1 1 49 0.49 290 0 0 0.0098 -0.00294 -0.00294 0 0 0 686 -2.11495e-13 -1.85685e-13 0 0 0 3.3614 3.3614 0 0 -1 1 1 50 0.5 290 0 0 0.01 -0.003 -0.003 0 0 0 700 -2.20592e-13 -2.21706e-13 0 0 0 3.5 3.5 0 0 -1 1 1 51 0.51 290 0 0 0.0102 -0.00306 -0.00306 0 0 0 714 -2.15479e-13 -2.00883e-13 0 0 0 3.6414 3.6414 0 0 -1 1 1 52 0.52 290 0 0 0.0104 -0.00312 -0.00312 0 0 0 728 -2.24577e-13 -2.36904e-13 0 0 0 3.7856 3.7856 0 0 -1 1 1 53 0.53 290 0 0 0.0106 -0.00318 -0.00318 0 0 0 742 -2.33674e-13 -2.16081e-13 0 0 0 3.9326 3.9326 0 0 -1 1 1 54 0.54 290 0 0 0.0108 -0.00324 -0.00324 0 0 0 756 -2.28561e-13 -2.52102e-13 0 0 0 4.0824 4.0824 0 0 -1 1 1 55 0.55 290 0 0 0.011 -0.0033 -0.0033 0 0 0 770 -2.23448e-13 -2.31279e-13 0 0 0 4.235 4.235 0 0 -1 1 1 56 0.56 290 0 0 0.0112 -0.00336 -0.00336 0 0 0 784 -2.18334e-13 -2.10457e-13 0 0 0 4.3904 4.3904 0 0 -1 1 1 57 0.57 290 0 0 0.0114 -0.00342 -0.00342 0 0 0 798 -2.41643e-13 -2.46477e-13 0 0 0 4.5486 4.5486 0 0 -1 1 1 58 0.58 290 0 0 0.0116 -0.00348 -0.00348 0 0 0 812 -2.3653e-13 -2.25655e-13 0 0 0 4.7096 4.7096 0 0 -1 1 1 59 0.59 290 0 0 0.0118 -0.00354 -0.00354 0 0 0 826 -2.31416e-13 -2.61675e-13 0 0 0 4.8734 4.8734 0 0 -1 1 1 60 0.6 290 0 0 0.012 -0.0036 -0.0036 0 0 0 840 -1.6946e-13 -1.84009e-13 0 0 0 5.04 5.04 0 0 -1 1 1 61 0.61 290 0 0 0.0122 -0.00366 -0.00366 0 0 0 854 -1.92768e-13 -1.63187e-13 0 0 0 5.2094 5.2094 0 0 -1 1 1 62 0.62 290 0 0 0.0124 -0.00372 -0.00372 0 0 0 868 -1.87655e-13 -1.99207e-13 0 0 0 5.3816 5.3816 0 0 -1 1 1 63 0.63 290 0 0 0.0126 -0.00378 -0.00378 0 0 0 882 -1.82542e-13 -1.78385e-13 0 0 0 5.5566 5.5566 0 0 -1 1 1 64 0.64 290 0 0 0.0128 -0.00384 -0.00384 0 0 0 896 -2.0585e-13 -2.14405e-13 0 0 0 5.7344 5.7344 0 0 -1 1 1 65 0.65 290 0 0 0.013 -0.0039 -0.0039 0 0 0 910 -2.00737e-13 -1.93583e-13 0 0 0 5.915 5.915 0 0 -1 1 1 66 0.66 290 0 0 0.0132 -0.00396 -0.00396 0 0 0 924 -1.49688e-13 -1.31894e-13 0 0 0 6.0984 6.0984 0 0 -1 1 1 67 0.67 290 0 0 0.0134 -0.00402 -0.00402 0 0 0 938 -1.44575e-13 -1.67915e-13 0 0 0 6.2846 6.2846 0 0 -1 1 1 68 0.68 290 0 0 0.0136 -0.00408 -0.00408 0 0 0 952 -1.39462e-13 -1.47092e-13 0 0 0 6.4736 6.4736 0 0 -1 1 1 69 0.69 290 0 0 0.0138 -0.00414 -0.00414 0 0 0 966 -1.34348e-13 -1.26269e-13 0 0 0 6.6654 6.6654 0 0 -1 1 1 70 0.7 290 0 0 0.014 -0.0042 -0.0042 0 0 0 980 -1.57657e-13 -1.6229e-13 0 0 0 6.86 6.86 0 0 -1 1 1 71 0.71 290 0 0 0.0142 -0.00426 -0.00426 0 0 0 994 -1.52544e-13 -1.41467e-13 0 0 0 7.0574 7.0574 0 0 -1 1 1 72 0.72 290 0 0 0.0144 -0.00432 -0.00432 0 0 0 1008 -1.4743e-13 -1.77488e-13 0 0 0 7.2576 7.2576 0 0 -1 1 1 73 0.73 290 0 0 0.0146 -0.00438 -0.00438 0 0 0 1022 -1.70739e-13 -1.56665e-13 0 0 0 7.4606 7.4606 0 0 -1 1 1 74 0.74 290 0 0 0.0148 -0.00444 -0.00444 0 0 0 1036 -1.65626e-13 -1.35842e-13 0 0 0 7.6664 7.6664 0 0 -1 1 1 75 0.75 290 0 0 0.015 -0.0045 -0.0045 0 0 0 1050 -1.60512e-13 -1.71863e-13 0 0 0 7.875 7.875 0 0 -1 1 1 76 0.76 290 0 0 0.0152 -0.00456 -0.00456 0 0 0 1064 -1.55399e-13 -1.5104e-13 0 0 0 8.0864 8.0864 0 0 -1 1 1 77 0.77 290 0 0 0.0154 -0.00462 -0.00462 0 0 0 1078 -1.78708e-13 -1.87061e-13 0 0 0 8.3006 8.3006 0 0 -1 1 1 78 0.78 290 0 0 0.0156 -0.00468 -0.00468 0 0 0 1092 -1.73594e-13 -1.66238e-13 0 0 0 8.5176 8.5176 0 0 -1 1 1 79 0.79 290 0 0 0.0158 -0.00474 -0.00474 0 0 0 1106 -1.68481e-13 -1.45416e-13 0 0 0 8.7374 8.7374 0 0 -1 1 1 80 0.8 290 0 0 0.016 -0.0048 -0.0048 0 0 0 1120 -2.77055e-13 -2.95123e-13 0 0 0 8.96 8.96 0 0 -1 1 1 81 0.81 290 0 0 0.0162 -0.00486 -0.00486 0 0 0 1134 -3.00363e-13 -2.74301e-13 0 0 0 9.1854 9.1854 0 0 -1 1 1 82 0.82 290 0 0 0.0164 -0.00492 -0.00492 0 0 0 1148 -4.08937e-13 -4.24008e-13 0 0 0 9.4136 9.4136 0 0 -1 1 1 83 0.83 290 0 0 0.0166 -0.00498 -0.00498 0 0 0 1162 -4.03824e-13 -4.03185e-13 0 0 0 9.6446 9.6446 0 0 -1 1 1 84 0.84 290 0 0 0.0168 -0.00504 -0.00504 0 0 0 1176 -5.12397e-13 -5.52893e-13 0 0 0 9.8784 9.8784 0 0 -1 1 1 85 0.85 290 0 0 0.017 -0.0051 -0.0051 0 0 0 1190 -6.49393e-13 -6.45757e-13 0 0 0 10.115 10.115 0 0 -1 1 1 86 0.86 290 0 0 0.0172 -0.00516 -0.00516 0 0 0 1204 -6.44279e-13 -6.24934e-13 0 0 0 10.3544 10.3544 0 0 -1 1 1 87 0.87 290 0 0 0.0174 -0.00522 -0.00522 0 0 0 1218 -7.52853e-13 -7.74642e-13 0 0 0 10.5966 10.5966 0 0 -1 1 1 88 0.88 290 0 0 0.0176 -0.00528 -0.00528 0 0 0 1232 -7.76161e-13 -7.53819e-13 0 0 0 10.8416 10.8416 0 0 -1 1 1 89 0.89 290 0 0 0.0178 -0.00534 -0.00534 0 0 0 1246 -8.84735e-13 -9.03527e-13 0 0 0 11.0894 11.0894 0 0 -1 1 1 90 0.9 290 0 0 0.018 -0.0054 -0.0054 0 0 0 1260 -9.93309e-13 -9.96391e-13 0 0 0 11.34 11.34 0 0 -1 1 1 91 0.91 290 0 0 0.0182 -0.00546 -0.00546 0 0 0 1274 -9.88195e-13 -9.75568e-13 0 0 0 11.5934 11.5934 0 0 -1 1 1 92 0.92 290 0 0 0.0184 -0.00552 -0.00552 0 0 0 1288 -1.12519e-12 -1.06843e-12 0 0 0 11.8496 11.8496 0 0 -1 1 1 93 0.93 290 0 0 0.0186 -0.00558 -0.00558 0 0 0 1302 -1.12008e-12 -1.1613e-12 0 0 0 12.1086 12.1086 0 0 -1 1 1 94 0.94 290 0 0 0.0188 -0.00564 -0.00564 0 0 0 1316 -1.22865e-12 -1.25416e-12 0 0 0 12.3704 12.3704 0 0 -1 1 1 95 0.95 290 0 0 0.019 -0.0057 -0.0057 0 0 0 1330 -1.33722e-12 -1.34702e-12 0 0 0 12.635 12.635 0 0 -1 1 1 96 0.96 290 0 0 0.0192 -0.00576 -0.00576 0 0 0 1344 -1.36053e-12 -1.3262e-12 0 0 0 12.9024 12.9024 0 0 -1 1 1 97 0.97 290 0 0 0.0194 -0.00582 -0.00582 0 0 0 1358 -1.46911e-12 -1.41907e-12 0 0 0 13.1726 13.1726 0 0 -1 1 1 98 0.98 290 0 0 0.0196 -0.00588 -0.00588 0 0 0 1372 -1.46399e-12 -1.51193e-12 0 0 0 13.4456 13.4456 0 0 -1 1 1 99 0.99 290 0 0 0.0198 -0.00594 -0.00594 0 0 0 1386 -1.57257e-12 -1.60479e-12 0 0 0 13.7214 13.7214 0 0 -1 1 1 100 1 290 0 0 0.02 -0.006 -0.006 0 0 0 1400 -1.70956e-12 -1.69766e-12 0 0 0 14 14 0 0 -1 1 2 1 1.01 290 0 0 0.0198 -0.00594 -0.00594 0 0 0 1386 -1.57257e-12 -1.60479e-12 0 0 0 13.7214 13.7214 0 0 -1 1 2 2 1.02 290 0 0 0.0196 -0.00588 -0.00588 0 0 0 1372 -1.46399e-12 -1.51193e-12 0 0 0 13.4456 13.4456 0 0 -1 1 2 3 1.03 290 0 0 0.0194 -0.00582 -0.00582 0 0 0 1358 -1.46911e-12 -1.41907e-12 0 0 0 13.1726 13.1726 0 0 -1 1 2 4 1.04 290 0 0 0.0192 -0.00576 -0.00576 0 0 0 1344 -1.36053e-12 -1.3262e-12 0 0 0 12.9024 12.9024 0 0 -1 1 2 5 1.05 290 0 0 0.019 -0.0057 -0.0057 0 0 0 1330 -1.33722e-12 -1.34702e-12 0 0 0 12.635 12.635 0 0 -1 1 2 6 1.06 290 0 0 0.0188 -0.00564 -0.00564 0 0 0 1316 -1.22865e-12 -1.25416e-12 0 0 0 12.3704 12.3704 0 0 -1 1 2 7 1.07 290 0 0 0.0186 -0.00558 -0.00558 0 0 0 1302 -1.12008e-12 -1.1613e-12 0 0 0 12.1086 12.1086 0 0 -1 1 2 8 1.08 290 0 0 0.0184 -0.00552 -0.00552 0 0 0 1288 -1.12519e-12 -1.06843e-12 0 0 0 11.8496 11.8496 0 0 -1 1 2 9 1.09 290 0 0 0.0182 -0.00546 -0.00546 0 0 0 1274 -9.88195e-13 -9.75568e-13 0 0 0 11.5934 11.5934 0 0 -1 1 2 10 1.1 290 0 0 0.018 -0.0054 -0.0054 0 0 0 1260 -9.93309e-13 -9.96391e-13 0 0 0 11.34 11.34 0 0 -1 1 2 11 1.11 290 0 0 0.0178 -0.00534 -0.00534 0 0 0 1246 -8.84735e-13 -9.03527e-13 0 0 0 11.0894 11.0894 0 0 -1 1 2 12 1.12 290 0 0 0.0176 -0.00528 -0.00528 0 0 0 1232 -7.76161e-13 -7.53819e-13 0 0 0 10.8416 10.8416 0 0 -1 1 2 13 1.13 290 0 0 0.0174 -0.00522 -0.00522 0 0 0 1218 -7.52853e-13 -7.74642e-13 0 0 0 10.5966 10.5966 0 0 -1 1 2 14 1.14 290 0 0 0.0172 -0.00516 -0.00516 0 0 0 1204 -6.44279e-13 -6.24934e-13 0 0 0 10.3544 10.3544 0 0 -1 1 2 15 1.15 290 0 0 0.017 -0.0051 -0.0051 0 0 0 1190 -6.49393e-13 -6.45757e-13 0 0 0 10.115 10.115 0 0 -1 1 2 16 1.16 290 0 0 0.0168 -0.00504 -0.00504 0 0 0 1176 -5.12397e-13 -5.52893e-13 0 0 0 9.8784 9.8784 0 0 -1 1 2 17 1.17 290 0 0 0.0166 -0.00498 -0.00498 0 0 0 1162 -4.03824e-13 -4.03185e-13 0 0 0 9.6446 9.6446 0 0 -1 1 2 18 1.18 290 0 0 0.0164 -0.00492 -0.00492 0 0 0 1148 -4.08937e-13 -4.24008e-13 0 0 0 9.4136 9.4136 0 0 -1 1 2 19 1.19 290 0 0 0.0162 -0.00486 -0.00486 0 0 0 1134 -3.00363e-13 -2.74301e-13 0 0 0 9.1854 9.1854 0 0 -1 1 2 20 1.2 290 0 0 0.016 -0.0048 -0.0048 0 0 0 1120 -2.77055e-13 -2.95123e-13 0 0 0 8.96 8.96 0 0 -1 1 2 21 1.21 290 0 0 0.0158 -0.00474 -0.00474 0 0 0 1106 -1.68481e-13 -1.45416e-13 0 0 0 8.7374 8.7374 0 0 -1 1 2 22 1.22 290 0 0 0.0156 -0.00468 -0.00468 0 0 0 1092 -1.73594e-13 -1.66238e-13 0 0 0 8.5176 8.5176 0 0 -1 1 2 23 1.23 290 0 0 0.0154 -0.00462 -0.00462 0 0 0 1078 -1.78708e-13 -1.87061e-13 0 0 0 8.3006 8.3006 0 0 -1 1 2 24 1.24 290 0 0 0.0152 -0.00456 -0.00456 0 0 0 1064 -1.55399e-13 -1.5104e-13 0 0 0 8.0864 8.0864 0 0 -1 1 2 25 1.25 290 0 0 0.015 -0.0045 -0.0045 0 0 0 1050 -1.60512e-13 -1.71863e-13 0 0 0 7.875 7.875 0 0 -1 1 2 26 1.26 290 0 0 0.0148 -0.00444 -0.00444 0 0 0 1036 -1.65626e-13 -1.35842e-13 0 0 0 7.6664 7.6664 0 0 -1 1 2 27 1.27 290 0 0 0.0146 -0.00438 -0.00438 0 0 0 1022 -1.70739e-13 -1.56665e-13 0 0 0 7.4606 7.4606 0 0 -1 1 2 28 1.28 290 0 0 0.0144 -0.00432 -0.00432 0 0 0 1008 -1.4743e-13 -1.77488e-13 0 0 0 7.2576 7.2576 0 0 -1 1 2 29 1.29 290 0 0 0.0142 -0.00426 -0.00426 0 0 0 994 -1.52544e-13 -1.41467e-13 0 0 0 7.0574 7.0574 0 0 -1 1 2 30 1.3 290 0 0 0.014 -0.0042 -0.0042 0 0 0 980 -1.57657e-13 -1.6229e-13 0 0 0 6.86 6.86 0 0 -1 1 2 31 1.31 290 0 0 0.0138 -0.00414 -0.00414 0 0 0 966 -1.34348e-13 -1.26269e-13 0 0 0 6.6654 6.6654 0 0 -1 1 2 32 1.32 290 0 0 0.0136 -0.00408 -0.00408 0 0 0 952 -1.39462e-13 -1.47092e-13 0 0 0 6.4736 6.4736 0 0 -1 1 2 33 1.33 290 0 0 0.0134 -0.00402 -0.00402 0 0 0 938 -1.44575e-13 -1.67915e-13 0 0 0 6.2846 6.2846 0 0 -1 1 2 34 1.34 290 0 0 0.0132 -0.00396 -0.00396 0 0 0 924 -1.49688e-13 -1.31894e-13 0 0 0 6.0984 6.0984 0 0 -1 1 2 35 1.35 290 0 0 0.013 -0.0039 -0.0039 0 0 0 910 -1.2638e-13 -1.52717e-13 0 0 0 5.915 5.915 0 0 -1 1 2 36 1.36 290 0 0 0.0128 -0.00384 -0.00384 0 0 0 896 -1.31493e-13 -1.16696e-13 0 0 0 5.7344 5.7344 0 0 -1 1 2 37 1.37 290 0 0 0.0126 -0.00378 -0.00378 0 0 0 882 -1.36606e-13 -1.37519e-13 0 0 0 5.5566 5.5566 0 0 -1 1 2 38 1.38 290 0 0 0.0124 -0.00372 -0.00372 0 0 0 868 -1.41719e-13 -1.01498e-13 0 0 0 5.3816 5.3816 0 0 -1 1 2 39 1.39 290 0 0 0.0122 -0.00366 -0.00366 0 0 0 854 -1.18411e-13 -1.22321e-13 0 0 0 5.2094 5.2094 0 0 -1 1 2 40 1.4 290 0 0 0.012 -0.0036 -0.0036 0 0 0 840 -1.23524e-13 -1.43143e-13 0 0 0 5.04 5.04 0 0 -1 1 2 41 1.41 290 0 0 0.0118 -0.00354 -0.00354 0 0 0 826 -1.85481e-13 -1.63966e-13 0 0 0 4.8734 4.8734 0 0 -1 1 2 42 1.42 290 0 0 0.0116 -0.00348 -0.00348 0 0 0 812 -1.90594e-13 -1.84789e-13 0 0 0 4.7096 4.7096 0 0 -1 1 2 43 1.43 290 0 0 0.0114 -0.00342 -0.00342 0 0 0 798 -1.67285e-13 -1.48768e-13 0 0 0 4.5486 4.5486 0 0 -1 1 2 44 1.44 290 0 0 0.0112 -0.00336 -0.00336 0 0 0 784 -1.72399e-13 -1.69591e-13 0 0 0 4.3904 4.3904 0 0 -1 1 2 45 1.45 290 0 0 0.011 -0.0033 -0.0033 0 0 0 770 -1.77512e-13 -1.90413e-13 0 0 0 4.235 4.235 0 0 -1 1 2 46 1.46 290 0 0 0.0108 -0.00324 -0.00324 0 0 0 756 -1.54203e-13 -1.54393e-13 0 0 0 4.0824 4.0824 0 0 -1 1 2 47 1.47 290 0 0 0.0106 -0.00318 -0.00318 0 0 0 742 -1.59317e-13 -1.75215e-13 0 0 0 3.9326 3.9326 0 0 -1 1 2 48 1.48 290 0 0 0.0104 -0.00312 -0.00312 0 0 0 728 -1.6443e-13 -1.39195e-13 0 0 0 3.7856 3.7856 0 0 -1 1 2 49 1.49 290 0 0 0.0102 -0.00306 -0.00306 0 0 0 714 -1.55332e-13 -1.60017e-13 0 0 0 3.6414 3.6414 0 0 -1 1 2 50 1.5 290 0 0 0.01 -0.003 -0.003 0 0 0 700 -1.60446e-13 -1.8084e-13 0 0 0 3.5 3.5 0 0 -1 1 2 51 1.51 290 0 0 0.0098 -0.00294 -0.00294 0 0 0 686 -1.51348e-13 -1.44819e-13 0 0 0 3.3614 3.3614 0 0 -1 1 2 52 1.52 290 0 0 0.0096 -0.00288 -0.00288 0 0 0 672 -1.56461e-13 -1.65642e-13 0 0 0 3.2256 3.2256 0 0 -1 1 2 53 1.53 290 0 0 0.0094 -0.00282 -0.00282 0 0 0 658 -1.47364e-13 -1.29621e-13 0 0 0 3.0926 3.0926 0 0 -1 1 2 54 1.54 290 0 0 0.0092 -0.00276 -0.00276 0 0 0 644 -1.52477e-13 -1.50444e-13 0 0 0 2.9624 2.9624 0 0 -1 1 2 55 1.55 290 0 0 0.009 -0.0027 -0.0027 0 0 0 630 -1.43379e-13 -1.42845e-13 0 0 0 2.835 2.835 0 0 -1 1 2 56 1.56 290 0 0 0.0088 -0.00264 -0.00264 0 0 0 616 -1.48492e-13 -1.35246e-13 0 0 0 2.7104 2.7104 0 0 -1 1 2 57 1.57 290 0 0 0.0086 -0.00258 -0.00258 0 0 0 602 -1.39395e-13 -1.27647e-13 0 0 0 2.5886 2.5886 0 0 -1 1 2 58 1.58 290 0 0 0.0084 -0.00252 -0.00252 0 0 0 588 -1.30297e-13 -1.4847e-13 0 0 0 2.4696 2.4696 0 0 -1 1 2 59 1.59 290 0 0 0.0082 -0.00246 -0.00246 0 0 0 574 -1.3541e-13 -1.40871e-13 0 0 0 2.3534 2.3534 0 0 -1 1 2 60 1.6 290 0 0 0.008 -0.0024 -0.0024 0 0 0 560 -1.26313e-13 -1.33272e-13 0 0 0 2.24 2.24 0 0 -1 1 2 61 1.61 290 0 0 0.0078 -0.00234 -0.00234 0 0 0 546 -1.31426e-13 -1.25673e-13 0 0 0 2.1294 2.1294 0 0 -1 1 2 62 1.62 290 0 0 0.0076 -0.00228 -0.00228 0 0 0 532 -6.5485e-14 -6.12304e-14 0 0 0 2.0216 2.0216 0 0 -1 1 2 63 1.63 290 0 0 0.0074 -0.00222 -0.00222 0 0 0 518 -1.37548e-14 3.21205e-15 0 0 0 1.9166 1.9166 0 0 -1 1 2 64 1.64 290 0 0 0.0072 -0.00216 -0.00216 0 0 0 504 -4.65718e-15 -1.76107e-14 0 0 0 1.8144 1.8144 0 0 -1 1 2 65 1.65 290 0 0 0.007 -0.0021 -0.0021 0 0 0 490 4.7073e-14 4.68318e-14 0 0 0 1.715 1.715 0 0 -1 1 2 66 1.66 290 0 0 0.0068 -0.00204 -0.00204 0 0 0 476 5.61706e-14 5.44308e-14 0 0 0 1.6184 1.6184 0 0 -1 1 2 67 1.67 290 0 0 0.0066 -0.00198 -0.00198 0 0 0 462 1.07901e-13 1.18873e-13 0 0 0 1.5246 1.5246 0 0 -1 1 2 68 1.68 290 0 0 0.0064 -0.00192 -0.00192 0 0 0 448 1.73842e-13 1.83316e-13 0 0 0 1.4336 1.4336 0 0 -1 1 2 69 1.69 290 0 0 0.0062 -0.00186 -0.00186 0 0 0 434 1.68729e-13 1.62493e-13 0 0 0 1.3454 1.3454 0 0 -1 1 2 70 1.7 290 0 0 0.006 -0.0018 -0.0018 0 0 0 420 2.06248e-13 1.98514e-13 0 0 0 1.26 1.26 0 0 -1 1 2 71 1.71 290 0 0 0.0058 -0.00174 -0.00174 0 0 0 406 2.72189e-13 2.62956e-13 0 0 0 1.1774 1.1774 0 0 -1 1 2 72 1.72 290 0 0 0.0056 -0.00168 -0.00168 0 0 0 392 2.95497e-13 2.98977e-13 0 0 0 1.0976 1.0976 0 0 -1 1 2 73 1.73 290 0 0 0.0054 -0.00162 -0.00162 0 0 0 378 3.33017e-13 3.34997e-13 0 0 0 1.0206 1.0206 0 0 -1 1 2 74 1.74 290 0 0 0.0052 -0.00156 -0.00156 0 0 0 364 3.56325e-13 3.71018e-13 0 0 0 0.9464 0.9464 0 0 -1 1 2 75 1.75 290 0 0 0.005 -0.0015 -0.0015 0 0 0 350 3.86739e-13 3.78617e-13 0 0 0 0.875 0.875 0 0 -1 1 2 76 1.76 290 0 0 0.0048 -0.00144 -0.00144 0 0 0 336 4.17153e-13 4.14638e-13 0 0 0 0.8064 0.8064 0 0 -1 1 2 77 1.77 290 0 0 0.0046 -0.00138 -0.00138 0 0 0 322 4.75989e-13 4.7908e-13 0 0 0 0.7406 0.7406 0 0 -1 1 2 78 1.78 290 0 0 0.0044 -0.00132 -0.00132 0 0 0 308 5.06403e-13 5.15101e-13 0 0 0 0.6776 0.6776 0 0 -1 1 2 79 1.79 290 0 0 0.0042 -0.00126 -0.00126 0 0 0 294 5.43922e-13 5.36911e-13 0 0 0 0.6174 0.6174 0 0 -1 1 2 80 1.8 290 0 0 0.004 -0.0012 -0.0012 0 0 0 280 5.74336e-13 5.72932e-13 0 0 0 0.56 0.56 0 0 -1 1 2 81 1.81 290 0 0 0.0038 -0.00114 -0.00114 0 0 0 266 6.0475e-13 6.08952e-13 0 0 0 0.5054 0.5054 0 0 -1 1 2 82 1.82 290 0 0 0.0036 -0.00108 -0.00108 0 0 0 252 6.06742e-13 6.0234e-13 0 0 0 0.4536 0.4536 0 0 -1 1 2 83 1.83 290 0 0 0.0034 -0.00102 -0.00102 0 0 0 238 6.37156e-13 6.38361e-13 0 0 0 0.4046 0.4046 0 0 -1 1 2 84 1.84 290 0 0 0.0032 -0.00096 -0.00096 0 0 0 224 6.20559e-13 6.21533e-13 0 0 0 0.3584 0.3584 0 0 -1 1 2 85 1.85 290 0 0 0.003 -0.0009 -0.0009 0 0 0 210 6.39489e-13 6.47337e-13 0 0 0 0.315 0.315 0 0 -1 1 2 86 1.86 290 0 0 0.0028 -0.00084 -0.00084 0 0 0 196 6.44208e-13 6.44719e-13 0 0 0 0.2744 0.2744 0 0 -1 1 2 87 1.87 290 0 0 0.0026 -0.00078 -0.00078 0 0 0 182 6.45374e-13 6.42102e-13 0 0 0 0.2366 0.2366 0 0 -1 1 2 88 1.88 290 0 0 0.0024 -0.00072 -0.00072 0 0 0 168 6.4654e-13 6.39484e-13 0 0 0 0.2016 0.2016 0 0 -1 1 2 89 1.89 290 0 0 0.0022 -0.00066 -0.00066 0 0 0 154 6.47707e-13 6.51078e-13 0 0 0 0.1694 0.1694 0 0 -1 1 2 90 1.9 290 0 0 0.002 -0.0006 -0.0006 0 0 0 140 6.52426e-13 6.4846e-13 0 0 0 0.14 0.14 0 0 -1 1 2 91 1.91 290 0 0 0.0018 -0.00054 -0.00054 0 0 0 126 6.67803e-13 6.67159e-13 0 0 0 0.1134 0.1134 0 0 -1 1 2 92 1.92 290 0 0 0.0016 -0.00048 -0.00048 0 0 0 112 6.74712e-13 6.76755e-13 0 0 0 0.0896 0.0896 0 0 -1 1 2 93 1.93 290 0 0 0.0014 -0.00042 -0.00042 0 0 0 98 6.99384e-13 7.00562e-13 0 0 0 0.0686 0.0686 0 0 -1 1 2 94 1.94 290 0 0 0.0012 -0.00036 -0.00036 0 0 0 84 7.24055e-13 7.24369e-13 0 0 0 0.0504 0.0504 0 0 -1 1 2 95 1.95 290 0 0 0.001 -0.0003 -0.0003 0 0 0 70 7.39846e-13 7.41071e-13 0 0 0 0.035 0.035 0 0 -1 1 2 96 1.96 290 0 0 0.0008 -0.00024 -0.00024 0 0 0 56 7.63423e-13 7.62324e-13 0 0 0 0.0224 0.0224 0 0 -1 1 2 97 1.97 290 0 0 0.0006 -0.00018 -0.00018 0 0 0 42 7.78118e-13 7.76471e-13 0 0 0 0.0126 0.0126 0 0 -1 1 2 98 1.98 290 0 0 0.0004 -0.00012 -0.00012 0 0 0 28 7.93702e-13 7.88842e-13 0 0 0 0.0056 0.0056 0 0 -1 1 2 99 1.99 290 0 0 0.0002 -6e-05 -6e-05 0 0 0 14 8.10448e-13 8.05405e-13 0 0 0 0.0014 0.0014 0 0 -1 1 2 100 2 290 0 0 2.00577e-18 5.57009e-18 5.46167e-18 0 0 0 6.34519e-13 8.26444e-13 8.20606e-13 0 0 0 9.68409e-15 9.68409e-15 0 0 diff --git a/testBin/Umats/UMEXT/results/results_job_local-0.txt b/testBin/Umats/UMEXT/results/results_job_local-0.txt deleted file mode 100755 index 3547bc6ef..000000000 --- a/testBin/Umats/UMEXT/results/results_job_local-0.txt +++ /dev/null @@ -1,200 +0,0 @@ -1 1 1 1 0.01 290 0 0 0.0002 -6e-05 -6e-05 0 0 0 14 2.37903e-16 1.00924e-15 0 0 0 0.0014 0.0014 0 0 -1 1 1 2 0.02 290 0 0 0.0004 -0.00012 -0.00012 0 0 0 28 4.75806e-16 2.01848e-15 0 0 0 0.0056 0.0056 0 0 -1 1 1 3 0.03 290 0 0 0.0006 -0.00018 -0.00018 0 0 0 42 3.48147e-15 6.96932e-15 0 0 0 0.0126 0.0126 0 0 -1 1 1 4 0.04 290 0 0 0.0008 -0.00024 -0.00024 0 0 0 56 9.51613e-16 4.03696e-15 0 0 0 0.0224 0.0224 0 0 -1 1 1 5 0.05 290 0 0 0.001 -0.0003 -0.0003 0 0 0 70 7.30354e-15 8.21002e-15 0 0 0 0.035 0.035 0 0 -1 1 1 6 0.06 290 0 0 0.0012 -0.00036 -0.00036 0 0 0 84 4.77368e-15 8.83037e-15 0 0 0 0.0504 0.0504 0 0 -1 1 1 7 0.07 290 0 0 0.0014 -0.00042 -0.00042 0 0 0 98 9.34926e-15 1.65562e-14 0 0 0 0.0686 0.0686 0 0 -1 1 1 8 0.08 290 0 0 0.0016 -0.00048 -0.00048 0 0 0 112 1.39248e-14 1.71765e-14 0 0 0 0.0896 0.0896 0 0 -1 1 1 9 0.09 290 0 0 0.0018 -0.00054 -0.00054 0 0 0 126 2.56058e-14 2.49023e-14 0 0 0 0.1134 0.1134 0 0 -1 1 1 10 0.1 290 0 0 0.002 -0.0006 -0.0006 0 0 0 140 3.94761e-14 3.77363e-14 0 0 0 0.14 0.14 0 0 -1 1 1 11 0.11 290 0 0 0.0022 -0.00066 -0.00066 0 0 0 154 4.97936e-14 5.05704e-14 0 0 0 0.1694 0.1694 0 0 -1 1 1 12 0.12 290 0 0 0.0024 -0.00072 -0.00072 0 0 0 168 6.36639e-14 5.6299e-14 0 0 0 0.2016 0.2016 0 0 -1 1 1 13 0.13 290 0 0 0.0026 -0.00078 -0.00078 0 0 0 182 5.97706e-14 5.49222e-14 0 0 0 0.2366 0.2366 0 0 -1 1 1 14 0.14 290 0 0 0.0028 -0.00084 -0.00084 0 0 0 196 7.71935e-14 6.77562e-14 0 0 0 0.2744 0.2744 0 0 -1 1 1 15 0.15 290 0 0 0.003 -0.0009 -0.0009 0 0 0 210 8.39583e-14 8.05902e-14 0 0 0 0.315 0.315 0 0 -1 1 1 16 0.16 290 0 0 0.0032 -0.00096 -0.00096 0 0 0 224 1.12039e-13 1.07635e-13 0 0 0 0.3584 0.3584 0 0 -1 1 1 17 0.17 290 0 0 0.0034 -0.00102 -0.00102 0 0 0 238 1.11699e-13 1.06258e-13 0 0 0 0.4046 0.4046 0 0 -1 1 1 18 0.18 290 0 0 0.0036 -0.00108 -0.00108 0 0 0 252 1.11358e-13 1.04882e-13 0 0 0 0.4536 0.4536 0 0 -1 1 1 19 0.19 290 0 0 0.0038 -0.00114 -0.00114 0 0 0 266 1.39439e-13 1.31926e-13 0 0 0 0.5054 0.5054 0 0 -1 1 1 20 0.2 290 0 0 0.004 -0.0012 -0.0012 0 0 0 280 1.31993e-13 1.3055e-13 0 0 0 0.56 0.56 0 0 -1 1 1 21 0.21 290 0 0 0.0042 -0.00126 -0.00126 0 0 0 294 1.31653e-13 1.29173e-13 0 0 0 0.6174 0.6174 0 0 -1 1 1 22 0.22 290 0 0 0.0044 -0.00132 -0.00132 0 0 0 308 1.0289e-13 9.93743e-14 0 0 0 0.6776 0.6776 0 0 -1 1 1 23 0.23 290 0 0 0.0046 -0.00138 -0.00138 0 0 0 322 1.0255e-13 1.12208e-13 0 0 0 0.7406 0.7406 0 0 -1 1 1 24 0.24 290 0 0 0.0048 -0.00144 -0.00144 0 0 0 336 1.02209e-13 9.66206e-14 0 0 0 0.8064 0.8064 0 0 -1 1 1 25 0.25 290 0 0 0.005 -0.0015 -0.0015 0 0 0 350 9.47632e-14 1.09455e-13 0 0 0 0.875 0.875 0 0 -1 1 1 26 0.26 290 0 0 0.0052 -0.00156 -0.00156 0 0 0 364 9.44226e-14 9.3867e-14 0 0 0 0.9464 0.9464 0 0 -1 1 1 27 0.27 290 0 0 0.0054 -0.00162 -0.00162 0 0 0 378 1.01187e-13 1.06701e-13 0 0 0 1.0206 1.0206 0 0 -1 1 1 28 0.28 290 0 0 0.0056 -0.00168 -0.00168 0 0 0 392 5.82142e-14 6.26917e-14 0 0 0 1.0976 1.0976 0 0 -1 1 1 29 0.29 290 0 0 0.0058 -0.00174 -0.00174 0 0 0 406 5.78736e-14 7.55257e-14 0 0 0 1.1774 1.1774 0 0 -1 1 1 30 0.3 290 0 0 0.006 -0.0018 -0.0018 0 0 0 420 5.7533e-14 5.9938e-14 0 0 0 1.26 1.26 0 0 -1 1 1 31 0.31 290 0 0 0.0062 -0.00186 -0.00186 0 0 0 434 5.71924e-14 7.27721e-14 0 0 0 1.3454 1.3454 0 0 -1 1 1 32 0.32 290 0 0 0.0064 -0.00192 -0.00192 0 0 0 448 5.68518e-14 5.71844e-14 0 0 0 1.4336 1.4336 0 0 -1 1 1 33 0.33 290 0 0 0.0066 -0.00198 -0.00198 0 0 0 462 -9.08921e-15 -7.25802e-15 0 0 0 1.5246 1.5246 0 0 -1 1 1 34 0.34 290 0 0 0.0068 -0.00204 -0.00204 0 0 0 476 -3.97598e-15 -1.4857e-14 0 0 0 1.6184 1.6184 0 0 -1 1 1 35 0.35 290 0 0 0.007 -0.0021 -0.0021 0 0 0 490 -6.9917e-14 -5.08777e-14 0 0 0 1.715 1.715 0 0 -1 1 1 36 0.36 290 0 0 0.0072 -0.00216 -0.00216 0 0 0 504 -6.48038e-14 -5.84767e-14 0 0 0 1.8144 1.8144 0 0 -1 1 1 37 0.37 290 0 0 0.0074 -0.00222 -0.00222 0 0 0 518 -1.30745e-13 -1.22919e-13 0 0 0 1.9166 1.9166 0 0 -1 1 1 38 0.38 290 0 0 0.0076 -0.00228 -0.00228 0 0 0 532 -1.82475e-13 -1.87362e-13 0 0 0 2.0216 2.0216 0 0 -1 1 1 39 0.39 290 0 0 0.0078 -0.00234 -0.00234 0 0 0 546 -1.91573e-13 -1.94961e-13 0 0 0 2.1294 2.1294 0 0 -1 1 1 40 0.4 290 0 0 0.008 -0.0024 -0.0024 0 0 0 560 -1.86459e-13 -2.0256e-13 0 0 0 2.24 2.24 0 0 -1 1 1 41 0.41 290 0 0 0.0082 -0.00246 -0.00246 0 0 0 574 -1.95557e-13 -1.81737e-13 0 0 0 2.3534 2.3534 0 0 -1 1 1 42 0.42 290 0 0 0.0084 -0.00252 -0.00252 0 0 0 588 -1.90444e-13 -1.89336e-13 0 0 0 2.4696 2.4696 0 0 -1 1 1 43 0.43 290 0 0 0.0086 -0.00258 -0.00258 0 0 0 602 -1.99541e-13 -1.96935e-13 0 0 0 2.5886 2.5886 0 0 -1 1 1 44 0.44 290 0 0 0.0088 -0.00264 -0.00264 0 0 0 616 -1.94428e-13 -2.04534e-13 0 0 0 2.7104 2.7104 0 0 -1 1 1 45 0.45 290 0 0 0.009 -0.0027 -0.0027 0 0 0 630 -2.03526e-13 -2.12133e-13 0 0 0 2.835 2.835 0 0 -1 1 1 46 0.46 290 0 0 0.0092 -0.00276 -0.00276 0 0 0 644 -1.98413e-13 -1.9131e-13 0 0 0 2.9624 2.9624 0 0 -1 1 1 47 0.47 290 0 0 0.0094 -0.00282 -0.00282 0 0 0 658 -2.0751e-13 -2.27331e-13 0 0 0 3.0926 3.0926 0 0 -1 1 1 48 0.48 290 0 0 0.0096 -0.00288 -0.00288 0 0 0 672 -2.16608e-13 -2.06508e-13 0 0 0 3.2256 3.2256 0 0 -1 1 1 49 0.49 290 0 0 0.0098 -0.00294 -0.00294 0 0 0 686 -2.11495e-13 -1.85685e-13 0 0 0 3.3614 3.3614 0 0 -1 1 1 50 0.5 290 0 0 0.01 -0.003 -0.003 0 0 0 700 -2.20592e-13 -2.21706e-13 0 0 0 3.5 3.5 0 0 -1 1 1 51 0.51 290 0 0 0.0102 -0.00306 -0.00306 0 0 0 714 -2.15479e-13 -2.00883e-13 0 0 0 3.6414 3.6414 0 0 -1 1 1 52 0.52 290 0 0 0.0104 -0.00312 -0.00312 0 0 0 728 -2.24577e-13 -2.36904e-13 0 0 0 3.7856 3.7856 0 0 -1 1 1 53 0.53 290 0 0 0.0106 -0.00318 -0.00318 0 0 0 742 -2.33674e-13 -2.16081e-13 0 0 0 3.9326 3.9326 0 0 -1 1 1 54 0.54 290 0 0 0.0108 -0.00324 -0.00324 0 0 0 756 -2.28561e-13 -2.52102e-13 0 0 0 4.0824 4.0824 0 0 -1 1 1 55 0.55 290 0 0 0.011 -0.0033 -0.0033 0 0 0 770 -2.23448e-13 -2.31279e-13 0 0 0 4.235 4.235 0 0 -1 1 1 56 0.56 290 0 0 0.0112 -0.00336 -0.00336 0 0 0 784 -2.18334e-13 -2.10457e-13 0 0 0 4.3904 4.3904 0 0 -1 1 1 57 0.57 290 0 0 0.0114 -0.00342 -0.00342 0 0 0 798 -2.41643e-13 -2.46477e-13 0 0 0 4.5486 4.5486 0 0 -1 1 1 58 0.58 290 0 0 0.0116 -0.00348 -0.00348 0 0 0 812 -2.3653e-13 -2.25655e-13 0 0 0 4.7096 4.7096 0 0 -1 1 1 59 0.59 290 0 0 0.0118 -0.00354 -0.00354 0 0 0 826 -2.31416e-13 -2.61675e-13 0 0 0 4.8734 4.8734 0 0 -1 1 1 60 0.6 290 0 0 0.012 -0.0036 -0.0036 0 0 0 840 -1.6946e-13 -1.84009e-13 0 0 0 5.04 5.04 0 0 -1 1 1 61 0.61 290 0 0 0.0122 -0.00366 -0.00366 0 0 0 854 -1.92768e-13 -1.63187e-13 0 0 0 5.2094 5.2094 0 0 -1 1 1 62 0.62 290 0 0 0.0124 -0.00372 -0.00372 0 0 0 868 -1.87655e-13 -1.99207e-13 0 0 0 5.3816 5.3816 0 0 -1 1 1 63 0.63 290 0 0 0.0126 -0.00378 -0.00378 0 0 0 882 -1.82542e-13 -1.78385e-13 0 0 0 5.5566 5.5566 0 0 -1 1 1 64 0.64 290 0 0 0.0128 -0.00384 -0.00384 0 0 0 896 -2.0585e-13 -2.14405e-13 0 0 0 5.7344 5.7344 0 0 -1 1 1 65 0.65 290 0 0 0.013 -0.0039 -0.0039 0 0 0 910 -2.00737e-13 -1.93583e-13 0 0 0 5.915 5.915 0 0 -1 1 1 66 0.66 290 0 0 0.0132 -0.00396 -0.00396 0 0 0 924 -1.49688e-13 -1.31894e-13 0 0 0 6.0984 6.0984 0 0 -1 1 1 67 0.67 290 0 0 0.0134 -0.00402 -0.00402 0 0 0 938 -1.44575e-13 -1.67915e-13 0 0 0 6.2846 6.2846 0 0 -1 1 1 68 0.68 290 0 0 0.0136 -0.00408 -0.00408 0 0 0 952 -1.39462e-13 -1.47092e-13 0 0 0 6.4736 6.4736 0 0 -1 1 1 69 0.69 290 0 0 0.0138 -0.00414 -0.00414 0 0 0 966 -1.34348e-13 -1.26269e-13 0 0 0 6.6654 6.6654 0 0 -1 1 1 70 0.7 290 0 0 0.014 -0.0042 -0.0042 0 0 0 980 -1.57657e-13 -1.6229e-13 0 0 0 6.86 6.86 0 0 -1 1 1 71 0.71 290 0 0 0.0142 -0.00426 -0.00426 0 0 0 994 -1.52544e-13 -1.41467e-13 0 0 0 7.0574 7.0574 0 0 -1 1 1 72 0.72 290 0 0 0.0144 -0.00432 -0.00432 0 0 0 1008 -1.4743e-13 -1.77488e-13 0 0 0 7.2576 7.2576 0 0 -1 1 1 73 0.73 290 0 0 0.0146 -0.00438 -0.00438 0 0 0 1022 -1.70739e-13 -1.56665e-13 0 0 0 7.4606 7.4606 0 0 -1 1 1 74 0.74 290 0 0 0.0148 -0.00444 -0.00444 0 0 0 1036 -1.65626e-13 -1.35842e-13 0 0 0 7.6664 7.6664 0 0 -1 1 1 75 0.75 290 0 0 0.015 -0.0045 -0.0045 0 0 0 1050 -1.60512e-13 -1.71863e-13 0 0 0 7.875 7.875 0 0 -1 1 1 76 0.76 290 0 0 0.0152 -0.00456 -0.00456 0 0 0 1064 -1.55399e-13 -1.5104e-13 0 0 0 8.0864 8.0864 0 0 -1 1 1 77 0.77 290 0 0 0.0154 -0.00462 -0.00462 0 0 0 1078 -1.78708e-13 -1.87061e-13 0 0 0 8.3006 8.3006 0 0 -1 1 1 78 0.78 290 0 0 0.0156 -0.00468 -0.00468 0 0 0 1092 -1.73594e-13 -1.66238e-13 0 0 0 8.5176 8.5176 0 0 -1 1 1 79 0.79 290 0 0 0.0158 -0.00474 -0.00474 0 0 0 1106 -1.68481e-13 -1.45416e-13 0 0 0 8.7374 8.7374 0 0 -1 1 1 80 0.8 290 0 0 0.016 -0.0048 -0.0048 0 0 0 1120 -2.77055e-13 -2.95123e-13 0 0 0 8.96 8.96 0 0 -1 1 1 81 0.81 290 0 0 0.0162 -0.00486 -0.00486 0 0 0 1134 -3.00363e-13 -2.74301e-13 0 0 0 9.1854 9.1854 0 0 -1 1 1 82 0.82 290 0 0 0.0164 -0.00492 -0.00492 0 0 0 1148 -4.08937e-13 -4.24008e-13 0 0 0 9.4136 9.4136 0 0 -1 1 1 83 0.83 290 0 0 0.0166 -0.00498 -0.00498 0 0 0 1162 -4.03824e-13 -4.03185e-13 0 0 0 9.6446 9.6446 0 0 -1 1 1 84 0.84 290 0 0 0.0168 -0.00504 -0.00504 0 0 0 1176 -5.12397e-13 -5.52893e-13 0 0 0 9.8784 9.8784 0 0 -1 1 1 85 0.85 290 0 0 0.017 -0.0051 -0.0051 0 0 0 1190 -6.49393e-13 -6.45757e-13 0 0 0 10.115 10.115 0 0 -1 1 1 86 0.86 290 0 0 0.0172 -0.00516 -0.00516 0 0 0 1204 -6.44279e-13 -6.24934e-13 0 0 0 10.3544 10.3544 0 0 -1 1 1 87 0.87 290 0 0 0.0174 -0.00522 -0.00522 0 0 0 1218 -7.52853e-13 -7.74642e-13 0 0 0 10.5966 10.5966 0 0 -1 1 1 88 0.88 290 0 0 0.0176 -0.00528 -0.00528 0 0 0 1232 -7.76161e-13 -7.53819e-13 0 0 0 10.8416 10.8416 0 0 -1 1 1 89 0.89 290 0 0 0.0178 -0.00534 -0.00534 0 0 0 1246 -8.84735e-13 -9.03527e-13 0 0 0 11.0894 11.0894 0 0 -1 1 1 90 0.9 290 0 0 0.018 -0.0054 -0.0054 0 0 0 1260 -9.93309e-13 -9.96391e-13 0 0 0 11.34 11.34 0 0 -1 1 1 91 0.91 290 0 0 0.0182 -0.00546 -0.00546 0 0 0 1274 -9.88195e-13 -9.75568e-13 0 0 0 11.5934 11.5934 0 0 -1 1 1 92 0.92 290 0 0 0.0184 -0.00552 -0.00552 0 0 0 1288 -1.12519e-12 -1.06843e-12 0 0 0 11.8496 11.8496 0 0 -1 1 1 93 0.93 290 0 0 0.0186 -0.00558 -0.00558 0 0 0 1302 -1.12008e-12 -1.1613e-12 0 0 0 12.1086 12.1086 0 0 -1 1 1 94 0.94 290 0 0 0.0188 -0.00564 -0.00564 0 0 0 1316 -1.22865e-12 -1.25416e-12 0 0 0 12.3704 12.3704 0 0 -1 1 1 95 0.95 290 0 0 0.019 -0.0057 -0.0057 0 0 0 1330 -1.33722e-12 -1.34702e-12 0 0 0 12.635 12.635 0 0 -1 1 1 96 0.96 290 0 0 0.0192 -0.00576 -0.00576 0 0 0 1344 -1.36053e-12 -1.3262e-12 0 0 0 12.9024 12.9024 0 0 -1 1 1 97 0.97 290 0 0 0.0194 -0.00582 -0.00582 0 0 0 1358 -1.46911e-12 -1.41907e-12 0 0 0 13.1726 13.1726 0 0 -1 1 1 98 0.98 290 0 0 0.0196 -0.00588 -0.00588 0 0 0 1372 -1.46399e-12 -1.51193e-12 0 0 0 13.4456 13.4456 0 0 -1 1 1 99 0.99 290 0 0 0.0198 -0.00594 -0.00594 0 0 0 1386 -1.57257e-12 -1.60479e-12 0 0 0 13.7214 13.7214 0 0 -1 1 1 100 1 290 0 0 0.02 -0.006 -0.006 0 0 0 1400 -1.70956e-12 -1.69766e-12 0 0 0 14 14 0 0 -1 1 2 1 1.01 290 0 0 0.0198 -0.00594 -0.00594 0 0 0 1386 -1.57257e-12 -1.60479e-12 0 0 0 13.7214 13.7214 0 0 -1 1 2 2 1.02 290 0 0 0.0196 -0.00588 -0.00588 0 0 0 1372 -1.46399e-12 -1.51193e-12 0 0 0 13.4456 13.4456 0 0 -1 1 2 3 1.03 290 0 0 0.0194 -0.00582 -0.00582 0 0 0 1358 -1.46911e-12 -1.41907e-12 0 0 0 13.1726 13.1726 0 0 -1 1 2 4 1.04 290 0 0 0.0192 -0.00576 -0.00576 0 0 0 1344 -1.36053e-12 -1.3262e-12 0 0 0 12.9024 12.9024 0 0 -1 1 2 5 1.05 290 0 0 0.019 -0.0057 -0.0057 0 0 0 1330 -1.33722e-12 -1.34702e-12 0 0 0 12.635 12.635 0 0 -1 1 2 6 1.06 290 0 0 0.0188 -0.00564 -0.00564 0 0 0 1316 -1.22865e-12 -1.25416e-12 0 0 0 12.3704 12.3704 0 0 -1 1 2 7 1.07 290 0 0 0.0186 -0.00558 -0.00558 0 0 0 1302 -1.12008e-12 -1.1613e-12 0 0 0 12.1086 12.1086 0 0 -1 1 2 8 1.08 290 0 0 0.0184 -0.00552 -0.00552 0 0 0 1288 -1.12519e-12 -1.06843e-12 0 0 0 11.8496 11.8496 0 0 -1 1 2 9 1.09 290 0 0 0.0182 -0.00546 -0.00546 0 0 0 1274 -9.88195e-13 -9.75568e-13 0 0 0 11.5934 11.5934 0 0 -1 1 2 10 1.1 290 0 0 0.018 -0.0054 -0.0054 0 0 0 1260 -9.93309e-13 -9.96391e-13 0 0 0 11.34 11.34 0 0 -1 1 2 11 1.11 290 0 0 0.0178 -0.00534 -0.00534 0 0 0 1246 -8.84735e-13 -9.03527e-13 0 0 0 11.0894 11.0894 0 0 -1 1 2 12 1.12 290 0 0 0.0176 -0.00528 -0.00528 0 0 0 1232 -7.76161e-13 -7.53819e-13 0 0 0 10.8416 10.8416 0 0 -1 1 2 13 1.13 290 0 0 0.0174 -0.00522 -0.00522 0 0 0 1218 -7.52853e-13 -7.74642e-13 0 0 0 10.5966 10.5966 0 0 -1 1 2 14 1.14 290 0 0 0.0172 -0.00516 -0.00516 0 0 0 1204 -6.44279e-13 -6.24934e-13 0 0 0 10.3544 10.3544 0 0 -1 1 2 15 1.15 290 0 0 0.017 -0.0051 -0.0051 0 0 0 1190 -6.49393e-13 -6.45757e-13 0 0 0 10.115 10.115 0 0 -1 1 2 16 1.16 290 0 0 0.0168 -0.00504 -0.00504 0 0 0 1176 -5.12397e-13 -5.52893e-13 0 0 0 9.8784 9.8784 0 0 -1 1 2 17 1.17 290 0 0 0.0166 -0.00498 -0.00498 0 0 0 1162 -4.03824e-13 -4.03185e-13 0 0 0 9.6446 9.6446 0 0 -1 1 2 18 1.18 290 0 0 0.0164 -0.00492 -0.00492 0 0 0 1148 -4.08937e-13 -4.24008e-13 0 0 0 9.4136 9.4136 0 0 -1 1 2 19 1.19 290 0 0 0.0162 -0.00486 -0.00486 0 0 0 1134 -3.00363e-13 -2.74301e-13 0 0 0 9.1854 9.1854 0 0 -1 1 2 20 1.2 290 0 0 0.016 -0.0048 -0.0048 0 0 0 1120 -2.77055e-13 -2.95123e-13 0 0 0 8.96 8.96 0 0 -1 1 2 21 1.21 290 0 0 0.0158 -0.00474 -0.00474 0 0 0 1106 -1.68481e-13 -1.45416e-13 0 0 0 8.7374 8.7374 0 0 -1 1 2 22 1.22 290 0 0 0.0156 -0.00468 -0.00468 0 0 0 1092 -1.73594e-13 -1.66238e-13 0 0 0 8.5176 8.5176 0 0 -1 1 2 23 1.23 290 0 0 0.0154 -0.00462 -0.00462 0 0 0 1078 -1.78708e-13 -1.87061e-13 0 0 0 8.3006 8.3006 0 0 -1 1 2 24 1.24 290 0 0 0.0152 -0.00456 -0.00456 0 0 0 1064 -1.55399e-13 -1.5104e-13 0 0 0 8.0864 8.0864 0 0 -1 1 2 25 1.25 290 0 0 0.015 -0.0045 -0.0045 0 0 0 1050 -1.60512e-13 -1.71863e-13 0 0 0 7.875 7.875 0 0 -1 1 2 26 1.26 290 0 0 0.0148 -0.00444 -0.00444 0 0 0 1036 -1.65626e-13 -1.35842e-13 0 0 0 7.6664 7.6664 0 0 -1 1 2 27 1.27 290 0 0 0.0146 -0.00438 -0.00438 0 0 0 1022 -1.70739e-13 -1.56665e-13 0 0 0 7.4606 7.4606 0 0 -1 1 2 28 1.28 290 0 0 0.0144 -0.00432 -0.00432 0 0 0 1008 -1.4743e-13 -1.77488e-13 0 0 0 7.2576 7.2576 0 0 -1 1 2 29 1.29 290 0 0 0.0142 -0.00426 -0.00426 0 0 0 994 -1.52544e-13 -1.41467e-13 0 0 0 7.0574 7.0574 0 0 -1 1 2 30 1.3 290 0 0 0.014 -0.0042 -0.0042 0 0 0 980 -1.57657e-13 -1.6229e-13 0 0 0 6.86 6.86 0 0 -1 1 2 31 1.31 290 0 0 0.0138 -0.00414 -0.00414 0 0 0 966 -1.34348e-13 -1.26269e-13 0 0 0 6.6654 6.6654 0 0 -1 1 2 32 1.32 290 0 0 0.0136 -0.00408 -0.00408 0 0 0 952 -1.39462e-13 -1.47092e-13 0 0 0 6.4736 6.4736 0 0 -1 1 2 33 1.33 290 0 0 0.0134 -0.00402 -0.00402 0 0 0 938 -1.44575e-13 -1.67915e-13 0 0 0 6.2846 6.2846 0 0 -1 1 2 34 1.34 290 0 0 0.0132 -0.00396 -0.00396 0 0 0 924 -1.49688e-13 -1.31894e-13 0 0 0 6.0984 6.0984 0 0 -1 1 2 35 1.35 290 0 0 0.013 -0.0039 -0.0039 0 0 0 910 -1.2638e-13 -1.52717e-13 0 0 0 5.915 5.915 0 0 -1 1 2 36 1.36 290 0 0 0.0128 -0.00384 -0.00384 0 0 0 896 -1.31493e-13 -1.16696e-13 0 0 0 5.7344 5.7344 0 0 -1 1 2 37 1.37 290 0 0 0.0126 -0.00378 -0.00378 0 0 0 882 -1.36606e-13 -1.37519e-13 0 0 0 5.5566 5.5566 0 0 -1 1 2 38 1.38 290 0 0 0.0124 -0.00372 -0.00372 0 0 0 868 -1.41719e-13 -1.01498e-13 0 0 0 5.3816 5.3816 0 0 -1 1 2 39 1.39 290 0 0 0.0122 -0.00366 -0.00366 0 0 0 854 -1.18411e-13 -1.22321e-13 0 0 0 5.2094 5.2094 0 0 -1 1 2 40 1.4 290 0 0 0.012 -0.0036 -0.0036 0 0 0 840 -1.23524e-13 -1.43143e-13 0 0 0 5.04 5.04 0 0 -1 1 2 41 1.41 290 0 0 0.0118 -0.00354 -0.00354 0 0 0 826 -1.85481e-13 -1.63966e-13 0 0 0 4.8734 4.8734 0 0 -1 1 2 42 1.42 290 0 0 0.0116 -0.00348 -0.00348 0 0 0 812 -1.90594e-13 -1.84789e-13 0 0 0 4.7096 4.7096 0 0 -1 1 2 43 1.43 290 0 0 0.0114 -0.00342 -0.00342 0 0 0 798 -1.67285e-13 -1.48768e-13 0 0 0 4.5486 4.5486 0 0 -1 1 2 44 1.44 290 0 0 0.0112 -0.00336 -0.00336 0 0 0 784 -1.72399e-13 -1.69591e-13 0 0 0 4.3904 4.3904 0 0 -1 1 2 45 1.45 290 0 0 0.011 -0.0033 -0.0033 0 0 0 770 -1.77512e-13 -1.90413e-13 0 0 0 4.235 4.235 0 0 -1 1 2 46 1.46 290 0 0 0.0108 -0.00324 -0.00324 0 0 0 756 -1.54203e-13 -1.54393e-13 0 0 0 4.0824 4.0824 0 0 -1 1 2 47 1.47 290 0 0 0.0106 -0.00318 -0.00318 0 0 0 742 -1.59317e-13 -1.75215e-13 0 0 0 3.9326 3.9326 0 0 -1 1 2 48 1.48 290 0 0 0.0104 -0.00312 -0.00312 0 0 0 728 -1.6443e-13 -1.39195e-13 0 0 0 3.7856 3.7856 0 0 -1 1 2 49 1.49 290 0 0 0.0102 -0.00306 -0.00306 0 0 0 714 -1.55332e-13 -1.60017e-13 0 0 0 3.6414 3.6414 0 0 -1 1 2 50 1.5 290 0 0 0.01 -0.003 -0.003 0 0 0 700 -1.60446e-13 -1.8084e-13 0 0 0 3.5 3.5 0 0 -1 1 2 51 1.51 290 0 0 0.0098 -0.00294 -0.00294 0 0 0 686 -1.51348e-13 -1.44819e-13 0 0 0 3.3614 3.3614 0 0 -1 1 2 52 1.52 290 0 0 0.0096 -0.00288 -0.00288 0 0 0 672 -1.56461e-13 -1.65642e-13 0 0 0 3.2256 3.2256 0 0 -1 1 2 53 1.53 290 0 0 0.0094 -0.00282 -0.00282 0 0 0 658 -1.47364e-13 -1.29621e-13 0 0 0 3.0926 3.0926 0 0 -1 1 2 54 1.54 290 0 0 0.0092 -0.00276 -0.00276 0 0 0 644 -1.52477e-13 -1.50444e-13 0 0 0 2.9624 2.9624 0 0 -1 1 2 55 1.55 290 0 0 0.009 -0.0027 -0.0027 0 0 0 630 -1.43379e-13 -1.42845e-13 0 0 0 2.835 2.835 0 0 -1 1 2 56 1.56 290 0 0 0.0088 -0.00264 -0.00264 0 0 0 616 -1.48492e-13 -1.35246e-13 0 0 0 2.7104 2.7104 0 0 -1 1 2 57 1.57 290 0 0 0.0086 -0.00258 -0.00258 0 0 0 602 -1.39395e-13 -1.27647e-13 0 0 0 2.5886 2.5886 0 0 -1 1 2 58 1.58 290 0 0 0.0084 -0.00252 -0.00252 0 0 0 588 -1.30297e-13 -1.4847e-13 0 0 0 2.4696 2.4696 0 0 -1 1 2 59 1.59 290 0 0 0.0082 -0.00246 -0.00246 0 0 0 574 -1.3541e-13 -1.40871e-13 0 0 0 2.3534 2.3534 0 0 -1 1 2 60 1.6 290 0 0 0.008 -0.0024 -0.0024 0 0 0 560 -1.26313e-13 -1.33272e-13 0 0 0 2.24 2.24 0 0 -1 1 2 61 1.61 290 0 0 0.0078 -0.00234 -0.00234 0 0 0 546 -1.31426e-13 -1.25673e-13 0 0 0 2.1294 2.1294 0 0 -1 1 2 62 1.62 290 0 0 0.0076 -0.00228 -0.00228 0 0 0 532 -6.5485e-14 -6.12304e-14 0 0 0 2.0216 2.0216 0 0 -1 1 2 63 1.63 290 0 0 0.0074 -0.00222 -0.00222 0 0 0 518 -1.37548e-14 3.21205e-15 0 0 0 1.9166 1.9166 0 0 -1 1 2 64 1.64 290 0 0 0.0072 -0.00216 -0.00216 0 0 0 504 -4.65718e-15 -1.76107e-14 0 0 0 1.8144 1.8144 0 0 -1 1 2 65 1.65 290 0 0 0.007 -0.0021 -0.0021 0 0 0 490 4.7073e-14 4.68318e-14 0 0 0 1.715 1.715 0 0 -1 1 2 66 1.66 290 0 0 0.0068 -0.00204 -0.00204 0 0 0 476 5.61706e-14 5.44308e-14 0 0 0 1.6184 1.6184 0 0 -1 1 2 67 1.67 290 0 0 0.0066 -0.00198 -0.00198 0 0 0 462 1.07901e-13 1.18873e-13 0 0 0 1.5246 1.5246 0 0 -1 1 2 68 1.68 290 0 0 0.0064 -0.00192 -0.00192 0 0 0 448 1.73842e-13 1.83316e-13 0 0 0 1.4336 1.4336 0 0 -1 1 2 69 1.69 290 0 0 0.0062 -0.00186 -0.00186 0 0 0 434 1.68729e-13 1.62493e-13 0 0 0 1.3454 1.3454 0 0 -1 1 2 70 1.7 290 0 0 0.006 -0.0018 -0.0018 0 0 0 420 2.06248e-13 1.98514e-13 0 0 0 1.26 1.26 0 0 -1 1 2 71 1.71 290 0 0 0.0058 -0.00174 -0.00174 0 0 0 406 2.72189e-13 2.62956e-13 0 0 0 1.1774 1.1774 0 0 -1 1 2 72 1.72 290 0 0 0.0056 -0.00168 -0.00168 0 0 0 392 2.95497e-13 2.98977e-13 0 0 0 1.0976 1.0976 0 0 -1 1 2 73 1.73 290 0 0 0.0054 -0.00162 -0.00162 0 0 0 378 3.33017e-13 3.34997e-13 0 0 0 1.0206 1.0206 0 0 -1 1 2 74 1.74 290 0 0 0.0052 -0.00156 -0.00156 0 0 0 364 3.56325e-13 3.71018e-13 0 0 0 0.9464 0.9464 0 0 -1 1 2 75 1.75 290 0 0 0.005 -0.0015 -0.0015 0 0 0 350 3.86739e-13 3.78617e-13 0 0 0 0.875 0.875 0 0 -1 1 2 76 1.76 290 0 0 0.0048 -0.00144 -0.00144 0 0 0 336 4.17153e-13 4.14638e-13 0 0 0 0.8064 0.8064 0 0 -1 1 2 77 1.77 290 0 0 0.0046 -0.00138 -0.00138 0 0 0 322 4.75989e-13 4.7908e-13 0 0 0 0.7406 0.7406 0 0 -1 1 2 78 1.78 290 0 0 0.0044 -0.00132 -0.00132 0 0 0 308 5.06403e-13 5.15101e-13 0 0 0 0.6776 0.6776 0 0 -1 1 2 79 1.79 290 0 0 0.0042 -0.00126 -0.00126 0 0 0 294 5.43922e-13 5.36911e-13 0 0 0 0.6174 0.6174 0 0 -1 1 2 80 1.8 290 0 0 0.004 -0.0012 -0.0012 0 0 0 280 5.74336e-13 5.72932e-13 0 0 0 0.56 0.56 0 0 -1 1 2 81 1.81 290 0 0 0.0038 -0.00114 -0.00114 0 0 0 266 6.0475e-13 6.08952e-13 0 0 0 0.5054 0.5054 0 0 -1 1 2 82 1.82 290 0 0 0.0036 -0.00108 -0.00108 0 0 0 252 6.06742e-13 6.0234e-13 0 0 0 0.4536 0.4536 0 0 -1 1 2 83 1.83 290 0 0 0.0034 -0.00102 -0.00102 0 0 0 238 6.37156e-13 6.38361e-13 0 0 0 0.4046 0.4046 0 0 -1 1 2 84 1.84 290 0 0 0.0032 -0.00096 -0.00096 0 0 0 224 6.20559e-13 6.21533e-13 0 0 0 0.3584 0.3584 0 0 -1 1 2 85 1.85 290 0 0 0.003 -0.0009 -0.0009 0 0 0 210 6.39489e-13 6.47337e-13 0 0 0 0.315 0.315 0 0 -1 1 2 86 1.86 290 0 0 0.0028 -0.00084 -0.00084 0 0 0 196 6.44208e-13 6.44719e-13 0 0 0 0.2744 0.2744 0 0 -1 1 2 87 1.87 290 0 0 0.0026 -0.00078 -0.00078 0 0 0 182 6.45374e-13 6.42102e-13 0 0 0 0.2366 0.2366 0 0 -1 1 2 88 1.88 290 0 0 0.0024 -0.00072 -0.00072 0 0 0 168 6.4654e-13 6.39484e-13 0 0 0 0.2016 0.2016 0 0 -1 1 2 89 1.89 290 0 0 0.0022 -0.00066 -0.00066 0 0 0 154 6.47707e-13 6.51078e-13 0 0 0 0.1694 0.1694 0 0 -1 1 2 90 1.9 290 0 0 0.002 -0.0006 -0.0006 0 0 0 140 6.52426e-13 6.4846e-13 0 0 0 0.14 0.14 0 0 -1 1 2 91 1.91 290 0 0 0.0018 -0.00054 -0.00054 0 0 0 126 6.67803e-13 6.67159e-13 0 0 0 0.1134 0.1134 0 0 -1 1 2 92 1.92 290 0 0 0.0016 -0.00048 -0.00048 0 0 0 112 6.74712e-13 6.76755e-13 0 0 0 0.0896 0.0896 0 0 -1 1 2 93 1.93 290 0 0 0.0014 -0.00042 -0.00042 0 0 0 98 6.99384e-13 7.00562e-13 0 0 0 0.0686 0.0686 0 0 -1 1 2 94 1.94 290 0 0 0.0012 -0.00036 -0.00036 0 0 0 84 7.24055e-13 7.24369e-13 0 0 0 0.0504 0.0504 0 0 -1 1 2 95 1.95 290 0 0 0.001 -0.0003 -0.0003 0 0 0 70 7.39846e-13 7.41071e-13 0 0 0 0.035 0.035 0 0 -1 1 2 96 1.96 290 0 0 0.0008 -0.00024 -0.00024 0 0 0 56 7.63423e-13 7.62324e-13 0 0 0 0.0224 0.0224 0 0 -1 1 2 97 1.97 290 0 0 0.0006 -0.00018 -0.00018 0 0 0 42 7.78118e-13 7.76471e-13 0 0 0 0.0126 0.0126 0 0 -1 1 2 98 1.98 290 0 0 0.0004 -0.00012 -0.00012 0 0 0 28 7.93702e-13 7.88842e-13 0 0 0 0.0056 0.0056 0 0 -1 1 2 99 1.99 290 0 0 0.0002 -6e-05 -6e-05 0 0 0 14 8.10448e-13 8.05405e-13 0 0 0 0.0014 0.0014 0 0 -1 1 2 100 2 290 0 0 2.00577e-18 5.57009e-18 5.46167e-18 0 0 0 6.34519e-13 8.26444e-13 8.20606e-13 0 0 0 9.68409e-15 9.68409e-15 0 0