Coques
En bref
Une coque est une surface qui porte à la fois des efforts de membrane (dans son plan) et des moments de flexion : une tôle, une paroi de réservoir, une dalle mince.
| opérateur | pyrucast.model.shell(fespace, formulation) |
| tag | mechanical |
| type | domaine avec comportement (label() : "Shell") |
| éléments | TRI3, QUA4, dans un espace 3-D |
formulation est "thick" (Reissner-Mindlin, cisaillement transverse intégré
de façon réduite) ou "kirchhoff" (Kirchhoff discret, DKT et DKQ, sans
cisaillement). Les deux partagent les inconnues, le repère local et la
membrane ; seuls la flexion et le cisaillement changent. Chaque maille est une
facette plane : une forte courbure demande un maillage plus fin.
Théorie
Six inconnues par nœud. Trois translations et trois rotations,
u_x…u_z, r_x…r_z, comme le cadre spatial des poutres : une coque et un
portique partagent leurs nœuds sans adaptateur. La sixième, la rotation autour
de la normale (le vrillage), n’a pas de raideur physique sur une facette
plane ; elle est liée à la rotation propre de la membrane, ce qui ne s’oppose
pas à une rotation rigide de la facette. Cette liaison travaille : elle a un
effort conjugué, M_drill.
Le repère local de chaque élément est construit depuis ses nœuds : la première arête et la normale de la facette. Les dérivées dans le plan de la facette sont le gradient tangent de la géométrie, projeté sur ce repère.
Reissner-Mindlin (thick). La fibre normale reste droite mais pas normale
à la surface déformée ; dans l’épaisseur \(z \in [-h/2, h/2]\),
\[ u_x = u + z\,\theta_y, \qquad u_y = v - z\,\theta_x, \qquad u_z = w . \]
Les déformations se séparent en membrane \(\varepsilon\), courbures \(\kappa\) et cisaillement transverse \(\gamma\). Pour un matériau homogène en contraintes planes, intégré dans l’épaisseur, les lois de section sont
\[ D_m = \frac{E h}{1-\nu^2}\begin{bmatrix} 1 & \nu & 0 \\ \nu & 1 & 0 \\ 0 & 0 & \tfrac{1-\nu}2 \end{bmatrix}, \qquad D_b = \frac{h^2}{12}\,D_m, \qquad D_s = k_s\,G\,h , \]
avec \(k_s = 5/6\) par défaut (composante k_s), qui corrige un cisaillement
supposé uniforme dans l’épaisseur. La raideur est
\(\int_A (B_m^{\mathsf T} D_m B_m + B_b^{\mathsf T} D_b B_b + B_s^{\mathsf T} D_s B_s)\),
la membrane et la flexion intégrées complètement, le cisaillement en un
point. Intégré complètement, ce terme, en \(h\), écraserait la flexion, en
\(h^3\), quand la coque s’amincit : l’élément refuserait de fléchir (blocage
en cisaillement).
Kirchhoff discret (kirchhoff). La fibre reste normale, \(\gamma = 0\).
Plutôt que des dérivées secondes de la flèche, qui demanderaient une base
\(C^1\), la rotation est interpolée quadratiquement et
\(\gamma = 0\) est imposé aux sommets et le long de chaque arête ; les
rotations de milieu d’arête sont ensuite éliminées. Rien n’est dérivé deux fois
et la base reste de Lagrange. La limite mince est exacte par construction : il
n’y a pas de cisaillement à bloquer. L’effort tranchant d’une plaque mince est
alors une réaction, pas une sortie de la loi.
Variables
thick | kirchhoff | |
|---|---|---|
| primales | u_x, u_y, u_z, r_x, r_y, r_z | u_x, u_y, u_z, r_x, r_y, r_z |
| duales | f_x, f_y, f_z, m_x, m_y, m_z | f_x, f_y, f_z, m_x, m_y, m_z |
| matériau requis | E, nu, h | E, nu, h |
| matériau facultatif | rho, k_s | rho |
| entrée du comportement | eps_xx, eps_yy, eps_xy, kappa_xx, kappa_yy, kappa_xy, drill, gamma_xz, gamma_yz | eps_xx, eps_yy, eps_xy, kappa_xx, kappa_yy, kappa_xy, drill |
| sortie du comportement | N_xx, N_yy, N_xy, M_xx, M_yy, M_xy, M_drill, Q_xz, Q_yz | N_xx, N_yy, N_xy, M_xx, M_yy, M_xy, M_drill |
| état | aucun | aucun |
L’entrée vient de pyrucast.element_field.shell_deformation, ou de strain :
membrane, flexion et vrillage à chaque point de Gauss, cisaillement au seul
point réduit.
Ce qu’elle produit
| raideur | membrane, flexion, cisaillement (thick) et vrillage |
| masse | — |
| géométrique | — |
| tangente | = raideur |
| forces internes | le transposé du même \(B\), ramené aux axes globaux : \(\int B^{\mathsf T}\sigma = K\,u\) exactement |
Exemple
Une facette carrée, ses inconnues et son matériau :
c = pyrucast.Coords(3)
n = [
c.add_node(p)
for p in ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0])
]
surface = pyrucast.Mesh(c, "QUA4")
surface.unit().add_cell(n)
coque = pyrucast.model.shell(
pyrucast.FiniteElementSpace(surface), "thick"
) # ou "kirchhoff"
print(coque[0].primal_vars()) # ['u_x', 'u_y', 'u_z', 'r_x', 'r_y', 'r_z']
print(coque[0].material_components()) # ['E', 'nu', 'h']
materiaux = pyrucast.element_field.material_field(
coque, [("E", 210e3), ("nu", 0.3), ("h", 0.01)]
)
K = pyrucast.matrix.stiffness(coque, materiaux)
print(K.n_rows) # 24 : six inconnues par nœud
Côté Rust
Shell (src/models/shell.rs, src/models/shell/) porte sa formulation en
attribut (ShellFormulation). Les deux formulations construisent leur
\(B\) au même endroit (shell::b_into) et ne diffèrent que par ses lignes de
flexion. thick intègre sur deux sous-espaces du même maillage, à quadrature
complète et réduite ; la physique les construit elle-même.
Les tests retrouvent la flèche d’une plaque carrée encastrée sous charge uniforme et vérifient que le maillage converge ; ils mesurent la flèche normalisée d’une plaque mince sur deux décades d’épaisseur, qu’un élément bloqué perdrait ; ils vérifient la membrane, et qu’une rotation rigide ne coûte aucune énergie.
use pyrucast::aggregate::Aggregate;
use pyrucast::atoms::{ElementType, Node};
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::shell::ShellFormulation;
use pyrucast::ops::model;
use pyrucast::ops::solver::solve;
use pyrucast::Result;
const E: f64 = 210_000.0;
const NU: f64 = 0.3;
/// Side of the square plate.
const A: f64 = 1.0;
/// Uniform transverse pressure.
const Q: f64 = 1.0;
#[test]
fn a_clamped_plate_matches_its_textbook_deflection() -> Result<()> {
let h = 0.01; // thin enough to compare with plate theory, thick enough to be safe
let w = central_deflection(12, h, ShellFormulation::Thick, ElementType::QUA4)?;
// Timoshenko & Woinowsky-Krieger: w_max = 0.00126 · qa⁴/D.
let d = E * h * h * h / (12.0 * (1.0 - NU * NU));
let exact = 0.00126 * Q * A.powi(4) / d;
assert!(
(w - exact).abs() < 0.06 * exact,
"central deflection {w}, textbook {exact}"
);
Ok(())
}
La même plaque en Kirchhoff discret, triangles et quadrangles :
#[test]
fn a_clamped_plate_matches_its_textbook_deflection_in_discrete_kirchhoff() -> Result<()> {
let h = 0.01;
let d = E * h * h * h / (12.0 * (1.0 - NU * NU));
let exact = 0.00126 * Q * A.powi(4) / d;
for element in [ElementType::QUA4, ElementType::TRI3] {
let w = central_deflection(12, h, ShellFormulation::Kirchhoff, element)?;
assert!(
(w - exact).abs() < 0.03 * exact,
"{element:?}: central deflection {w}, textbook {exact}"
);
}
Ok(())
}