Skip to main content

solid_mechanics/
plane.rs

1//! Plane stress and plane strain conditions.
2
3use serde::{Deserialize, Serialize};
4
5use crate::constitutive::HookeIsotropic;
6
7/// Plane stress condition (σ_zz = τ_xz = τ_yz = 0).
8/// Thin structures loaded in-plane.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct PlaneStress {
11    pub material: HookeIsotropic,
12}
13
14impl PlaneStress {
15    pub fn new(material: HookeIsotropic) -> Self {
16        Self { material }
17    }
18
19    /// 2D stress → 2D strain under plane stress.
20    /// ε_xx = (σ_xx - ν·σ_yy) / E
21    /// ε_yy = (σ_yy - ν·σ_xx) / E
22    /// γ_xy = τ_xy / G
23    pub fn strain_from_stress_2d(&self, sxx: f64, syy: f64, txy: f64) -> (f64, f64, f64) {
24        let e = self.material.youngs_modulus;
25        let nu = self.material.poissons_ratio;
26        let g = self.material.shear_modulus();
27        let exx = (sxx - nu * syy) / e;
28        let eyy = (syy - nu * sxx) / e;
29        let gxy = txy / g;
30        (exx, eyy, gxy)
31    }
32
33    /// 2D strain → 2D stress under plane stress.
34    pub fn stress_from_strain_2d(&self, exx: f64, eyy: f64, gxy: f64) -> (f64, f64, f64) {
35        let e = self.material.youngs_modulus;
36        let nu = self.material.poissons_ratio;
37        let factor = e / (1.0 - nu * nu);
38        let sxx = factor * (exx + nu * eyy);
39        let syy = factor * (eyy + nu * exx);
40        let txy = self.material.shear_modulus() * gxy;
41        (sxx, syy, txy)
42    }
43
44    /// Out-of-plane strain under plane stress: ε_zz = -ν(σ_xx + σ_yy)/E
45    pub fn out_of_plane_strain(&self, sxx: f64, syy: f64) -> f64 {
46        let e = self.material.youngs_modulus;
47        let nu = self.material.poissons_ratio;
48        -nu * (sxx + syy) / e
49    }
50
51    /// Plane stress stiffness matrix (3×3 for [ε_xx, ε_yy, γ_xy]).
52    pub fn stiffness_matrix_2d(&self) -> [[f64; 3]; 3] {
53        let e = self.material.youngs_modulus;
54        let nu = self.material.poissons_ratio;
55        let factor = e / (1.0 - nu * nu);
56        let g = e / (2.0 * (1.0 + nu));
57        [
58            [factor, factor * nu, 0.0],
59            [factor * nu, factor, 0.0],
60            [0.0, 0.0, g],
61        ]
62    }
63}
64
65/// Plane strain condition (ε_zz = γ_xz = γ_yz = 0).
66/// Thick structures or constrained in z-direction.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PlaneStrain {
69    pub material: HookeIsotropic,
70}
71
72impl PlaneStrain {
73    pub fn new(material: HookeIsotropic) -> Self {
74        Self { material }
75    }
76
77    /// 2D stress → 2D strain under plane strain.
78    pub fn strain_from_stress_2d(&self, sxx: f64, syy: f64, txy: f64) -> (f64, f64, f64) {
79        let e = self.material.youngs_modulus;
80        let nu = self.material.poissons_ratio;
81        let g = self.material.shear_modulus();
82        let factor = (1.0 + nu) * (1.0 - 2.0 * nu);
83        let exx = ((1.0 - nu) * sxx - nu * syy) / (e * factor / (1.0 + nu));
84        let eyy = ((1.0 - nu) * syy - nu * sxx) / (e * factor / (1.0 + nu));
85        let gxy = txy / g;
86        (exx, eyy, gxy)
87    }
88
89    /// 2D strain → 2D stress under plane strain.
90    pub fn stress_from_strain_2d(&self, exx: f64, eyy: f64, gxy: f64) -> (f64, f64, f64) {
91        let e = self.material.youngs_modulus;
92        let nu = self.material.poissons_ratio;
93        let factor = e / ((1.0 + nu) * (1.0 - 2.0 * nu));
94        let sxx = factor * ((1.0 - nu) * exx + nu * eyy);
95        let syy = factor * (nu * exx + (1.0 - nu) * eyy);
96        let txy = self.material.shear_modulus() * gxy;
97        (sxx, syy, txy)
98    }
99
100    /// Out-of-plane stress under plane strain: σ_zz = ν(σ_xx + σ_yy)
101    pub fn out_of_plane_stress(&self, sxx: f64, syy: f64) -> f64 {
102        self.material.poissons_ratio * (sxx + syy)
103    }
104
105    /// Plane strain stiffness matrix (3×3).
106    pub fn stiffness_matrix_2d(&self) -> [[f64; 3]; 3] {
107        let e = self.material.youngs_modulus;
108        let nu = self.material.poissons_ratio;
109        let factor = e / ((1.0 + nu) * (1.0 - 2.0 * nu));
110        let g = e / (2.0 * (1.0 + nu));
111        [
112            [factor * (1.0 - nu), factor * nu, 0.0],
113            [factor * nu, factor * (1.0 - nu), 0.0],
114            [0.0, 0.0, g],
115        ]
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use approx::assert_relative_eq;
123
124    #[test]
125    fn test_plane_stress_roundtrip() {
126        let mat = HookeIsotropic::new(200e9, 0.3);
127        let ps = PlaneStress::new(mat);
128        let (sxx, syy, txy) = (100e6, 50e6, 20e6);
129        let (exx, eyy, gxy) = ps.strain_from_stress_2d(sxx, syy, txy);
130        let (sxx2, syy2, txy2) = ps.stress_from_strain_2d(exx, eyy, gxy);
131        assert_relative_eq!(sxx, sxx2, epsilon = 1e-3);
132        assert_relative_eq!(syy, syy2, epsilon = 1e-3);
133        assert_relative_eq!(txy, txy2, epsilon = 1e-3);
134    }
135
136    #[test]
137    fn test_plane_strain_out_of_plane() {
138        let mat = HookeIsotropic::new(200e9, 0.3);
139        let pe = PlaneStrain::new(mat);
140        let s_zz = pe.out_of_plane_stress(100e6, 50e6);
141        assert_relative_eq!(s_zz, 0.3 * 150e6);
142    }
143
144    #[test]
145    fn test_plane_stress_out_of_plane_strain() {
146        let mat = HookeIsotropic::new(200e9, 0.3);
147        let ps = PlaneStress::new(mat);
148        let e_zz = ps.out_of_plane_strain(100e6, 50e6);
149        assert_relative_eq!(e_zz, -0.3 * 150e6 / 200e9);
150    }
151
152    #[test]
153    fn test_plane_stress_stiffness_symmetry() {
154        let mat = HookeIsotropic::new(200e9, 0.3);
155        let ps = PlaneStress::new(mat);
156        let c = ps.stiffness_matrix_2d();
157        assert_relative_eq!(c[0][1], c[1][0]);
158    }
159}