Plasticité de von Mises
En bref
Un métal qui se déforme de façon irréversible au-delà d’une limite d’élasticité. Deux lois, au critère de von Mises :
| opérateurs | pyrucast.model.plasticity_perfect(fespace, kinematics, strain_measure="linear") (sans écrouissage), pyrucast.model.plasticity_isotropic(fespace, kinematics, strain_measure="linear") (écrouissage isotrope linéaire) |
| tag | mechanical |
| type | domaine avec comportement (label() : "Plasticity") |
| éléments | 2-D, en plan ou en axisymétrie : TRI3, QUA4, TRI6, QUA8, QUA9 ; 3-D : TET4, PYRA5, PENTA6, HEX8, TET10, PENTA15, HEX20, HEX27 |
| cinématiques | plane_stress, plane_strain, axisymmetric, full_3d |
Théorie
L’élastoplasticité en petites déformations partage la déformation en une part élastique et une part plastique :
\[ \varepsilon = \varepsilon^e + \varepsilon^p, \qquad \sigma = D : (\varepsilon - \varepsilon^p), \qquad f(\sigma, p) \le 0, \qquad \dot\varepsilon^p = \dot\lambda\,\frac{\partial f}{\partial\sigma}, \]
avec les conditions de Kuhn-Tucker \(\dot\lambda \ge 0\), \(f \le 0\), \(\dot\lambda\,f = 0\). Le critère de von Mises porte sur la contrainte équivalente \(q = \sqrt{\tfrac32\,s : s}\), où \(s\) est le déviateur de \(\sigma\) :
\[ f(\sigma, p) = q - \big(\sigma_y + H\,p\big), \qquad \dot p = \dot\lambda , \]
\(p\) étant la déformation plastique cumulée et \(H\) le module d’écrouissage, nul pour la loi parfaite.
Le retour radial. Un pas part de l’état \(A\) (\(\varepsilon^p_A\), \(p_A\)) et de la déformation totale \(\varepsilon_B\) :
- prédiction élastique, \(\sigma^{tr} = D : (\varepsilon_B - \varepsilon^p_A)\), et sa contrainte équivalente \(q^{tr}\) ;
- si \(q^{tr} \le \sigma_y + H\,p_A\), le pas est élastique ;
- sinon, la normale étant colinéaire au déviateur, la consistance est affine et se résout sans itérer :
\[ \Delta p = \frac{q^{tr} - (\sigma_y + H\,p_A)}{3\mu + H}, \qquad s_B = s^{tr}\Big(1 - \frac{3\mu\,\Delta p}{q^{tr}}\Big), \qquad \varepsilon^p_B = \varepsilon^p_A + \frac{3\,\Delta p}{2\,q^{tr}}\,s^{tr}, \]
\(\mu\) étant le module de cisaillement ; la pression ne change pas.
Le calcul est mené en 3-D quelle que soit la cinématique. En déformations planes, les composantes hors du plan de la déformation sont nulles ; en contraintes planes, la condition \(\sigma_{zz} = 0\) est résolue par une itération locale autour du retour radial ; en axisymétrie, la déformation orthoradiale est mesurée.
La tangente cohérente est la dérivée exacte du retour,
\[ D_\text{alg} = K\,I\otimes I + 2\mu\,\theta\,\mathbb I_\text{dev} - 2\mu\,\bar\theta\;\hat n\otimes\hat n, \qquad \theta = \frac{\sigma_y + H\,p_B}{q^{tr}}, \qquad \bar\theta = \frac{3\mu}{3\mu + H} - (1 - \theta), \]
avec \(\hat n = s^{tr}/\lVert s^{tr}\rVert\) et \(K\) le module de compressibilité. Le facteur \(\theta\) rend compte du pas fini : c’est ce qui donne à Newton sa convergence quadratique.
La raideur reste la raideur élastique \(\int B^{\mathsf T} D\,B\) : un opérateur constant, celui d’un Newton modifié. La tangente \(\int B^{\mathsf T} D_\text{alg} B\) est celle d’un Newton complet.
Variables
Selon la cinématique :
plane_stress, plane_strain | axisymmetric | full_3d | |
|---|---|---|---|
| primales | u_x, u_y | u_x, u_y | u_x, u_y, u_z |
| duales | f_x, f_y | f_x, f_y | f_x, f_y, f_z |
| entrée du comportement | eps_xx, eps_yy, eps_xy | eps_xx, eps_yy, eps_zz, eps_xy | eps_xx, eps_yy, eps_zz, eps_yz, eps_xz, eps_xy |
| contrainte | sigma_xx, sigma_yy, sigma_xy | sigma_xx, sigma_yy, sigma_zz, sigma_xy | sigma_xx, sigma_yy, sigma_zz, sigma_yz, sigma_xz, sigma_xy |
En axisymétrie, eps_zz et sigma_zz sont les composantes orthoradiales.
La sortie du comportement est la contrainte, suivie de l’état :
| matériau requis | E, nu, sigma_y ; plus H pour plasticity_isotropic |
| matériau facultatif | rho, alpha |
| état, écrit après la contrainte | la déformation plastique eps_p_xx, eps_p_yy, eps_p_zz, eps_p_yz, eps_p_xz, eps_p_xy ; la déformation plastique cumulée p ; la déformation totale eps_xx, eps_yy, eps_zz, eps_yz, eps_xz, eps_xy ; et, en plane_stress et plane_strain, sigma_zz |
état relu (prev) | toute la sortie du pas précédent : la contrainte et l’état |
La déformation plastique et la déformation totale ont toujours six
composantes, quelle que soit la cinématique. Au premier pas, prev est l’état
de repos, element_field.initial_state(modele, materiaux), où tout est nul.
Ensuite, la sortie d’un pas se passe telle quelle comme prev au pas suivant.
Ce qu’elle produit
| raideur | élastique |
| masse | exige rho |
| géométrique | sous la contrainte sigma_* |
| tangente | analytique, \(D_\text{alg}\) ; demande la déformation et prev |
| forces internes | \(\int B^{\mathsf T}\sigma\) |
Exemple
Une maille étirée à \(\varepsilon_{xx} = 2\cdot10^{-3}\), au-delà de \(\sigma_y/E\). La contrainte équivalente reste à \(\sigma_y\) :
fes, u = _un_qua4(2e-3) # une maille, ε_xx = 2·10⁻³ : au-delà de σ_y/E
modele = pyrucast.model.plasticity_perfect(fes, "plane_stress")
materiaux = pyrucast.element_field.material_field(
modele, [("E", 210e3), ("nu", 0.3), ("sigma_y", 250.0)]
)
eps = pyrucast.element_field.strain(modele, u, materiaux)
repos = pyrucast.element_field.initial_state(
modele, materiaux
) # premier pas : l'état de repos
etat = pyrucast.element_field.integrate_behavior(modele, eps, materiaux, repos)
s = {nom: etat[0].value(0, 0, nom) for nom in ("sigma_xx", "sigma_yy", "sigma_xy")}
q = math.sqrt(
s["sigma_xx"] ** 2
+ s["sigma_yy"] ** 2
- s["sigma_xx"] * s["sigma_yy"]
+ 3 * s["sigma_xy"] ** 2
)
print(round(q, 3)) # 250.0 : la contrainte équivalente est plafonnée à σ_y
print(etat[0].value(0, 0, "p") > 0) # True : de la déformation plastique
# Le pas suivant repart de cet état.
etat = pyrucast.element_field.integrate_behavior(modele, eps, materiaux, prev=etat)
Kt = pyrucast.matrix.tangent(
modele, materiaux, eps, prev=etat
) # le module algorithmique
Avec écrouissage, elle vaut \(\sigma_y + H\,p\) :
modele = pyrucast.model.plasticity_isotropic(fes, "plane_stress")
materiaux = pyrucast.element_field.material_field(
modele, [("E", 210e3), ("nu", 0.3), ("sigma_y", 250.0), ("H", 1000.0)]
)
eps = pyrucast.element_field.strain(modele, u, materiaux)
etat = pyrucast.element_field.integrate_behavior(
modele, eps, materiaux, pyrucast.element_field.initial_state(modele, materiaux)
)
s = {nom: etat[0].value(0, 0, nom) for nom in ("sigma_xx", "sigma_yy", "sigma_xy")}
q = math.sqrt(
s["sigma_xx"] ** 2
+ s["sigma_yy"] ** 2
- s["sigma_xx"] * s["sigma_yy"]
+ 3 * s["sigma_xy"] ** 2
)
print(round(q - (250.0 + 1000.0 * etat[0].value(0, 0, "p")), 3)) # 0.0 : q = σ_y + H·p
La boucle de Newton complète (pas de charge, résidu
\(F_\text{ext} - F_\text{int}\), résolution, report de l’état) s’écrit en
Python avec ces opérateurs ; examples/plasticite_poutre_console.py en est un
exemple, une console cisaillée jusqu’à plastifier à l’encastrement.
Côté Rust
Plasticity (src/models/plasticity.rs) porte la loi d’écoulement en
attribut : une même physique sert les dix lois de cette page, de la page
Drucker-Prager et Ottosen, de la page
Fluage et la loi de Gurson. Les deux lois de von Mises vivent dans
src/models/plasticity/von_mises.rs ; H = 0 redonne la loi parfaite par le
même chemin de code. examples/plasticite_poutre_console.rs est la même
console en Rust, et sert de banc de parallélisme.
use pyrucast::aggregate::Aggregate;
use pyrucast::atoms::{ElementType, Node};
use pyrucast::containers::element_field::ElementField;
use pyrucast::containers::finite_element_space::FiniteElementSpace;
use pyrucast::containers::mesh::{Mesh, SubMesh};
use pyrucast::containers::model::Model;
use pyrucast::containers::node_field::{NodeField, SubNodeField};
use pyrucast::coords::Coords;
use pyrucast::handle::Handle;
use pyrucast::models::plasticity::law::PlasticLaw;
use pyrucast::models::tensor::Kinematics;
use pyrucast::ops::element_field::{
behavior::{initial_state, integrate},
deformation, material_field, StrainMeasure,
};
use pyrucast::ops::matrix::tangent;
use pyrucast::ops::model;
use pyrucast::ops::node_field::internal_forces;
use pyrucast::Result;
const AXES: [&str; 3] = ["x", "y", "z"];
/// von Mises with linear isotropic hardening.
const ISOTROPIC: &[(&str, f64)] = &[
("E", 70_000.0),
("nu", 0.3),
("sigma_y", 200.0),
("H", 5_000.0),
];
/// A frictional, mildly dilatant material — `ψ < α`, so the flow is
/// non-associated.
const DRUCKER: &[(&str, f64)] = &[
("E", 20_000.0),
("nu", 0.2),
("friction", 0.3),
("k", 30.0),
("psi", 0.1),
];
/// Ottosen's classic concrete set, for a tensile/compressive strength ratio of
/// about 0.1.
const OTTOSEN: &[(&str, f64)] = &[
("E", 30_000.0),
("nu", 0.2),
("a", 1.2759),
("b", 3.1962),
("k_1", 11.7365),
("k_2", 0.9801),
("sigma_c", 30.0),
];
#[test]
fn isotropic_hardening_satisfies_its_consistency_condition() -> Result<()> {
let cube = Cube::new(PlasticLaw::Isotropic, ISOTROPIC)?;
// Well past yield, so the step is definitely plastic.
let s = cube.stress(&uniaxial(0.02))?;
let (q, p) = (von_mises(&s.sigma), s.p);
assert!(p > 0.0, "the step must be plastic (p = {p})");
let expected = 200.0 + 5_000.0 * p;
assert!(
(q - expected).abs() < 1e-6 * expected,
"q = {q}, expected σ_y + H·p = {expected}"
);
Ok(())
}