From 7b1dfc488ccc53b0acc12dfd3bbdc93168f6fbb7 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:44:14 +0100 Subject: [PATCH 01/16] Add simcoon.modular to recipe test imports Include 'simcoon.modular' in the conda.recipe/meta.yaml test.imports list so the conda build/test verifies the modular subpackage can be imported. This ensures the modular component is packaged and available during CI or conda package validation. --- conda.recipe/meta.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 32bdd431..da5c4f04 100755 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -52,3 +52,4 @@ test: imports: - simcoon - simcoon._core + - simcoon.modular From 421e5d77ca872a3963e52edd71d3ba5d84634c54 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:44:36 +0100 Subject: [PATCH 02/16] Add scalar DamageMechanism (CDM) Introduce a new DamageMechanism implementing isotropic scalar continuum damage mechanics. Adds header and implementation files that integrate with StrainMechanism and InternalVariableCollection, register history variables (D, Y_max), compute the energy-based driving force Y = 0.5 * sigma : S : sigma, and evaluate damage via linear, exponential, power-law, and Weibull evolution laws. Includes damaged stiffness/tangent contributions, Jacobian/work computations, parameter configuration and basic validation, and caching of the compliance tensor. --- .../Umat/Modular/damage_mechanism.hpp | 194 +++++++++++ .../Umat/Modular/damage_mechanism.cpp | 327 ++++++++++++++++++ 2 files changed, 521 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/damage_mechanism.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp 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 00000000..8fdf1404 --- /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/src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/damage_mechanism.cpp new file mode 100644 index 00000000..f88364a7 --- /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 From d6920d255e7ee181449a970dd52575e465ab70ce Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:44:50 +0100 Subject: [PATCH 03/16] Add PlasticityMechanism for modular UMAT Introduce PlasticityMechanism class (header + implementation) to provide a rate-independent plasticity strain mechanism for the modular UMAT. The new files combine a yield criterion, isotropic hardening and kinematic hardening (via factory-created hardening objects), register internal variables (p, EP and backstresses), and implement core routines: configure, register_variables, compute_constraints, compute_flow_directions, compute_jacobian_contribution, inelastic_strain, update, tangent_contribution and compute_work. The implementation caches flow direction and related quantities for Jacobian/tangent calculations, supports multiple yield/hardening types, and uses Armadillo vectors/matrices and the existing InternalVariableCollection API. --- .../Umat/Modular/plasticity_mechanism.hpp | 175 ++++++++++++ .../Umat/Modular/plasticity_mechanism.cpp | 259 ++++++++++++++++++ 2 files changed, 434 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/plasticity_mechanism.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp 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 00000000..09cb9ff1 --- /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/src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/plasticity_mechanism.cpp new file mode 100644 index 00000000..7c25accd --- /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 From 41c07a0669de387d7af02507da76fda6e2925745 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:45:01 +0100 Subject: [PATCH 04/16] Add ElasticityModule for modular UMAT Introduce ElasticityModule (header + implementation) to encapsulate linear elastic behavior for the modular UMAT. Supports isotropic, cubic (EnuG or Cii conventions), transversely isotropic, and orthotropic configurations via configure_* helpers and a configure(type, props, offset) convenience. Stores 6x6 stiffness (L) and compliance (M) matrices, 6-component CTE vector (alpha), and provides derived helpers damaged_L(d) and thermal_strain(DT). Uses Armadillo types and existing constitutive functions (L_iso/M_iso, L_cubic/M_cubic, L_isotrans/M_isotrans, L_ortho/M_ortho). Adds basic validation (configured_ flag) and a constexpr props_count helper. --- .../Umat/Modular/elasticity_module.hpp | 212 ++++++++++++++++ .../Umat/Modular/elasticity_module.cpp | 228 ++++++++++++++++++ 2 files changed, 440 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/elasticity_module.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp 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 00000000..c9e25762 --- /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/src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp b/src/Continuum_mechanics/Umat/Modular/elasticity_module.cpp new file mode 100644 index 00000000..0b336e71 --- /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 From f3173394fac16165327a753fb2558081fa9c876c Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:45:14 +0100 Subject: [PATCH 05/16] Add modular isotropic and kinematic hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce modular hardening framework: new header and implementation for isotropic and kinematic hardening models. Implements No/Linear/Power-law/Voce/Combined-Voce isotropic models and No/Prager/Armstrong–Frederick/Chaboche kinematic models, with factory create() functions, property parsing (configure), evaluation (R, dR/dp), internal-variable registration, backstress accumulation, flow rules and update routines. Uses Armadillo and InternalVariableCollection for vector storage and exposes props_count helpers to consume material property arrays. --- .../Umat/Modular/hardening.hpp | 370 ++++++++++++++++++ .../Umat/Modular/hardening.cpp | 257 ++++++++++++ 2 files changed, 627 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/hardening.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/hardening.cpp 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 00000000..360c4640 --- /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/src/Continuum_mechanics/Umat/Modular/hardening.cpp b/src/Continuum_mechanics/Umat/Modular/hardening.cpp new file mode 100644 index 00000000..9632a913 --- /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 From 888b0d2ea6212fa5b3c2082f145106b792217ce5 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:45:27 +0100 Subject: [PATCH 06/16] Add InternalVariable for UMAT modular Add a type-safe InternalVariable class to support constitutive-model state storage. New files include/include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable.hpp and src/Continuum_mechanics/Umat/Modular/internal_variable.cpp implement scalar, 6-component Voigt vector, and 6x6 matrix types with start/current state tracking, delta computation, rotation/objectivity (uses rotate_strain / rotateL), and pack/unpack serialization to a flat statev array with configurable offset. Uses Armadillo, performs input validation, and throws on type mismatches. Intended for use in UMAT modular constitutive implementations. --- .../Umat/Modular/internal_variable.hpp | 280 ++++++++++++++++ .../Umat/Modular/internal_variable.cpp | 315 ++++++++++++++++++ 2 files changed, 595 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/internal_variable.cpp 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 00000000..790b9b6f --- /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/src/Continuum_mechanics/Umat/Modular/internal_variable.cpp b/src/Continuum_mechanics/Umat/Modular/internal_variable.cpp new file mode 100644 index 00000000..74258646 --- /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 From 72c3b15e3fb51315a169975ccedda1b12bcbb898 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:45:38 +0100 Subject: [PATCH 07/16] Add InternalVariableCollection class Introduce InternalVariableCollection (header and implementation) to manage multiple InternalVariable instances for modular UMAT models. Provides named registration (scalar/6-vector/6x6-matrix), automatic statev offset computation, pack/unpack, rotation, start/reset helpers, iteration, and utility functions (names, clear). Uses unique_ptr storage (non-copyable, moveable), throws on invalid access/duplicate names, and depends on Armadillo. --- .../Modular/internal_variable_collection.hpp | 246 ++++++++++++++++++ .../Modular/internal_variable_collection.cpp | 209 +++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/internal_variable_collection.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp 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 00000000..f275ddab --- /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/src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp b/src/Continuum_mechanics/Umat/Modular/internal_variable_collection.cpp new file mode 100644 index 00000000..9b609bde --- /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 From 4a7bd66f6f7e0e1d5cfbac649e828fb0d1b5fb54 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:45:48 +0100 Subject: [PATCH 08/16] Add Modular UMAT orchestrator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a composable ModularUMAT class and standalone umat_modular function to orchestrate elasticity and multiple strain mechanisms (plasticity, viscoelasticity, damage). Adds public configuration APIs (set_elasticity, add_plasticity/add_viscoelasticity/add_damage, configure_from_props), initialization and state handling (internal variable collection), the main run() entry that performs elastic prediction, return mapping (Newton + Fischer–Burmeister), consistent tangent computation, and work calculations. Implements compute_tangent and return_mapping, and integrates with existing mechanism classes and numeric solver utilities. Files added: include/.../modular_umat.hpp and src/.../modular_umat.cpp (GPL header included). --- .../Umat/Modular/modular_umat.hpp | 388 +++++++++++++++ .../Umat/Modular/modular_umat.cpp | 453 ++++++++++++++++++ 2 files changed, 841 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/modular_umat.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/modular_umat.cpp 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 00000000..59e2496d --- /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/src/Continuum_mechanics/Umat/Modular/modular_umat.cpp b/src/Continuum_mechanics/Umat/Modular/modular_umat.cpp new file mode 100644 index 00000000..4efb8b17 --- /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 From adeecd173048315de6dc96070924809c111f41ba Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:46:10 +0100 Subject: [PATCH 09/16] Add StrainMechanism base class Introduce a new header defining the StrainMechanism abstract base class for modular UMATs. Adds MechanismType enum and a comprehensive interface for pluggable strain mechanisms (configuration, variable registration, constraint evaluation, flow directions, Jacobian contributions, inelastic strain, internal variable updates, tangent contributions, and work decomposition). Uses Armadillo and InternalVariableCollection and includes GPL license header; intended to standardize integration of plasticity, viscoelasticity, damage, etc. --- .../Umat/Modular/strain_mechanism.hpp | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/strain_mechanism.hpp 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 00000000..2838d60c --- /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 From 23d13726200ad077c24a674caa1500f8d1734dab Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:46:21 +0100 Subject: [PATCH 10/16] Add ViscoelasticMechanism (Prony series) Introduce ViscoelasticMechanism implementing a generalized Maxwell (Prony series) viscoelastic branch model. Adds header and source with configuration, internal-variable registration, constraint evaluation, flow directions, Jacobian contribution, inelastic strain accumulation, update routine, tangent modification and work computation. Supports N Prony terms, registers one viscous strain vector per branch, reads g_i and tau_i from property vector, and uses an optional reference stiffness (L_0) / compliance for branch targets. Includes basic runtime checks (positive tau) and a simple algorithmic tangent and update/constraint approximations (implicit exponential integration and norm-based constraints). Files use Armadillo and include GPL license headers. --- .../Umat/Modular/viscoelastic_mechanism.hpp | 151 ++++++++++ .../Umat/Modular/viscoelastic_mechanism.cpp | 281 ++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp 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 00000000..bd231847 --- /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/src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp b/src/Continuum_mechanics/Umat/Modular/viscoelastic_mechanism.cpp new file mode 100644 index 00000000..d1aac0c1 --- /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 From 18e13fd19eabb4f21135c7fbde2f5b65d013c541 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:46:30 +0100 Subject: [PATCH 11/16] Add YieldCriterion module for UMAT Introduce YieldCriterion class (header and implementation) to provide a modular wrapper around existing Eq_stress/dEq_stress criterion functions for UMAT. Adds YieldType enum (VON_MISES, TRESCA, DRUCKER, HILL, DFA, ANISOTROPIC), configuration helpers (including configure from a props vector with offset), parameter storage via arma::vec, and public APIs to compute equivalent_stress, flow_direction and plastic_flow (with optional backstress shift). Includes a constexpr props_count utility and runtime checks that the criterion is configured; integration relies on simcoon/Continuum_mechanics/Functions/criteria.hpp. --- .../Umat/Modular/yield_criterion.hpp | 234 ++++++++++++++++++ .../Umat/Modular/yield_criterion.cpp | 198 +++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 include/simcoon/Continuum_mechanics/Umat/Modular/yield_criterion.hpp create mode 100644 src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp 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 00000000..00c26be0 --- /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/src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp b/src/Continuum_mechanics/Umat/Modular/yield_criterion.cpp new file mode 100644 index 00000000..9abb9980 --- /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 From f31f6156a4c759d7f2c80ab140d223be64f59c7d Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:46:42 +0100 Subject: [PATCH 12/16] Add modular UMAT material module Introduce a new simcoon.modular module implementing a composable UMAT material system. Adds enums and dataclasses for elasticity (isotropic, cubic, transverse isotropic, orthotropic), yield criteria (VonMises, Tresca, Drucker, Hill, DFA, anisotropic), isotropic and kinematic hardening laws (linear, power-law, Voce, combined Voce, Prager, Armstrong-Frederick, Chaboche), and mechanisms (Plasticity, Viscoelasticity with Prony series, Damage). Implements ModularMaterial to assemble a flat props array (for the C++ "MODUL" UMAT), compute nprops/nstatev, provide human-readable summaries, and includes convenience factory functions (elastic_model, elastoplastic_model, viscoelastic_model). --- python-setup/simcoon/modular.py | 1109 +++++++++++++++++++++++++++++++ 1 file changed, 1109 insertions(+) create mode 100644 python-setup/simcoon/modular.py diff --git a/python-setup/simcoon/modular.py b/python-setup/simcoon/modular.py new file mode 100644 index 00000000..599deb2c --- /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)), + ], + ) From 23164510c47fb0e41a6762d5e50a06be0fb82e5d Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:46:55 +0100 Subject: [PATCH 13/16] Import modular in package __init__ Add an explicit `from simcoon import modular` to simcoon.__init__ so the `simcoon.modular` submodule is available to consumers when importing the package. This makes the modular API accessible without requiring an extra import. --- python-setup/simcoon/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python-setup/simcoon/__init__.py b/python-setup/simcoon/__init__.py index 266fd211..c9c29d59 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 From b0bd8d70b1b2231e52ac8aaff57725709718eac6 Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:50:49 +0100 Subject: [PATCH 14/16] Remove UMEXT results test files Delete generated test output files from testBin/Umats/UMEXT/results: results_job_global-0.txt and results_job_local-0.txt. These are result artifacts (deleted mode 100755) and are not intended to be tracked in source control. --- .../UMEXT/results/results_job_global-0.txt | 200 ------------------ .../UMEXT/results/results_job_local-0.txt | 200 ------------------ 2 files changed, 400 deletions(-) delete mode 100755 testBin/Umats/UMEXT/results/results_job_global-0.txt delete mode 100755 testBin/Umats/UMEXT/results/results_job_local-0.txt 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 3547bc6e..00000000 --- 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 3547bc6e..00000000 --- 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 From ba5e1339a0dba660890162a7c80040cfaa5a9dae Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:51:00 +0100 Subject: [PATCH 15/16] Add unit tests for modular UMAT components Add comprehensive GTest unit tests for the Modular UMAT implementation (test/Libraries/Umat/Modular/Tmodular_umat.cpp) and register the new test in test/CMakeLists.txt. The tests cover InternalVariable and InternalVariableCollection, ElasticityModule (isotropic/cubic/CTE), YieldCriterion (von Mises), Isotropic and Kinematic hardening models, PlasticityMechanism behavior, ModularUMAT initialization/run behavior, and an integration test for uniaxial tension. This provides broad verification of elastic/plastic responses, packing/unpacking of state variables, hardening laws, and consistency of tangents and stresses. --- test/CMakeLists.txt | 1 + test/Libraries/Umat/Modular/Tmodular_umat.cpp | 601 ++++++++++++++++++ 2 files changed, 602 insertions(+) create mode 100644 test/Libraries/Umat/Modular/Tmodular_umat.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 155f1002..cf9b294f 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 00000000..bb3729e3 --- /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); + } +} From 3ab718f56f7eadead39f940960d859d7c4cacd1f Mon Sep 17 00:00:00 2001 From: Yves Chemisky Date: Wed, 28 Jan 2026 22:51:11 +0100 Subject: [PATCH 16/16] Add Modular UMAT integration Integrate the new modular UMAT into the project: add modular_umat includes to the Python wrapper and smart UMAT selector, register the "MODUL" UMAT id in both mappings (30 for Python wrapper, 200 for smart selector), and add case branches to call umat_modular accordingly. Update CMakeLists to include the Modular UMAT source files (internal_variable, elasticity_module, yield_criterion, hardening, plasticity_mechanism, viscoelastic_mechanism, damage_mechanism, and modular_umat) so the modular implementation is built. This wires the composable constitutive model into the build, runtime selection, and Python bindings. --- .../Libraries/Continuum_mechanics/umat.cpp | 9 ++++++++- src/CMakeLists.txt | 11 +++++++++++ src/Continuum_mechanics/Umat/umat_smart.cpp | 8 +++++++- 3 files changed, 26 insertions(+), 2 deletions(-) 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 8641b994..5acc3025 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 439b5125..1ac8e74e 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/umat_smart.cpp b/src/Continuum_mechanics/Umat/umat_smart.cpp index 1a5974c0..e5b9a342 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;