1use crate::shapes::{GeoKind, Scratchpad};
2use crate::StrError;
3use russell_lab::math::SQRT_2;
4use russell_lab::{mat_mat_mul, mat_t_mat_mul, Matrix, Vector};
5use russell_tensor::{LinElasticity, Tensor2};
6
7pub struct AnalyticalTri3 {
9 pub area: f64,
11
12 pub bb: Matrix,
24
25 pub bbe: Matrix,
27
28 pub ll: Vec<f64>,
30
31 x0: f64,
33 x1: f64,
34 x2: f64,
35}
36
37impl AnalyticalTri3 {
38 pub fn new(pad: &Scratchpad) -> Self {
40 assert_eq!(pad.kind, GeoKind::Tri3);
41
42 let (x0, y0) = (pad.xxt.get(0, 0), pad.xxt.get(1, 0));
44 let (x1, y1) = (pad.xxt.get(0, 1), pad.xxt.get(1, 1));
45 let (x2, y2) = (pad.xxt.get(0, 2), pad.xxt.get(1, 2));
46 let (a0, a1, a2) = (y1 - y2, y2 - y0, y0 - y1);
47 let (b0, b1, b2) = (x2 - x1, x0 - x2, x1 - x0);
48 let (f0, f1, f2) = (x1 * y2 - x2 * y1, x2 * y0 - x0 * y2, x0 * y1 - x1 * y0);
49
50 let area = (f0 + f1 + f2) / 2.0;
52
53 let r = 2.0 * area;
55 let s = r * SQRT_2;
56
57 #[rustfmt::skip]
59 let bb = Matrix::from(&[
60 [a0/r, b0/r],
61 [a1/r, b1/r],
62 [a2/r, b2/r],
63 ]);
64
65 #[rustfmt::skip]
67 let bbe = Matrix::from(&[
68 [a0/r, 0.0, a1/r, 0.0, a2/r, 0.0],
69 [ 0.0, b0/r, 0.0, b1/r, 0.0, b2/r],
70 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
71 [b0/s, a0/s, b1/s, a1/s, b2/s, a2/s],
72 ]);
73
74 let ll = vec![
76 f64::sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1)), f64::sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)), f64::sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)), ];
80
81 AnalyticalTri3 {
83 area,
84 bb,
85 bbe,
86 ll,
87 x0,
88 x1,
89 x2,
90 }
91 }
92
93 pub fn vec_01_ns(&self, s: f64, axisymmetric: bool) -> Vector {
95 if axisymmetric {
96 let c = s * self.area / 12.0;
97 Vector::from(&[
98 c * (2.0 * self.x0 + self.x1 + self.x2),
99 c * (self.x0 + 2.0 * self.x1 + self.x2),
100 c * (self.x0 + self.x1 + 2.0 * self.x2),
101 ])
102 } else {
103 let c = s * self.area / 3.0;
104 Vector::from(&[c, c, c])
105 }
106 }
107
108 pub fn vec_01_ns_bry(&self, side: usize, s: f64, axisymmetric: bool) -> Vector {
112 if axisymmetric {
113 let (a0, a1) = match side {
114 0 => (self.x1, self.x0),
115 1 => (self.x2, self.x1),
116 2 => (self.x0, self.x2),
117 _ => panic!("invalid side"),
118 };
119 let c = s * self.ll[side] / 6.0;
120 Vector::from(&[c * (2.0 * a0 + a1), c * (a0 + 2.0 * a1)])
121 } else {
122 let c = s * self.ll[side] / 2.0;
123 Vector::from(&[c, c])
124 }
125 }
126
127 pub fn vec_02_nv(&self, v0: f64, v1: f64) -> Vector {
144 Vector::from(&[
145 v0 * self.area / 3.0,
146 v1 * self.area / 3.0,
147 v0 * self.area / 3.0,
148 v1 * self.area / 3.0,
149 v0 * self.area / 3.0,
150 v1 * self.area / 3.0,
151 ])
152 }
153
154 pub fn vec_03_bv(&self, w0: f64, w1: f64) -> Vector {
162 Vector::from(&[
163 (w0 * self.bb.get(0, 0) + w1 * self.bb.get(0, 1)) * self.area,
164 (w0 * self.bb.get(1, 0) + w1 * self.bb.get(1, 1)) * self.area,
165 (w0 * self.bb.get(2, 0) + w1 * self.bb.get(2, 1)) * self.area,
166 ])
167 }
168
169 #[rustfmt::skip]
182 pub fn vec_03_bv_bilinear(&self, pad: &Scratchpad) -> Vector {
183 let (x0, x1, x2) = (pad.xxt.get(0,0), pad.xxt.get(0,1), pad.xxt.get(0,2));
184 let (y0, y1, y2) = (pad.xxt.get(1,0), pad.xxt.get(1,1), pad.xxt.get(1,2));
185 Vector::from(&[
186 ((x0 + x1 + x2) * self.bb.get(0,0) + (y0 + y1 + y2) * self.bb.get(0,1)) * self.area / 3.0,
187 ((x0 + x1 + x2) * self.bb.get(1,0) + (y0 + y1 + y2) * self.bb.get(1,1)) * self.area / 3.0,
188 ((x0 + x1 + x2) * self.bb.get(2,0) + (y0 + y1 + y2) * self.bb.get(2,1)) * self.area / 3.0,
189 ])
190 }
191
192 #[rustfmt::skip]
194 pub fn vec_04_bt(&self, tt: &Tensor2, axisymmetric: bool) -> Vector {
195 let (x0, x1, x2) = (self.x0, self.x1, self.x2);
196 let (b00, b01) = (self.bb.get(0,0), self.bb.get(0,1));
197 let (b10, b11) = (self.bb.get(1,0), self.bb.get(1,1));
198 let (b20, b21) = (self.bb.get(2,0), self.bb.get(2,1));
199 let (t0, t1, t2, t3) = (tt.vector()[0], tt.vector()[1], tt.vector()[2], tt.vector()[3]);
200 if axisymmetric {
201 let c = self.area / 3.0;
202 Vector::from(&[
203 c * (b00 * t0 + b01 * t3 / SQRT_2) * (x0 + x1 + x2) + c * t2,
204 c * (b01 * t1 + b00 * t3 / SQRT_2) * (x0 + x1 + x2),
205 c * (b10 * t0 + b11 * t3 / SQRT_2) * (x0 + x1 + x2) + c * t2,
206 c * (b11 * t1 + b10 * t3 / SQRT_2) * (x0 + x1 + x2),
207 c * (b20 * t0 + b21 * t3 / SQRT_2) * (x0 + x1 + x2) + c * t2,
208 c * (b21 * t1 + b20 * t3 / SQRT_2) * (x0 + x1 + x2),
209 ])
210 } else {
211 let c = self.area;
212 Vector::from(&[
213 c * (b00 * t0 + b01 * t3 / SQRT_2),
214 c * (b01 * t1 + b00 * t3 / SQRT_2),
215 c * (b10 * t0 + b11 * t3 / SQRT_2),
216 c * (b11 * t1 + b10 * t3 / SQRT_2),
217 c * (b20 * t0 + b21 * t3 / SQRT_2),
218 c * (b21 * t1 + b20 * t3 / SQRT_2),
219 ])
220 }
221 }
222
223 pub fn mat_01_nsn(&self, s: f64, th: f64) -> Matrix {
225 let c = th * s * self.area / 12.0;
226 Matrix::from(&[[2.0 * c, c, c], [c, 2.0 * c, c], [c, c, 2.0 * c]])
227 }
228
229 pub fn mat_01_nsn_bry(&self, side: usize, s: f64, axisymmetric: bool) -> Matrix {
233 if axisymmetric {
234 let (a0, a1) = match side {
235 0 => (self.x1, self.x0),
236 1 => (self.x2, self.x1),
237 2 => (self.x0, self.x2),
238 _ => panic!("invalid side"),
239 };
240 let c = s * self.ll[side] / 12.0;
241 Matrix::from(&[
242 [c * (3.0 * a0 + a1), c * (a0 + a1)],
243 [c * (a0 + a1), c * (a0 + 3.0 * a1)],
244 ])
245 } else {
246 let c = s * self.ll[side] / 6.0;
247 Matrix::from(&[[2.0 * c, c], [c, 2.0 * c]])
248 }
249 }
250
251 #[rustfmt::skip]
253 pub fn mat_02_bvn(&self, v0: f64, v1: f64) -> Matrix {
254 let aa = self.area;
255 let g = &self.bb;
256 Matrix::from(&[
257 [aa*(g.get(0,0)*v0 + g.get(0,1)*v1)/3.0, aa*(g.get(0,0)*v0 + g.get(0,1)*v1)/3.0, aa*(g.get(0,0)*v0 + g.get(0,1)*v1)/3.0],
258 [aa*(g.get(1,0)*v0 + g.get(1,1)*v1)/3.0, aa*(g.get(1,0)*v0 + g.get(1,1)*v1)/3.0, aa*(g.get(1,0)*v0 + g.get(1,1)*v1)/3.0],
259 [aa*(g.get(2,0)*v0 + g.get(2,1)*v1)/3.0, aa*(g.get(2,0)*v0 + g.get(2,1)*v1)/3.0, aa*(g.get(2,0)*v0 + g.get(2,1)*v1)/3.0],
260 ])
261 }
262
263 #[rustfmt::skip]
265 pub fn mat_02_bvn_bilinear(&self, pad: &Scratchpad) -> Matrix {
266 let aa = self.area;
267 let (b00, b01) = (self.bb.get(0,0), self.bb.get(0,1));
268 let (b10, b11) = (self.bb.get(1,0), self.bb.get(1,1));
269 let (b20, b21) = (self.bb.get(2,0), self.bb.get(2,1));
270 let (x0, y0) = (pad.xxt.get(0,0), pad.xxt.get(1,0));
271 let (x1, y1) = (pad.xxt.get(0,1), pad.xxt.get(1,1));
272 let (x2, y2) = (pad.xxt.get(0,2), pad.xxt.get(1,2));
273 Matrix::from(&[
274 [(aa*(2.0*b00*x0 + b00*x1 + b00*x2 + 2.0*b01*y0 + b01*y1 + b01*y2))/12.0,(aa*(b00*x0 + 2.0*b00*x1 + b00*x2 + b01*y0 + 2.0*b01*y1 + b01*y2))/12.0, (aa*(b00*x0 + b00*x1 + 2.0*b00*x2 + b01*y0 + b01*y1 + 2.0*b01*y2))/12.0],
275 [(aa*(2.0*b10*x0 + b10*x1 + b10*x2 + 2.0*b11*y0 + b11*y1 + b11*y2))/12.0,(aa*(b10*x0 + 2.0*b10*x1 + b10*x2 + b11*y0 + 2.0*b11*y1 + b11*y2))/12.0, (aa*(b10*x0 + b10*x1 + 2.0*b10*x2 + b11*y0 + b11*y1 + 2.0*b11*y2))/12.0],
276 [(aa*(2.0*b20*x0 + b20*x1 + b20*x2 + 2.0*b21*y0 + b21*y1 + b21*y2))/12.0,(aa*(b20*x0 + 2.0*b20*x1 + b20*x2 + b21*y0 + 2.0*b21*y1 + b21*y2))/12.0, (aa*(b20*x0 + b20*x1 + 2.0*b20*x2 + b21*y0 + b21*y1 + 2.0*b21*y2))/12.0],
277 ])
278 }
279
280 pub fn mat_03_btb(&self, kx: f64, ky: f64, axisymmetric: bool) -> Matrix {
282 let c = if axisymmetric {
283 self.area * (self.x0 + self.x1 + self.x2) / 3.0
284 } else {
285 self.area
286 };
287 let (b00, b01) = (self.bb.get(0, 0), self.bb.get(0, 1));
288 let (b10, b11) = (self.bb.get(1, 0), self.bb.get(1, 1));
289 let (b20, b21) = (self.bb.get(2, 0), self.bb.get(2, 1));
290 let k00 = c * (b00 * b00 * kx + b01 * b01 * ky);
291 let k11 = c * (b10 * b10 * kx + b11 * b11 * ky);
292 let k01 = c * (b00 * b10 * kx + b01 * b11 * ky);
293 let k12 = c * (b10 * b20 * kx + b11 * b21 * ky);
294 let k02 = c * (b00 * b20 * kx + b01 * b21 * ky);
295 let k22 = c * (b20 * b20 * kx + b21 * b21 * ky);
296 Matrix::from(&[[k00, k01, k02], [k01, k11, k12], [k02, k12, k22]])
297 }
298
299 pub fn mat_08_ntn(&self, rho: f64, th: f64) -> Matrix {
301 let c = th * rho * self.area / 12.0;
302 Matrix::from(&[
303 [c * 2.0, c * 0.0, c * 1.0, c * 0.0, c * 1.0, c * 0.0],
304 [c * 0.0, c * 2.0, c * 0.0, c * 1.0, c * 0.0, c * 1.0],
305 [c * 1.0, c * 0.0, c * 2.0, c * 0.0, c * 1.0, c * 0.0],
306 [c * 0.0, c * 1.0, c * 0.0, c * 2.0, c * 0.0, c * 1.0],
307 [c * 1.0, c * 0.0, c * 1.0, c * 0.0, c * 2.0, c * 0.0],
308 [c * 0.0, c * 1.0, c * 0.0, c * 1.0, c * 0.0, c * 2.0],
309 ])
310 }
311
312 #[rustfmt::skip]
314 pub fn mat_09_nvb(&self, v0: f64, v1: f64) -> Matrix {
315 let (b00, b01) = (self.bb.get(0,0), self.bb.get(0,1));
316 let (b10, b11) = (self.bb.get(1,0), self.bb.get(1,1));
317 let (b20, b21) = (self.bb.get(2,0), self.bb.get(2,1));
318 let c = self.area / 3.0;
319 Matrix::from(&[
320 [c*b00*v0, c*b01*v0, c*b10*v0, c*b11*v0, c*b20*v0, c*b21*v0],
321 [c*b00*v1, c*b01*v1, c*b10*v1, c*b11*v1, c*b20*v1, c*b21*v1],
322 [c*b00*v0, c*b01*v0, c*b10*v0, c*b11*v0, c*b20*v0, c*b21*v0],
323 [c*b00*v1, c*b01*v1, c*b10*v1, c*b11*v1, c*b20*v1, c*b21*v1],
324 [c*b00*v0, c*b01*v0, c*b10*v0, c*b11*v0, c*b20*v0, c*b21*v0],
325 [c*b00*v1, c*b01*v1, c*b10*v1, c*b11*v1, c*b20*v1, c*b21*v1],
326 ])
327 }
328
329 pub fn mat_10_bdb(&self, young: f64, poisson: f64, plane_stress: bool, th: f64) -> Result<Matrix, StrError> {
337 let ela = LinElasticity::new(young, poisson, true, plane_stress);
338 let dd = ela.get_modulus();
339 let dim_dd = 4;
340 let dim_kk = 6;
341 let mut bb_t_dd = Matrix::new(dim_kk, dim_dd);
342 let mut kk = Matrix::new(dim_kk, dim_kk);
343 mat_t_mat_mul(&mut bb_t_dd, 1.0, &self.bbe, dd.matrix(), 0.0).unwrap();
344 mat_mat_mul(&mut kk, th * self.area, &bb_t_dd, &self.bbe, 0.0).unwrap();
345 Ok(kk)
346 }
347}
348
349#[cfg(test)]
352mod tests {
353 use super::AnalyticalTri3;
354 use crate::shapes::{GeoKind, Scratchpad};
355 use russell_lab::approx_eq;
356
357 #[test]
358 fn new_works() {
359 let space_ndim = 2;
362 let mut pad = Scratchpad::new(space_ndim, GeoKind::Tri3).unwrap();
363 pad.set_xx(0, 0, 0.0);
364 pad.set_xx(0, 1, 0.0);
365 pad.set_xx(1, 0, 0.2);
366 pad.set_xx(1, 1, 0.0);
367 pad.set_xx(2, 0, 0.1);
368 pad.set_xx(2, 1, 0.1);
369 let ana = AnalyticalTri3::new(&pad);
370 approx_eq(ana.area, 0.01, 1e-15);
371 assert_eq!(
375 format!("{:.2}", ana.bb),
376 "┌ ┐\n\
377 │ -5.00 -5.00 │\n\
378 │ 5.00 -5.00 │\n\
379 │ 0.00 10.00 │\n\
380 └ ┘"
381 );
382 }
383}