1use super::CiarletElement;
4use crate::map::ContravariantPiolaMap;
5use crate::math::orthogonalise_3;
6use crate::polynomials::{legendre_shape, polynomial_count, tabulate_legendre_polynomials};
7use crate::quadrature::gauss_jacobi_rule;
8use crate::reference_cell;
9use crate::traits::ElementFamily;
10use crate::types::{Continuity, ReferenceCellType};
11use itertools::izip;
12use rlst::dense::linalg::lapack::interface::{getrf::Getrf, getri::Getri};
13use rlst::{DynArray, RlstScalar, SliceArray, rlst_dynamic_array};
14use std::marker::PhantomData;
15
16fn create_simplex<T: RlstScalar + Getrf + Getri, TGeo: RlstScalar>(
17 cell_type: ReferenceCellType,
18 degree: usize,
19 continuity: Continuity,
20) -> CiarletElement<T, ContravariantPiolaMap, TGeo> {
21 if cell_type != ReferenceCellType::Triangle && cell_type != ReferenceCellType::Tetrahedron {
22 panic!("Invalid cell: {cell_type:?}");
23 }
24
25 if degree < 1 {
26 panic!("Degree must be at least 1");
27 }
28
29 let tdim = reference_cell::dim(cell_type);
30 let facet_type = if tdim == 2 {
31 ReferenceCellType::Interval
32 } else {
33 ReferenceCellType::Triangle
34 };
35 let pdim_minus1 = polynomial_count(cell_type, degree - 1);
36 let pdim_facet_minus1 = polynomial_count(facet_type, degree - 1);
37 let pdim_minus2 = if degree < 2 {
38 0
39 } else {
40 polynomial_count(cell_type, degree - 2)
41 };
42
43 let cell_q = gauss_jacobi_rule(cell_type, 2 * degree).unwrap();
44 let pts_t = cell_q
45 .points
46 .iter()
47 .map(|i| TGeo::from(*i).unwrap())
48 .collect::<Vec<_>>();
49 let pts = SliceArray::<TGeo, 2>::from_shape(&pts_t, [tdim, cell_q.npoints]);
50
51 let mut phi = DynArray::<T, 3>::from_shape(legendre_shape(cell_type, &pts, degree, 0));
52 tabulate_legendre_polynomials(cell_type, &pts, degree, 0, &mut phi);
53
54 let pdim = phi.shape()[1];
55
56 let mut wcoeffs = rlst_dynamic_array!(T, [pdim_minus1 * tdim + pdim_facet_minus1, tdim, pdim]);
57
58 for i in 0..tdim {
60 for j in 0..pdim_minus1 {
61 *wcoeffs.get_mut([i * pdim_minus1 + j, i, j]).unwrap() = T::from(1.0).unwrap();
62 }
63 }
64
65 for i in 0..pdim_facet_minus1 {
67 for k in pdim_minus1..pdim {
68 for j in 0..tdim {
69 *wcoeffs.get_mut([pdim_minus1 * tdim + i, j, k]).unwrap() = cell_q
70 .weights
71 .iter()
72 .enumerate()
73 .map(|(w_i, wt)| {
74 T::from(*wt).unwrap()
75 * phi[[0, pdim_minus2 + i, w_i]]
76 * T::from(pts[[j, w_i]]).unwrap()
77 * phi[[0, k, w_i]]
78 })
79 .sum();
80 }
81 }
82 }
83
84 orthogonalise_3(&mut wcoeffs, pdim_minus1 * tdim);
85
86 let mut x = [vec![], vec![], vec![], vec![]];
87 let mut m = [vec![], vec![], vec![], vec![]];
88
89 let entity_counts = reference_cell::entity_counts(cell_type);
90 let vertices = reference_cell::vertices::<TGeo>(cell_type);
91
92 for d in 0..tdim - 1 {
93 for _ in 0..entity_counts[d] {
94 x[d].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
95 m[d].push(rlst_dynamic_array!(T, [0, tdim, 0]));
96 }
97 }
98
99 let facet_q = gauss_jacobi_rule(facet_type, 2 * degree - 1).unwrap();
101 let facet_pts_t = facet_q
102 .points
103 .iter()
104 .map(|i| TGeo::from(*i).unwrap())
105 .collect::<Vec<_>>();
106 let facet_pts = SliceArray::<TGeo, 2>::from_shape(&facet_pts_t, [tdim - 1, facet_q.npoints]);
107
108 let mut facet_phi =
109 DynArray::<T, 3>::from_shape(legendre_shape(facet_type, &facet_pts, degree - 1, 0));
110 tabulate_legendre_polynomials(facet_type, &facet_pts, degree - 1, 0, &mut facet_phi);
111
112 for (facet, normal) in izip!(
113 if tdim == 2 {
114 reference_cell::edges(cell_type)
115 } else {
116 reference_cell::faces(cell_type)
117 },
118 reference_cell::facet_normals::<TGeo>(cell_type),
119 ) {
120 let mut pts = rlst_dynamic_array!(TGeo, [tdim, facet_q.npoints]);
121 let mut mat = rlst_dynamic_array!(T, [pdim_facet_minus1, tdim, facet_q.npoints]);
122
123 for (w_i, wt) in facet_q.weights.iter().enumerate() {
124 for i in 0..tdim {
125 pts[[i, w_i]] = vertices[facet[0]][i]
126 + izip!(
127 facet.iter().skip(1),
128 &facet_q.points[w_i * (tdim - 1)..(w_i + 1) * (tdim - 1)]
129 )
130 .map(|(v_i, pt)| {
131 (vertices[*v_i][i] - vertices[facet[0]][i]) * TGeo::from(*pt).unwrap()
132 })
133 .sum();
134
135 for j in 0..pdim_facet_minus1 {
136 mat[[j, i, w_i]] = T::from(*wt).unwrap()
137 * facet_phi[[0, j, w_i]]
138 * T::from(normal[i]).unwrap();
139 }
140 }
141 }
142
143 x[tdim - 1].push(pts);
144 m[tdim - 1].push(mat);
145 }
146
147 if degree == 1 {
148 for _ in 0..entity_counts[tdim] {
149 x[tdim].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
150 m[tdim].push(rlst_dynamic_array!(T, [0, tdim, 0]))
151 }
152 } else {
153 let internal_q = gauss_jacobi_rule(cell_type, 2 * degree - 2).unwrap();
154 let mut pts = rlst_dynamic_array!(TGeo, [tdim, internal_q.npoints]);
155 for p in 0..internal_q.npoints {
156 for d in 0..tdim {
157 pts[[d, p]] = TGeo::from(internal_q.points[tdim * p + d]).unwrap()
158 }
159 }
160
161 let mut internal_phi =
162 DynArray::<T, 3>::from_shape(legendre_shape(cell_type, &pts, degree - 2, 0));
163 tabulate_legendre_polynomials(cell_type, &pts, degree - 2, 0, &mut internal_phi);
164
165 let mut mat = rlst_dynamic_array!(T, [tdim * pdim_minus2, tdim, internal_q.npoints]);
166 for (w_i, wt) in internal_q.weights.iter().enumerate() {
167 for i in 0..tdim {
168 for j in 0..pdim_minus2 {
169 mat[[j + pdim_minus2 * i, i, w_i]] =
170 T::from(*wt).unwrap() * internal_phi[[0, j, w_i]];
171 }
172 }
173 }
174
175 x[tdim].push(pts);
176 m[tdim].push(mat);
177 }
178
179 CiarletElement::create(
180 "Raviart-Thomas".to_string(),
181 cell_type,
182 degree,
183 vec![tdim],
184 wcoeffs,
185 x,
186 m,
187 continuity,
188 degree,
189 ContravariantPiolaMap {},
190 )
191}
192
193fn create_tp<T: RlstScalar + Getrf + Getri, TGeo: RlstScalar>(
194 cell_type: ReferenceCellType,
195 degree: usize,
196 continuity: Continuity,
197) -> CiarletElement<T, ContravariantPiolaMap, TGeo> {
198 if cell_type != ReferenceCellType::Quadrilateral && cell_type != ReferenceCellType::Hexahedron {
199 panic!("Invalid cell: {cell_type:?}");
200 }
201
202 if degree < 1 {
203 panic!("Degree must be at least 1");
204 }
205
206 let tdim = reference_cell::dim(cell_type);
207 let cell_q = gauss_jacobi_rule(cell_type, 2 * degree).unwrap();
208 let pts_t = cell_q
209 .points
210 .iter()
211 .map(|i| TGeo::from(*i).unwrap())
212 .collect::<Vec<_>>();
213 let pts = SliceArray::<TGeo, 2>::from_shape(&pts_t, [tdim, cell_q.npoints]);
214
215 let mut phi = DynArray::<T, 3>::from_shape(legendre_shape(cell_type, &pts, degree, 0));
216 tabulate_legendre_polynomials(cell_type, &pts, degree, 0, &mut phi);
217
218 let pdim = phi.shape()[1];
219
220 let pdim_edge = polynomial_count(ReferenceCellType::Interval, degree);
221 let pdim_edge_minus1 = polynomial_count(ReferenceCellType::Interval, degree - 1);
222 let pdim_edge_minus2 = if degree < 2 {
223 0
224 } else {
225 polynomial_count(ReferenceCellType::Interval, degree - 2)
226 };
227
228 let facet_type = if tdim == 2 {
229 ReferenceCellType::Interval
230 } else {
231 ReferenceCellType::Quadrilateral
232 };
233 let pdim_facet_minus1 = polynomial_count(facet_type, degree - 1);
234 let pdim_internal = tdim * pdim_edge_minus2 * pdim_edge_minus1.pow(tdim as u32 - 1);
235
236 let entity_counts = reference_cell::entity_counts(cell_type);
237
238 let wdim = entity_counts[tdim - 1] * pdim_facet_minus1 + entity_counts[tdim] * pdim_internal;
239 let mut wcoeffs = rlst_dynamic_array!(T, [wdim, tdim, pdim]);
240
241 if tdim == 2 {
243 for i in 0..pdim_edge_minus1 {
244 for j in 0..pdim_edge {
245 *wcoeffs
246 .get_mut([j * pdim_edge_minus1 + i, 0, j * pdim_edge + i])
247 .unwrap() = T::from(1.0).unwrap();
248 *wcoeffs
249 .get_mut([
250 pdim_edge_minus1 * pdim_edge + i * pdim_edge + j,
251 1,
252 i * pdim_edge + j,
253 ])
254 .unwrap() = T::from(1.0).unwrap();
255 }
256 }
257 } else {
258 for i in 0..pdim_edge_minus1 {
259 for j in 0..pdim_edge_minus1 {
260 for k in 0..pdim_edge {
261 *wcoeffs
262 .get_mut([
263 k * pdim_edge_minus1.pow(2) + j * pdim_edge_minus1 + i,
264 0,
265 k * pdim_edge.pow(2) + j * pdim_edge + i,
266 ])
267 .unwrap() = T::from(1.0).unwrap();
268 *wcoeffs
269 .get_mut([
270 pdim_edge * pdim_edge_minus1.pow(2)
271 + i * pdim_edge * pdim_edge_minus1
272 + k * pdim_edge_minus1
273 + j,
274 1,
275 i * pdim_edge.pow(2) + k * pdim_edge + j,
276 ])
277 .unwrap() = T::from(1.0).unwrap();
278 *wcoeffs
279 .get_mut([
280 pdim_edge * pdim_edge_minus1.pow(2) * 2
281 + j * pdim_edge * pdim_edge_minus1
282 + i * pdim_edge
283 + k,
284 2,
285 j * pdim_edge.pow(2) + i * pdim_edge + k,
286 ])
287 .unwrap() = T::from(1.0).unwrap();
288 }
289 }
290 }
291 }
292
293 let mut x = [vec![], vec![], vec![], vec![]];
294 let mut m = [vec![], vec![], vec![], vec![]];
295
296 let vertices = reference_cell::vertices::<TGeo>(cell_type);
297
298 for d in 0..tdim - 1 {
299 for _ in 0..entity_counts[d] {
300 x[d].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
301 m[d].push(rlst_dynamic_array!(T, [0, tdim, 0]));
302 }
303 }
304 let facet_q = gauss_jacobi_rule(facet_type, 2 * degree - 1).unwrap();
306 let facet_pts_t = facet_q
307 .points
308 .iter()
309 .map(|i| TGeo::from(*i).unwrap())
310 .collect::<Vec<_>>();
311 let facet_pts = SliceArray::<TGeo, 2>::from_shape(&facet_pts_t, [tdim - 1, facet_q.npoints]);
312
313 let mut facet_phi =
314 DynArray::<T, 3>::from_shape(legendre_shape(facet_type, &facet_pts, degree - 1, 0));
315 tabulate_legendre_polynomials(facet_type, &facet_pts, degree - 1, 0, &mut facet_phi);
316
317 for (facet, normal) in izip!(
318 reference_cell::facets(cell_type),
319 reference_cell::facet_normals::<TGeo>(cell_type),
320 ) {
321 let mut pts = rlst_dynamic_array!(TGeo, [tdim, facet_q.npoints]);
322 let mut mat = rlst_dynamic_array!(T, [pdim_facet_minus1, tdim, facet_q.npoints]);
323
324 for (w_i, wt) in facet_q.weights.iter().enumerate() {
325 for d in 0..tdim {
326 pts[[d, w_i]] = vertices[facet[0]][d]
327 + (0..tdim - 1)
328 .map(|x| {
329 (vertices[facet[usize::pow(2, x as u32)]][d] - vertices[facet[0]][d])
330 * facet_pts[[x, w_i]]
331 })
332 .sum::<TGeo>();
333 for j in 0..facet_phi.shape()[1] {
334 mat[[j, d, w_i]] = T::from(*wt).unwrap()
335 * facet_phi[[0, j, w_i]]
336 * T::from(normal[d]).unwrap();
337 }
338 }
339 }
340
341 x[tdim - 1].push(pts);
342 m[tdim - 1].push(mat);
343 }
344
345 if degree == 1 {
347 x[tdim].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
348 m[tdim].push(rlst_dynamic_array!(T, [0, tdim, 0]))
349 } else if tdim == 2 {
350 let face_q = gauss_jacobi_rule(ReferenceCellType::Quadrilateral, 2 * degree - 1).unwrap();
351 let face_pts_t = face_q
352 .points
353 .iter()
354 .map(|i| TGeo::from(*i).unwrap())
355 .collect::<Vec<_>>();
356 let face_pts = SliceArray::<TGeo, 2>::from_shape(&face_pts_t, [2, face_q.npoints]);
357
358 let mut face_phi = DynArray::<T, 3>::from_shape(legendre_shape(
359 ReferenceCellType::Quadrilateral,
360 &face_pts,
361 degree - 1,
362 0,
363 ));
364 tabulate_legendre_polynomials(
365 ReferenceCellType::Quadrilateral,
366 &face_pts,
367 degree - 1,
368 0,
369 &mut face_phi,
370 );
371
372 let mut pts = rlst_dynamic_array!(TGeo, [tdim, face_q.npoints]);
373 let mdim = 2 * pdim_edge_minus2 * pdim_edge_minus1;
374 let mut mat = rlst_dynamic_array!(T, [mdim, tdim, face_q.npoints]);
375
376 for (w_i, wt) in face_q.weights.iter().enumerate() {
377 for i in 0..tdim {
378 pts[[i, w_i]] = face_pts[[i, w_i]];
379 }
380 for i in 0..pdim_edge_minus2 {
381 for j in 0..pdim_edge_minus1 {
382 let index = 2 * (i * pdim_edge_minus1 + j);
383 mat[[index, 0, w_i]] =
384 T::from(*wt).unwrap() * face_phi[[0, i * pdim_edge_minus1 + j, w_i]];
385 mat[[index + 1, 1, w_i]] =
386 T::from(*wt).unwrap() * face_phi[[0, j * pdim_edge_minus1 + i, w_i]];
387 }
388 }
389 }
390 x[2].push(pts);
391 m[2].push(mat);
392 } else {
393 let interior_q = gauss_jacobi_rule(ReferenceCellType::Hexahedron, 2 * degree - 1).unwrap();
394 let interior_pts_t = interior_q
395 .points
396 .iter()
397 .map(|i| TGeo::from(*i).unwrap())
398 .collect::<Vec<_>>();
399 let interior_pts =
400 SliceArray::<TGeo, 2>::from_shape(&interior_pts_t, [3, interior_q.npoints]);
401
402 let mut interior_phi = DynArray::<T, 3>::from_shape(legendre_shape(
403 ReferenceCellType::Hexahedron,
404 &interior_pts,
405 degree - 1,
406 0,
407 ));
408 tabulate_legendre_polynomials(
409 ReferenceCellType::Hexahedron,
410 &interior_pts,
411 degree - 1,
412 0,
413 &mut interior_phi,
414 );
415
416 let mut pts = rlst_dynamic_array!(TGeo, [tdim, interior_q.npoints]);
417 let mdim = 3 * pdim_edge_minus1.pow(2) * pdim_edge_minus2;
418 let mut mat = rlst_dynamic_array!(T, [mdim, tdim, interior_q.npoints]);
419
420 for (w_i, wt) in interior_q.weights.iter().enumerate() {
421 for i in 0..tdim {
422 pts[[i, w_i]] = interior_pts[[i, w_i]];
423 }
424 for i in 0..pdim_edge_minus2 {
425 for j in 0..pdim_edge_minus1 {
426 for k in 0..pdim_edge_minus1 {
427 let index = 3 * (i * pdim_edge_minus1.pow(2) + j * pdim_edge_minus1 + k);
428 mat[[index, 0, w_i]] = T::from(*wt).unwrap()
429 * interior_phi[[
430 0,
431 i * pdim_edge_minus1.pow(2) + j * pdim_edge_minus1 + k,
432 w_i,
433 ]];
434 mat[[index + 1, 1, w_i]] = T::from(*wt).unwrap()
435 * interior_phi[[
436 0,
437 k * pdim_edge_minus1.pow(2) + i * pdim_edge_minus1 + j,
438 w_i,
439 ]];
440 mat[[index + 2, 2, w_i]] = T::from(*wt).unwrap()
441 * interior_phi[[
442 0,
443 j * pdim_edge_minus1.pow(2) + k * pdim_edge_minus1 + i,
444 w_i,
445 ]];
446 }
447 }
448 }
449 }
450 x[3].push(pts);
451 m[3].push(mat);
452 }
453
454 CiarletElement::create(
455 "Raviart-Thomas".to_string(),
456 cell_type,
457 degree,
458 vec![tdim],
459 wcoeffs,
460 x,
461 m,
462 continuity,
463 degree,
464 ContravariantPiolaMap {},
465 )
466}
467
468pub fn create<T: RlstScalar + Getrf + Getri, TGeo: RlstScalar>(
470 cell_type: ReferenceCellType,
471 degree: usize,
472 continuity: Continuity,
473) -> CiarletElement<T, ContravariantPiolaMap, TGeo> {
474 if cell_type == ReferenceCellType::Triangle || cell_type == ReferenceCellType::Tetrahedron {
475 create_simplex(cell_type, degree, continuity)
476 } else if cell_type == ReferenceCellType::Quadrilateral
477 || cell_type == ReferenceCellType::Hexahedron
478 {
479 create_tp(cell_type, degree, continuity)
480 } else {
481 panic!("Invalid cell: {cell_type:?}");
482 }
483}
484
485pub struct RaviartThomasElementFamily<T: RlstScalar + Getrf + Getri = f64, TGeo: RlstScalar = f64> {
490 degree: usize,
491 continuity: Continuity,
492 _t: PhantomData<T>,
493 _tgeo: PhantomData<TGeo>,
494}
495
496impl<T: RlstScalar + Getrf + Getri, TGeo: RlstScalar> RaviartThomasElementFamily<T, TGeo> {
497 pub fn new(degree: usize, continuity: Continuity) -> Self {
499 Self {
500 degree,
501 continuity,
502 _t: PhantomData,
503 _tgeo: PhantomData,
504 }
505 }
506}
507
508impl<T: RlstScalar + Getrf + Getri, TGeo: RlstScalar> ElementFamily
509 for RaviartThomasElementFamily<T, TGeo>
510{
511 type T = T;
512 type CellType = ReferenceCellType;
513 type FiniteElement = CiarletElement<T, ContravariantPiolaMap, TGeo>;
514 fn element(
515 &self,
516 cell_type: ReferenceCellType,
517 ) -> CiarletElement<T, ContravariantPiolaMap, TGeo> {
518 create::<T, TGeo>(cell_type, self.degree, self.continuity)
519 }
520}