Skip to main content

ndelement/
ciarlet.rs

1//! Ciarlet finite elements.
2//!
3//! In the Ciarlet definition, a finite element is defined to be a triple
4//! $(\mathcal{R}, \mathcal{V}, \mathcal{L})$, where:
5//!
6//! - $\mathcal{R}\subset\mathbb{R}^d$ is the reference cell,
7//! - $\mathcal{V}$ is a finite dimensional function space on $\mathcal{R}$ of dimension $n$, usually polynomials,
8//! - $\mathcal{L} = \{\ell_0,\dots, \ell_{n-1}\}$ is a basis of the dual space $\mathcal{V}^* = \set{f:\mathcal{V} -> \mathbb{R}: f\text{ is linear}}$, with
9//    each functional associated with a subentity of the reference cell $\mathcal{R}$.
10//!
11//! The basis functions $\phi_0,\dots, \phi_{n-1}$ of the finite element space are defined by
12//! $$\ell_i(\phi_j) = \begin{cases}1 &\text{if }~i = j \newline
13//! 0 &\text{otherwise}\end{cases}.
14//! $$
15//!
16//! ## Example
17//! The order 1 [Lagrange space](https://defelement.org/elements/lagrange.html) on a triangle is
18//! defined by:
19//!
20//! - $\mathcal{R}$ is a triangle with vertices $(0, 0)$, $(1, 0)$, $(0, 1)$.
21//! - $\mathcal{V} = \text{span}\set{1, x, y}$.
22//! - $\mathcal{L} = \set{\ell_0, \ell_0, \ell_1}$ with $\ell_j$ the pointwise evaluation at vertex $v_j$, and each functional associated with the relevant vertex.
23//!
24//! This gives the basis functions $\phi_0(x, y) = 1 - x - y$, $\phi_1(x, y) = x$, $\phi_2(x, y) = y$.
25//!
26//! ## References
27//! - [https://defelement.org/ciarlet.html](https://defelement.org/ciarlet.html)
28
29extern crate blas_src;
30extern crate lapack_src;
31
32use crate::math;
33use crate::polynomials::{legendre_shape, polynomial_count, tabulate_legendre_polynomials};
34use crate::reference_cell;
35use crate::traits::{FiniteElement, Map, MappedFiniteElement};
36use crate::types::{Continuity, DofTransformation, ReferenceCellType, Transformation};
37use itertools::izip;
38use rlst::RlstScalar;
39use rlst::{Array, DynArray, Inverse, MutableArrayImpl, ValueArrayImpl, rlst_dynamic_array};
40use std::collections::HashMap;
41use std::fmt::{Debug, Formatter};
42
43pub mod lagrange;
44pub mod nedelec;
45pub mod raviart_thomas;
46pub use lagrange::{LagrangeElementFamily, Variant as LagrangeVariant};
47pub use nedelec::NedelecFirstKindElementFamily;
48pub use raviart_thomas::RaviartThomasElementFamily;
49
50type EntityPoints<T> = [Vec<DynArray<T, 2>>; 4];
51type EntityWeights<T> = [Vec<DynArray<T, 3>>; 4];
52
53/// Compute the number of derivatives for a cell
54fn compute_derivative_count(nderivs: usize, cell_type: ReferenceCellType) -> usize {
55    match cell_type {
56        ReferenceCellType::Point => 0,
57        ReferenceCellType::Interval => nderivs + 1,
58        ReferenceCellType::Triangle => (nderivs + 1) * (nderivs + 2) / 2,
59        ReferenceCellType::Quadrilateral => (nderivs + 1) * (nderivs + 2) / 2,
60        ReferenceCellType::Tetrahedron => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
61        ReferenceCellType::Hexahedron => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
62        ReferenceCellType::Prism => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
63        ReferenceCellType::Pyramid => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
64    }
65}
66
67/// A Ciarlet element
68pub struct CiarletElement<T: RlstScalar, M: Map, TGeo: RlstScalar> {
69    family_name: String,
70    cell_type: ReferenceCellType,
71    degree: usize,
72    embedded_superdegree: usize,
73    value_shape: Vec<usize>,
74    value_size: usize,
75    continuity: Continuity,
76    dim: usize,
77    coefficients: DynArray<T, 3>,
78    entity_dofs: [Vec<Vec<usize>>; 4],
79    entity_closure_dofs: [Vec<Vec<usize>>; 4],
80    interpolation_points: EntityPoints<TGeo>,
81    interpolation_weights: EntityWeights<T>,
82    map: M,
83    dof_transformations: HashMap<(ReferenceCellType, Transformation), DofTransformation<T>>,
84}
85
86impl<T: RlstScalar, M: Map, TGeo: RlstScalar> Debug for CiarletElement<T, M, TGeo> {
87    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
88        f.debug_tuple("CiarletElement")
89            .field(&self.family_name)
90            .field(&self.cell_type)
91            .field(&self.degree)
92            .finish()
93    }
94}
95
96impl<T: RlstScalar, M: Map, TGeo: RlstScalar> CiarletElement<T, M, TGeo>
97where
98    DynArray<T, 2>: Inverse<Output = DynArray<T, 2>>,
99{
100    /// Create a Ciarlet element.
101    ///
102    /// The majority of users will not wish to use this directly, and should insteal call
103    /// the `create`function for one of the following special cases of a general Ciarlet element:
104    /// - [crate::ciarlet::lagrange::create]: Create a new Lagrange element.
105    /// - [crate::ciarlet::nedelec::create]: Create a new Nedelec element.
106    /// - [crate::ciarlet::raviart_thomas::create]: Create a Raviart-Thomas element.
107    #[allow(clippy::too_many_arguments)]
108    pub fn create(
109        family_name: String,
110        cell_type: ReferenceCellType,
111        degree: usize,
112        value_shape: Vec<usize>,
113        polynomial_coeffs: DynArray<T, 3>,
114        interpolation_points: EntityPoints<TGeo>,
115        interpolation_weights: EntityWeights<T>,
116        continuity: Continuity,
117        embedded_superdegree: usize,
118        map: M,
119    ) -> Self {
120        let mut dim = 0;
121        let mut npts = 0;
122
123        for emats in &interpolation_weights {
124            for mat in emats {
125                dim += mat.shape()[0];
126                npts += mat.shape()[2];
127            }
128        }
129        let tdim = reference_cell::dim(cell_type);
130
131        let mut value_size = 1;
132        for i in &value_shape {
133            value_size *= *i;
134        }
135
136        for matrices in &interpolation_weights {
137            for mat in matrices {
138                if mat.shape()[1] != value_size {
139                    panic!("Incompatible value size");
140                }
141            }
142        }
143
144        // Format the interpolation points and weights
145        let new_pts = if continuity == Continuity::Discontinuous {
146            let mut new_pts: EntityPoints<TGeo> = [vec![], vec![], vec![], vec![]];
147            let mut all_pts = rlst_dynamic_array!(TGeo, [tdim, npts]);
148            for (i, pts_i) in interpolation_points.iter().take(tdim).enumerate() {
149                for _pts in pts_i {
150                    new_pts[i].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
151                }
152            }
153            let mut col = 0;
154            for pts_i in interpolation_points.iter() {
155                for pts in pts_i {
156                    let ncols = pts.shape()[1];
157                    all_pts
158                        .r_mut()
159                        .into_subview([0, col], [tdim, ncols])
160                        .fill_from(pts);
161                    col += ncols;
162                }
163            }
164            new_pts[tdim].push(all_pts);
165            new_pts
166        } else {
167            interpolation_points
168        };
169        let new_wts = if continuity == Continuity::Discontinuous {
170            let mut new_wts = [vec![], vec![], vec![], vec![]];
171            let mut pn = 0;
172            let mut dn = 0;
173            let mut all_mat = rlst_dynamic_array!(T, [dim, value_size, npts]);
174            for (i, mi) in interpolation_weights.iter().take(tdim).enumerate() {
175                for _mat in mi {
176                    new_wts[i].push(rlst_dynamic_array!(T, [0, value_size, 0]));
177                }
178            }
179            for mi in interpolation_weights.iter() {
180                for mat in mi {
181                    let dim0 = mat.shape()[0];
182                    let dim2 = mat.shape()[2];
183                    all_mat
184                        .r_mut()
185                        .into_subview([dn, 0, pn], [dim0, value_size, dim2])
186                        .fill_from(mat);
187                    dn += dim0;
188                    pn += dim2;
189                }
190            }
191            new_wts[tdim].push(all_mat);
192            new_wts
193        } else {
194            interpolation_weights
195        };
196
197        // Compute the dual matrix
198        let pdim = polynomial_count(cell_type, embedded_superdegree);
199        let mut d_matrix = rlst_dynamic_array!(T, [value_size, pdim, dim]);
200
201        let mut dof = 0;
202        for d in 0..4 {
203            for (e, pts) in new_pts[d].iter().enumerate() {
204                if pts.shape()[1] > 0 {
205                    let mut table = rlst_dynamic_array!(T, [1, pdim, pts.shape()[1]]);
206                    tabulate_legendre_polynomials(
207                        cell_type,
208                        pts,
209                        embedded_superdegree,
210                        0,
211                        &mut table,
212                    );
213                    let mat = &new_wts[d][e];
214                    for i in 0..mat.shape()[0] {
215                        for l in 0..pdim {
216                            for j in 0..value_size {
217                                // d_matrix[j, l, dof + i] = inner(mat[i, j, :], table[0, l, :])
218                                *d_matrix.get_mut([j, l, dof + i]).unwrap() = mat
219                                    .r()
220                                    .slice::<2>(0, i)
221                                    .slice(0, j)
222                                    .inner(&table.r().slice::<2>(0, 0).slice(0, l))
223                                    .unwrap();
224                            }
225                        }
226                    }
227                    dof += mat.shape()[0];
228                }
229            }
230        }
231
232        // Compute the coefficients that define the basis functions
233        let mut dual_mat = rlst::rlst_dynamic_array!(T, [dim, dim]);
234
235        for i in 0..dim {
236            for j in 0..dim {
237                *dual_mat.get_mut([i, j]).unwrap() = (0..value_size)
238                    .map(|k| {
239                        (0..pdim)
240                            .map(|l| {
241                                *polynomial_coeffs.get([i, k, l]).unwrap()
242                                    * *d_matrix.get([k, l, j]).unwrap()
243                            })
244                            .sum::<T>()
245                    })
246                    .sum::<T>();
247            }
248        }
249
250        let inverse = dual_mat.inverse().unwrap();
251
252        let mut coefficients = rlst_dynamic_array!(T, [dim, value_size, pdim]);
253        for i in 0..dim {
254            for j in 0..value_size {
255                for k in 0..pdim {
256                    // coefficients[i, j, k] = inner(inverse[i, :], polynomial_coeffs[:, j, k])
257                    *coefficients.get_mut([i, j, k]).unwrap() = inverse
258                        .r()
259                        .slice::<1>(0, i)
260                        .inner(&polynomial_coeffs.r().slice::<2>(1, j).slice(1, k))
261                        .unwrap();
262                }
263            }
264        }
265
266        // Compute entity DOFs and entity closure DOFs
267        let mut entity_dofs = [vec![], vec![], vec![], vec![]];
268        let mut dof = 0;
269        for i in 0..4 {
270            for wts in &new_wts[i] {
271                let dofs = (dof..dof + wts.shape()[0]).collect::<Vec<_>>();
272                entity_dofs[i].push(dofs);
273                dof += wts.shape()[0];
274            }
275        }
276        let connectivity = reference_cell::connectivity(cell_type);
277        let mut entity_closure_dofs = [vec![], vec![], vec![], vec![]];
278        for (edim, (ecdofs, connectivity_edim)) in
279            izip!(entity_closure_dofs.iter_mut(), &connectivity).enumerate()
280        {
281            for connectivity_edim_eindex in connectivity_edim {
282                let mut cdofs = vec![];
283                for (edim2, connectivity_edim_eindex_edim2) in
284                    connectivity_edim_eindex.iter().take(edim + 1).enumerate()
285                {
286                    for index in connectivity_edim_eindex_edim2 {
287                        for i in &entity_dofs[edim2][*index] {
288                            cdofs.push(*i)
289                        }
290                    }
291                }
292                ecdofs.push(cdofs);
293            }
294        }
295
296        // Compute DOF transformations
297        let mut dof_transformations = HashMap::new();
298        for (entity, entity_id, transform, f) in match cell_type {
299            ReferenceCellType::Point => vec![],
300            ReferenceCellType::Interval => vec![],
301            ReferenceCellType::Triangle => vec![(
302                ReferenceCellType::Interval,
303                0,
304                Transformation::Reflection,
305                (|x| vec![x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
306            )],
307            ReferenceCellType::Quadrilateral => vec![(
308                ReferenceCellType::Interval,
309                0,
310                Transformation::Reflection,
311                (|x| vec![TGeo::one() - x[0], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
312            )],
313            ReferenceCellType::Tetrahedron => vec![
314                (
315                    ReferenceCellType::Interval,
316                    0,
317                    Transformation::Reflection,
318                    (|x| vec![x[0], x[2], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
319                ),
320                (
321                    ReferenceCellType::Triangle,
322                    0,
323                    Transformation::Rotation,
324                    (|x| vec![x[1], x[2], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
325                ),
326                (
327                    ReferenceCellType::Triangle,
328                    0,
329                    Transformation::Reflection,
330                    (|x| vec![x[0], x[2], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
331                ),
332            ],
333            ReferenceCellType::Hexahedron => vec![
334                (
335                    ReferenceCellType::Interval,
336                    0,
337                    Transformation::Reflection,
338                    (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
339                ),
340                (
341                    ReferenceCellType::Quadrilateral,
342                    0,
343                    Transformation::Rotation,
344                    (|x| vec![x[1], TGeo::one() - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
345                ),
346                (
347                    ReferenceCellType::Quadrilateral,
348                    0,
349                    Transformation::Reflection,
350                    (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
351                ),
352            ],
353            ReferenceCellType::Prism => vec![
354                (
355                    ReferenceCellType::Interval,
356                    0,
357                    Transformation::Reflection,
358                    (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
359                ),
360                (
361                    ReferenceCellType::Triangle,
362                    0,
363                    Transformation::Rotation,
364                    (|x| vec![x[1], TGeo::one() - x[1] - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
365                ),
366                (
367                    ReferenceCellType::Triangle,
368                    0,
369                    Transformation::Reflection,
370                    (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
371                ),
372                (
373                    ReferenceCellType::Quadrilateral,
374                    1,
375                    Transformation::Rotation,
376                    (|x| vec![x[2], TGeo::one() - x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
377                ),
378                (
379                    ReferenceCellType::Quadrilateral,
380                    1,
381                    Transformation::Reflection,
382                    (|x| vec![x[2], x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
383                ),
384            ],
385            ReferenceCellType::Pyramid => vec![
386                (
387                    ReferenceCellType::Interval,
388                    0,
389                    Transformation::Reflection,
390                    (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
391                ),
392                (
393                    ReferenceCellType::Triangle,
394                    1,
395                    Transformation::Rotation,
396                    (|x| vec![x[2], x[1], TGeo::one() - x[2] - x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
397                ),
398                (
399                    ReferenceCellType::Triangle,
400                    1,
401                    Transformation::Reflection,
402                    (|x| vec![x[2], x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
403                ),
404                (
405                    ReferenceCellType::Quadrilateral,
406                    0,
407                    Transformation::Rotation,
408                    (|x| vec![x[1], TGeo::one() - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
409                ),
410                (
411                    ReferenceCellType::Quadrilateral,
412                    0,
413                    Transformation::Reflection,
414                    (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
415                ),
416            ],
417        } {
418            let edim = reference_cell::dim(entity);
419            let ref_pts = &new_pts[edim][entity_id];
420            let npts = ref_pts.shape()[1];
421
422            let finv = match transform {
423                Transformation::Reflection => {
424                    (|x, f| f(x)) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
425                }
426                Transformation::Rotation => match entity {
427                    ReferenceCellType::Interval => {
428                        (|x, f| f(x)) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
429                    }
430                    ReferenceCellType::Triangle => {
431                        (|x, f| f(&f(x))) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
432                    }
433                    ReferenceCellType::Quadrilateral => {
434                        (|x, f| f(&f(&f(x)))) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
435                    }
436                    _ => panic!("Unsupported entity: {entity:?}"),
437                },
438            };
439
440            let mut pts = DynArray::<TGeo, 2>::from_shape(ref_pts.shape());
441            for p in 0..npts {
442                for (i, c) in finv(
443                    &ref_pts.data().unwrap()[ref_pts.shape()[0] * p..ref_pts.shape()[0] * (p + 1)],
444                    f,
445                )
446                .iter()
447                .enumerate()
448                {
449                    *pts.get_mut([i, p]).unwrap() = *c
450                }
451            }
452
453            let wts = &new_wts[edim][entity_id];
454            let edofs = &entity_dofs[edim][entity_id];
455            let endofs = edofs.len();
456            let mut j = rlst_dynamic_array![TGeo, [tdim, tdim, npts]];
457            for t_in in 0..tdim {
458                for (t_out, (a, b)) in izip!(
459                    f(&vec![TGeo::zero(); tdim]),
460                    f(&{
461                        let mut axis = vec![TGeo::zero(); tdim];
462                        axis[t_in] = TGeo::one();
463                        axis
464                    })
465                )
466                .enumerate()
467                {
468                    for p in 0..npts {
469                        *j.get_mut([t_out, t_in, p]).unwrap() = b - a;
470                    }
471                }
472            }
473            // f is linear. Use this to compute determinants
474            let jdet = vec![
475                match transform {
476                    Transformation::Reflection => -TGeo::one(),
477                    Transformation::Rotation => TGeo::one(),
478                };
479                npts
480            ];
481            let mut jinv = rlst_dynamic_array![TGeo, [tdim, tdim, npts]];
482            for t_in in 0..tdim {
483                for (t_out, (a, b)) in izip!(
484                    finv(&vec![TGeo::zero(); tdim], f),
485                    finv(
486                        &{
487                            let mut axis = vec![TGeo::zero(); tdim];
488                            axis[t_in] = TGeo::one();
489                            axis
490                        },
491                        f
492                    )
493                )
494                .enumerate()
495                {
496                    for p in 0..npts {
497                        *jinv.get_mut([t_out, t_in, p]).unwrap() = TGeo::from(b - a).unwrap();
498                    }
499                }
500            }
501
502            let mut table = DynArray::<T, 3>::from_shape(legendre_shape(
503                cell_type,
504                &pts,
505                embedded_superdegree,
506                0,
507            ));
508            tabulate_legendre_polynomials(cell_type, &pts, embedded_superdegree, 0, &mut table);
509
510            let mut data = rlst_dynamic_array!(T, [1, npts, endofs, value_size]);
511            for p in 0..npts {
512                for j in 0..value_size {
513                    for (b, dof) in edofs.iter().enumerate() {
514                        // data[0, p, b, j] = inner(self.coefficients[b, j, :], table[0, :, p])
515                        *data.get_mut([0, p, b, j]).unwrap() = coefficients
516                            .r()
517                            .slice::<2>(0, *dof)
518                            .slice(0, j)
519                            .inner(&table.r().slice::<2>(0, 0).slice(1, p))
520                            .unwrap();
521                    }
522                }
523            }
524
525            let mut pushed_data = rlst_dynamic_array!(T, [1, npts, endofs, value_size]);
526            map.push_forward(&data, 0, &j, &jdet, &jinv, &mut pushed_data);
527
528            let mut dof_transform = rlst_dynamic_array!(T, [edofs.len(), edofs.len()]);
529            for i in 0..edofs.len() {
530                for j in 0..edofs.len() {
531                    *dof_transform.get_mut([i, j]).unwrap() = (0..value_size)
532                        .map(|l| {
533                            (0..npts)
534                                .map(|m| {
535                                    *wts.get([j, l, m]).unwrap()
536                                        * *pushed_data.get([0, m, i, l]).unwrap()
537                                })
538                                .sum::<T>()
539                        })
540                        .sum::<T>();
541                }
542            }
543
544            let perm = math::prepare_matrix(&mut dof_transform);
545
546            // Check if transformation is the identity or a permutation
547            let mut is_identity = true;
548            'outer: for j in 0..edofs.len() {
549                for i in 0..edofs.len() {
550                    if (*dof_transform.get([i, j]).unwrap()
551                        - if i == j { T::one() } else { T::zero() })
552                    .abs()
553                        > T::from(1e-8).unwrap().re()
554                    {
555                        is_identity = false;
556                        break 'outer;
557                    }
558                }
559            }
560
561            if is_identity {
562                let mut is_unpermuted = true;
563                for (i, p) in perm.iter().enumerate() {
564                    if i != *p {
565                        is_unpermuted = false;
566                        break;
567                    }
568                }
569                if is_unpermuted {
570                    dof_transformations.insert((entity, transform), DofTransformation::Identity);
571                } else {
572                    dof_transformations
573                        .insert((entity, transform), DofTransformation::Permutation(perm));
574                }
575            } else {
576                dof_transformations.insert(
577                    (entity, transform),
578                    DofTransformation::Transformation(dof_transform, perm),
579                );
580            }
581        }
582        CiarletElement::<T, M, TGeo> {
583            family_name,
584            cell_type,
585            degree,
586            embedded_superdegree,
587            value_shape,
588            value_size,
589            continuity,
590            dim,
591            coefficients,
592            entity_dofs,
593            entity_closure_dofs,
594            interpolation_points: new_pts,
595            interpolation_weights: new_wts,
596            map,
597            dof_transformations,
598        }
599    }
600
601    /// The polynomial degree
602    pub fn degree(&self) -> usize {
603        self.degree
604    }
605    /// The continuity of the element
606    pub fn continuity(&self) -> Continuity {
607        self.continuity
608    }
609    /// The interpolation points
610    pub fn interpolation_points(&self) -> &EntityPoints<TGeo> {
611        &self.interpolation_points
612    }
613    /// The interpolation weights
614    pub fn interpolation_weights(&self) -> &EntityWeights<T> {
615        &self.interpolation_weights
616    }
617}
618
619impl<T: RlstScalar, M: Map, TGeoInternal: RlstScalar> FiniteElement
620    for CiarletElement<T, M, TGeoInternal>
621{
622    type CellType = ReferenceCellType;
623    type T = T;
624
625    fn value_shape(&self) -> &[usize] {
626        &self.value_shape
627    }
628
629    fn value_size(&self) -> usize {
630        self.value_size
631    }
632
633    fn cell_type(&self) -> ReferenceCellType {
634        self.cell_type
635    }
636
637    fn dim(&self) -> usize {
638        self.dim
639    }
640
641    fn tabulate<
642        TGeo: RlstScalar,
643        Array2Impl: ValueArrayImpl<TGeo, 2>,
644        Array4MutImpl: MutableArrayImpl<T, 4>,
645    >(
646        &self,
647        points: &Array<Array2Impl, 2>,
648        nderivs: usize,
649        data: &mut Array<Array4MutImpl, 4>,
650    ) {
651        let mut table = DynArray::<T, 3>::from_shape(legendre_shape(
652            self.cell_type,
653            points,
654            self.embedded_superdegree,
655            nderivs,
656        ));
657        tabulate_legendre_polynomials(
658            self.cell_type,
659            points,
660            self.embedded_superdegree,
661            nderivs,
662            &mut table,
663        );
664
665        for d in 0..table.shape()[0] {
666            for p in 0..points.shape()[1] {
667                for j in 0..self.value_size {
668                    for b in 0..self.dim {
669                        // data[d, p, b, j] = inner(self.coefficients[b, j, :], table[d, :, p])
670                        *data.get_mut([d, p, b, j]).unwrap() = self
671                            .coefficients
672                            .r()
673                            .slice::<2>(0, b)
674                            .slice(0, j)
675                            .inner(&table.r().slice::<2>(0, d).slice(1, p))
676                            .unwrap();
677                    }
678                }
679            }
680        }
681    }
682
683    fn tabulate_array_shape(&self, nderivs: usize, npoints: usize) -> [usize; 4] {
684        let deriv_count = compute_derivative_count(nderivs, self.cell_type());
685        let point_count = npoints;
686        let basis_count = self.dim();
687        let value_size = self.value_size();
688        [deriv_count, point_count, basis_count, value_size]
689    }
690
691    fn entity_dofs(&self, entity_dim: usize, entity_number: usize) -> Option<&[usize]> {
692        if entity_dim < 4 && entity_number < self.entity_dofs[entity_dim].len() {
693            Some(&self.entity_dofs[entity_dim][entity_number])
694        } else {
695            None
696        }
697    }
698
699    fn entity_closure_dofs(&self, entity_dim: usize, entity_number: usize) -> Option<&[usize]> {
700        if entity_dim < 4 && entity_number < self.entity_closure_dofs[entity_dim].len() {
701            Some(&self.entity_closure_dofs[entity_dim][entity_number])
702        } else {
703            None
704        }
705    }
706}
707
708impl<T: RlstScalar, M: Map, TGeoInternal: RlstScalar> MappedFiniteElement
709    for CiarletElement<T, M, TGeoInternal>
710{
711    type TransformationType = Transformation;
712
713    fn lagrange_superdegree(&self) -> usize {
714        self.embedded_superdegree
715    }
716
717    fn push_forward<
718        TGeo: RlstScalar,
719        Array3GeoImpl: ValueArrayImpl<TGeo, 3>,
720        Array4Impl: ValueArrayImpl<T, 4>,
721        Array4MutImpl: MutableArrayImpl<T, 4>,
722    >(
723        &self,
724        reference_values: &Array<Array4Impl, 4>,
725        nderivs: usize,
726        jacobians: &Array<Array3GeoImpl, 3>,
727        jacobian_determinants: &[TGeo],
728        inverse_jacobians: &Array<Array3GeoImpl, 3>,
729        physical_values: &mut Array<Array4MutImpl, 4>,
730    ) {
731        self.map.push_forward(
732            reference_values,
733            nderivs,
734            jacobians,
735            jacobian_determinants,
736            inverse_jacobians,
737            physical_values,
738        )
739    }
740
741    fn pull_back<
742        TGeo: RlstScalar,
743        Array3GeoImpl: ValueArrayImpl<TGeo, 3>,
744        Array4Impl: ValueArrayImpl<T, 4>,
745        Array4MutImpl: MutableArrayImpl<T, 4>,
746    >(
747        &self,
748        physical_values: &Array<Array4Impl, 4>,
749        nderivs: usize,
750        jacobians: &Array<Array3GeoImpl, 3>,
751        jacobian_determinants: &[TGeo],
752        inverse_jacobians: &Array<Array3GeoImpl, 3>,
753        reference_values: &mut Array<Array4MutImpl, 4>,
754    ) {
755        self.map.pull_back(
756            physical_values,
757            nderivs,
758            jacobians,
759            jacobian_determinants,
760            inverse_jacobians,
761            reference_values,
762        )
763    }
764
765    fn physical_value_shape(&self, gdim: usize) -> Vec<usize> {
766        self.map.physical_value_shape(gdim)
767    }
768
769    fn dof_transformation(
770        &self,
771        entity: ReferenceCellType,
772        transformation: Transformation,
773    ) -> Option<&DofTransformation<T>> {
774        self.dof_transformations.get(&(entity, transformation))
775    }
776
777    fn apply_dof_permutations<Type>(&self, data: &mut [Type], cell_orientation: i32) {
778        debug_assert_eq!(data.len() % self.dim, 0);
779        let block_size = data.len() / self.dim;
780        let tdim = reference_cell::dim(self.cell_type);
781        let mut n = 0;
782        if tdim > 1
783            && let Some(perm) = match self
784                .dof_transformations
785                .get(&(ReferenceCellType::Interval, Transformation::Reflection))
786            {
787                Some(DofTransformation::Permutation(p)) => Some(p),
788                Some(DofTransformation::Transformation(_, p)) => Some(p),
789                _ => None,
790            }
791        {
792            for dofs in &self.entity_dofs[1] {
793                #[cfg(debug_assertions)]
794                for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
795                    assert_eq!(i + 1, *j);
796                }
797                if (cell_orientation >> n) & 1 == 1 {
798                    math::apply_permutation(
799                        perm,
800                        &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
801                    )
802                }
803                n += 1
804            }
805        }
806        if tdim > 2 {
807            for (ftype, dofs) in izip!(
808                &reference_cell::entity_types(self.cell_type)[2],
809                &self.entity_dofs[2]
810            ) {
811                #[cfg(debug_assertions)]
812                for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
813                    assert_eq!(i + 1, *j);
814                }
815                if let Some(perm) = match self
816                    .dof_transformations
817                    .get(&(*ftype, Transformation::Rotation))
818                {
819                    Some(DofTransformation::Permutation(p)) => Some(p),
820                    Some(DofTransformation::Transformation(_, p)) => Some(p),
821                    _ => None,
822                } {
823                    for _ in 0..(cell_orientation >> n) & 3 {
824                        math::apply_permutation(
825                            perm,
826                            &mut data
827                                [block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
828                        )
829                    }
830                }
831                n += 2;
832                if let Some(perm) = match self
833                    .dof_transformations
834                    .get(&(*ftype, Transformation::Reflection))
835                {
836                    Some(DofTransformation::Permutation(p)) => Some(p),
837                    Some(DofTransformation::Transformation(_, p)) => Some(p),
838                    _ => None,
839                } && (cell_orientation >> n) & 1 == 1
840                {
841                    math::apply_permutation(
842                        perm,
843                        &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
844                    )
845                }
846                n += 1;
847            }
848        }
849    }
850
851    fn apply_dof_transformations(&self, data: &mut [T], cell_orientation: i32) {
852        debug_assert_eq!(data.len() % self.dim, 0);
853        let block_size = data.len() / self.dim;
854        let tdim = reference_cell::dim(self.cell_type);
855        let mut n = 0;
856        if tdim > 1
857            && let Some(mat) = match self
858                .dof_transformations
859                .get(&(ReferenceCellType::Interval, Transformation::Reflection))
860            {
861                Some(DofTransformation::Transformation(m, _)) => Some(m),
862                _ => None,
863            }
864        {
865            for dofs in &self.entity_dofs[1] {
866                #[cfg(debug_assertions)]
867                for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
868                    assert_eq!(i + 1, *j);
869                }
870                if (cell_orientation >> n) & 1 == 1 {
871                    math::apply_matrix(
872                        mat,
873                        &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
874                    )
875                }
876                n += 1
877            }
878        }
879        if tdim > 2 {
880            for (ftype, dofs) in izip!(
881                &reference_cell::entity_types(self.cell_type)[2],
882                &self.entity_dofs[2]
883            ) {
884                #[cfg(debug_assertions)]
885                for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
886                    assert_eq!(i + 1, *j);
887                }
888                if let Some(mat) = match self
889                    .dof_transformations
890                    .get(&(*ftype, Transformation::Rotation))
891                {
892                    Some(DofTransformation::Transformation(m, _)) => Some(m),
893                    _ => None,
894                } {
895                    for _ in 0..(cell_orientation >> n) & 3 {
896                        math::apply_matrix(
897                            mat,
898                            &mut data
899                                [block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
900                        )
901                    }
902                }
903                n += 2;
904                if let Some(mat) = match self
905                    .dof_transformations
906                    .get(&(*ftype, Transformation::Reflection))
907                {
908                    Some(DofTransformation::Transformation(m, _)) => Some(m),
909                    _ => None,
910                } && (cell_orientation >> n) & 1 == 1
911                {
912                    math::apply_matrix(
913                        mat,
914                        &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
915                    )
916                }
917                n += 1;
918            }
919        }
920    }
921}
922
923#[cfg(test)]
924mod test {
925    use super::*;
926    use approx::*;
927    use paste::paste;
928    use rlst::rlst_dynamic_array;
929
930    pub fn check_dofs(e: impl FiniteElement<CellType = ReferenceCellType>) {
931        let mut ndofs = 0;
932        for (dim, entity_count) in match e.cell_type() {
933            ReferenceCellType::Point => vec![1],
934            ReferenceCellType::Interval => vec![2, 1],
935            ReferenceCellType::Triangle => vec![3, 3, 1],
936            ReferenceCellType::Quadrilateral => vec![4, 4, 1],
937            ReferenceCellType::Tetrahedron => vec![4, 6, 4, 1],
938            ReferenceCellType::Hexahedron => vec![8, 12, 6, 1],
939            ReferenceCellType::Prism => vec![6, 9, 5, 1],
940            ReferenceCellType::Pyramid => vec![5, 8, 5, 1],
941        }
942        .iter()
943        .enumerate()
944        {
945            for entity in 0..*entity_count {
946                ndofs += e.entity_dofs(dim, entity).unwrap().len();
947            }
948        }
949        assert_eq!(ndofs, e.dim());
950    }
951
952    #[test]
953    fn test_raviart_thomas_1_triangle() {
954        let e = raviart_thomas::create::<f64, f64>(
955            ReferenceCellType::Triangle,
956            1,
957            Continuity::Standard,
958        );
959        assert_eq!(e.value_size(), 2);
960        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
961        let mut points = rlst_dynamic_array!(f64, [2, 6]);
962        *points.get_mut([0, 0]).unwrap() = 0.0;
963        *points.get_mut([1, 0]).unwrap() = 0.0;
964        *points.get_mut([0, 1]).unwrap() = 1.0;
965        *points.get_mut([1, 1]).unwrap() = 0.0;
966        *points.get_mut([0, 2]).unwrap() = 0.0;
967        *points.get_mut([1, 2]).unwrap() = 1.0;
968        *points.get_mut([0, 3]).unwrap() = 0.5;
969        *points.get_mut([1, 3]).unwrap() = 0.0;
970        *points.get_mut([0, 4]).unwrap() = 0.0;
971        *points.get_mut([1, 4]).unwrap() = 0.5;
972        *points.get_mut([0, 5]).unwrap() = 0.5;
973        *points.get_mut([1, 5]).unwrap() = 0.5;
974        e.tabulate(&points, 0, &mut data);
975
976        for pt in 0..6 {
977            let x = *points.get([0, pt]).unwrap();
978            let y = *points.get([1, pt]).unwrap();
979            for (i, basis_f) in [[-x, -y], [x - 1.0, y], [-x, 1.0 - y]].iter().enumerate() {
980                for (d, value) in basis_f.iter().enumerate() {
981                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
982                }
983            }
984        }
985        check_dofs(e);
986    }
987
988    #[test]
989    fn test_raviart_thomas_2_triangle() {
990        let e = raviart_thomas::create::<f64, f64>(
991            ReferenceCellType::Triangle,
992            2,
993            Continuity::Standard,
994        );
995        assert_eq!(e.value_size(), 2);
996        check_dofs(e);
997    }
998
999    #[test]
1000    fn test_raviart_thomas_3_triangle() {
1001        let e = raviart_thomas::create::<f64, f64>(
1002            ReferenceCellType::Triangle,
1003            3,
1004            Continuity::Standard,
1005        );
1006        assert_eq!(e.value_size(), 2);
1007        check_dofs(e);
1008    }
1009
1010    #[test]
1011    fn test_raviart_thomas_1_quadrilateral() {
1012        let e = raviart_thomas::create::<f64, f64>(
1013            ReferenceCellType::Quadrilateral,
1014            1,
1015            Continuity::Standard,
1016        );
1017        assert_eq!(e.value_size(), 2);
1018        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1019        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1020        *points.get_mut([0, 0]).unwrap() = 0.0;
1021        *points.get_mut([1, 0]).unwrap() = 0.0;
1022        *points.get_mut([0, 1]).unwrap() = 1.0;
1023        *points.get_mut([1, 1]).unwrap() = 0.0;
1024        *points.get_mut([0, 2]).unwrap() = 0.0;
1025        *points.get_mut([1, 2]).unwrap() = 1.0;
1026        *points.get_mut([0, 3]).unwrap() = 0.5;
1027        *points.get_mut([1, 3]).unwrap() = 0.0;
1028        *points.get_mut([0, 4]).unwrap() = 1.0;
1029        *points.get_mut([1, 4]).unwrap() = 0.5;
1030        *points.get_mut([0, 5]).unwrap() = 0.5;
1031        *points.get_mut([1, 5]).unwrap() = 0.5;
1032        e.tabulate(&points, 0, &mut data);
1033
1034        for pt in 0..6 {
1035            let x = *points.get([0, pt]).unwrap();
1036            let y = *points.get([1, pt]).unwrap();
1037            for (i, basis_f) in [[0.0, 1.0 - y], [x - 1.0, 0.0], [-x, 0.0], [0.0, y]]
1038                .iter()
1039                .enumerate()
1040            {
1041                for (d, value) in basis_f.iter().enumerate() {
1042                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1043                }
1044            }
1045        }
1046        check_dofs(e);
1047    }
1048
1049    #[test]
1050    fn test_raviart_thomas_2_quadrilateral() {
1051        let e = raviart_thomas::create::<f64, f64>(
1052            ReferenceCellType::Quadrilateral,
1053            2,
1054            Continuity::Standard,
1055        );
1056        assert_eq!(e.value_size(), 2);
1057        check_dofs(e);
1058    }
1059
1060    #[test]
1061    fn test_raviart_thomas_3_quadrilateral() {
1062        let e = raviart_thomas::create::<f64, f64>(
1063            ReferenceCellType::Quadrilateral,
1064            3,
1065            Continuity::Standard,
1066        );
1067        assert_eq!(e.value_size(), 2);
1068        check_dofs(e);
1069    }
1070
1071    #[test]
1072    fn test_raviart_thomas_1_tetrahedron() {
1073        let e = raviart_thomas::create::<f64, f64>(
1074            ReferenceCellType::Tetrahedron,
1075            1,
1076            Continuity::Standard,
1077        );
1078        assert_eq!(e.value_size(), 3);
1079        check_dofs(e);
1080    }
1081
1082    #[test]
1083    fn test_raviart_thomas_2_tetrahedron() {
1084        let e = raviart_thomas::create::<f64, f64>(
1085            ReferenceCellType::Tetrahedron,
1086            2,
1087            Continuity::Standard,
1088        );
1089        assert_eq!(e.value_size(), 3);
1090        check_dofs(e);
1091    }
1092
1093    #[test]
1094    fn test_raviart_thomas_3_tetrahedron() {
1095        let e = raviart_thomas::create::<f64, f64>(
1096            ReferenceCellType::Tetrahedron,
1097            3,
1098            Continuity::Standard,
1099        );
1100        assert_eq!(e.value_size(), 3);
1101        check_dofs(e);
1102    }
1103
1104    #[test]
1105    fn test_raviart_thomas_1_hexahedron() {
1106        let e = raviart_thomas::create::<f64, f64>(
1107            ReferenceCellType::Hexahedron,
1108            1,
1109            Continuity::Standard,
1110        );
1111        assert_eq!(e.value_size(), 3);
1112        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1113        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1114        *points.get_mut([0, 0]).unwrap() = 0.0;
1115        *points.get_mut([1, 0]).unwrap() = 0.0;
1116        *points.get_mut([2, 0]).unwrap() = 0.0;
1117        *points.get_mut([0, 1]).unwrap() = 1.0;
1118        *points.get_mut([1, 1]).unwrap() = 0.0;
1119        *points.get_mut([2, 1]).unwrap() = 0.8;
1120        *points.get_mut([0, 2]).unwrap() = 0.0;
1121        *points.get_mut([1, 2]).unwrap() = 1.0;
1122        *points.get_mut([2, 2]).unwrap() = 1.0;
1123        *points.get_mut([0, 3]).unwrap() = 0.5;
1124        *points.get_mut([1, 3]).unwrap() = 0.0;
1125        *points.get_mut([2, 3]).unwrap() = 0.1;
1126        *points.get_mut([0, 4]).unwrap() = 1.0;
1127        *points.get_mut([1, 4]).unwrap() = 0.5;
1128        *points.get_mut([2, 4]).unwrap() = 0.5;
1129        *points.get_mut([0, 5]).unwrap() = 0.5;
1130        *points.get_mut([1, 5]).unwrap() = 0.5;
1131        *points.get_mut([2, 5]).unwrap() = 1.0;
1132        e.tabulate(&points, 0, &mut data);
1133
1134        for pt in 0..6 {
1135            let x = *points.get([0, pt]).unwrap();
1136            let y = *points.get([1, pt]).unwrap();
1137            let z = *points.get([2, pt]).unwrap();
1138            for (i, basis_f) in [
1139                [0.0, 0.0, 1.0 - z],
1140                [0.0, y - 1.0, 0.0],
1141                [1.0 - x, 0.0, 0.0],
1142                [x, 0.0, 0.0],
1143                [0.0, -y, 0.0],
1144                [0.0, 0.0, z],
1145            ]
1146            .iter()
1147            .enumerate()
1148            {
1149                for (d, value) in basis_f.iter().enumerate() {
1150                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1151                }
1152            }
1153        }
1154        check_dofs(e);
1155    }
1156
1157    #[test]
1158    fn test_raviart_thomas_2_hexahedron() {
1159        let e = raviart_thomas::create::<f64, f64>(
1160            ReferenceCellType::Hexahedron,
1161            2,
1162            Continuity::Standard,
1163        );
1164        assert_eq!(e.value_size(), 3);
1165        check_dofs(e);
1166    }
1167
1168    #[test]
1169    fn test_nedelec_1_triangle() {
1170        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1171        assert_eq!(e.value_size(), 2);
1172        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1173        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1174        *points.get_mut([0, 0]).unwrap() = 0.0;
1175        *points.get_mut([1, 0]).unwrap() = 0.0;
1176        *points.get_mut([0, 1]).unwrap() = 1.0;
1177        *points.get_mut([1, 1]).unwrap() = 0.0;
1178        *points.get_mut([0, 2]).unwrap() = 0.0;
1179        *points.get_mut([1, 2]).unwrap() = 1.0;
1180        *points.get_mut([0, 3]).unwrap() = 0.5;
1181        *points.get_mut([1, 3]).unwrap() = 0.0;
1182        *points.get_mut([0, 4]).unwrap() = 0.0;
1183        *points.get_mut([1, 4]).unwrap() = 0.5;
1184        *points.get_mut([0, 5]).unwrap() = 0.5;
1185        *points.get_mut([1, 5]).unwrap() = 0.5;
1186        e.tabulate(&points, 0, &mut data);
1187
1188        for pt in 0..6 {
1189            let x = *points.get([0, pt]).unwrap();
1190            let y = *points.get([1, pt]).unwrap();
1191            for (i, basis_f) in [[-y, x], [y, 1.0 - x], [1.0 - y, x]].iter().enumerate() {
1192                for (d, value) in basis_f.iter().enumerate() {
1193                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1194                }
1195            }
1196        }
1197        check_dofs(e);
1198    }
1199
1200    #[test]
1201    fn test_nedelec_2_triangle() {
1202        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1203        assert_eq!(e.value_size(), 2);
1204        check_dofs(e);
1205    }
1206
1207    #[test]
1208    fn test_nedelec_3_triangle() {
1209        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Standard);
1210        assert_eq!(e.value_size(), 2);
1211        check_dofs(e);
1212    }
1213
1214    #[test]
1215    fn test_nedelec_1_quadrilateral() {
1216        let e =
1217            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 1, Continuity::Standard);
1218        assert_eq!(e.value_size(), 2);
1219        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1220        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1221        *points.get_mut([0, 0]).unwrap() = 0.0;
1222        *points.get_mut([1, 0]).unwrap() = 0.0;
1223        *points.get_mut([0, 1]).unwrap() = 1.0;
1224        *points.get_mut([1, 1]).unwrap() = 0.0;
1225        *points.get_mut([0, 2]).unwrap() = 0.0;
1226        *points.get_mut([1, 2]).unwrap() = 1.0;
1227        *points.get_mut([0, 3]).unwrap() = 0.5;
1228        *points.get_mut([1, 3]).unwrap() = 0.0;
1229        *points.get_mut([0, 4]).unwrap() = 1.0;
1230        *points.get_mut([1, 4]).unwrap() = 0.5;
1231        *points.get_mut([0, 5]).unwrap() = 0.5;
1232        *points.get_mut([1, 5]).unwrap() = 0.5;
1233        e.tabulate(&points, 0, &mut data);
1234
1235        for pt in 0..6 {
1236            let x = *points.get([0, pt]).unwrap();
1237            let y = *points.get([1, pt]).unwrap();
1238            for (i, basis_f) in [[1.0 - y, 0.0], [0.0, 1.0 - x], [0.0, x], [y, 0.0]]
1239                .iter()
1240                .enumerate()
1241            {
1242                for (d, value) in basis_f.iter().enumerate() {
1243                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1244                }
1245            }
1246        }
1247        check_dofs(e);
1248    }
1249
1250    #[test]
1251    fn test_nedelec_2_quadrilateral() {
1252        let e =
1253            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 2, Continuity::Standard);
1254        assert_eq!(e.value_size(), 2);
1255        check_dofs(e);
1256    }
1257
1258    #[test]
1259    fn test_nedelec_3_quadrilateral() {
1260        let e =
1261            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 3, Continuity::Standard);
1262        assert_eq!(e.value_size(), 2);
1263        check_dofs(e);
1264    }
1265
1266    #[test]
1267    fn test_nedelec_1_tetrahedron() {
1268        let e =
1269            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1270        assert_eq!(e.value_size(), 3);
1271        check_dofs(e);
1272    }
1273
1274    #[test]
1275    fn test_nedelec_2_tetrahedron() {
1276        let e =
1277            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
1278        assert_eq!(e.value_size(), 3);
1279        check_dofs(e);
1280    }
1281
1282    #[test]
1283    fn test_nedelec_3_tetrahedron() {
1284        let e =
1285            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 3, Continuity::Standard);
1286        assert_eq!(e.value_size(), 3);
1287        check_dofs(e);
1288    }
1289
1290    #[test]
1291    fn test_nedelec_1_hexahedron() {
1292        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 1, Continuity::Standard);
1293        assert_eq!(e.value_size(), 3);
1294        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1295        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1296        *points.get_mut([0, 0]).unwrap() = 0.0;
1297        *points.get_mut([1, 0]).unwrap() = 0.0;
1298        *points.get_mut([2, 0]).unwrap() = 0.0;
1299        *points.get_mut([0, 1]).unwrap() = 1.0;
1300        *points.get_mut([1, 1]).unwrap() = 0.0;
1301        *points.get_mut([2, 1]).unwrap() = 0.8;
1302        *points.get_mut([0, 2]).unwrap() = 0.0;
1303        *points.get_mut([1, 2]).unwrap() = 1.0;
1304        *points.get_mut([2, 2]).unwrap() = 1.0;
1305        *points.get_mut([0, 3]).unwrap() = 0.5;
1306        *points.get_mut([1, 3]).unwrap() = 0.0;
1307        *points.get_mut([2, 3]).unwrap() = 0.1;
1308        *points.get_mut([0, 4]).unwrap() = 1.0;
1309        *points.get_mut([1, 4]).unwrap() = 0.5;
1310        *points.get_mut([2, 4]).unwrap() = 0.5;
1311        *points.get_mut([0, 5]).unwrap() = 0.5;
1312        *points.get_mut([1, 5]).unwrap() = 0.5;
1313        *points.get_mut([2, 5]).unwrap() = 1.0;
1314        e.tabulate(&points, 0, &mut data);
1315
1316        for pt in 0..6 {
1317            let x = *points.get([0, pt]).unwrap();
1318            let y = *points.get([1, pt]).unwrap();
1319            let z = *points.get([2, pt]).unwrap();
1320            for (i, basis_f) in [
1321                [(1.0 - y) * (1.0 - z), 0.0, 0.0],
1322                [0.0, (1.0 - x) * (1.0 - z), 0.0],
1323                [0.0, 0.0, (1.0 - x) * (1.0 - y)],
1324                [0.0, x * (1.0 - z), 0.0],
1325                [0.0, 0.0, x * (1.0 - y)],
1326                [y * (1.0 - z), 0.0, 0.0],
1327                [0.0, 0.0, (1.0 - x) * y],
1328                [0.0, 0.0, x * y],
1329                [(1.0 - y) * z, 0.0, 0.0],
1330                [0.0, (1.0 - x) * z, 0.0],
1331                [0.0, x * z, 0.0],
1332                [y * z, 0.0, 0.0],
1333            ]
1334            .iter()
1335            .enumerate()
1336            {
1337                for (d, value) in basis_f.iter().enumerate() {
1338                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1339                }
1340            }
1341        }
1342        check_dofs(e);
1343    }
1344
1345    #[test]
1346    fn test_nedelec_2_hexahedron() {
1347        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1348        assert_eq!(e.value_size(), 3);
1349        check_dofs(e);
1350    }
1351
1352    macro_rules! test_entity_closure_dofs_lagrange {
1353        ($cell:ident, $degree:expr) => {
1354            paste! {
1355                #[test]
1356                fn [<test_entity_closure_dofs_ $cell:lower _ $degree>]() {
1357                    let e = lagrange::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard, LagrangeVariant::Equispaced);
1358                    let c = reference_cell::connectivity(ReferenceCellType::[<$cell>]);
1359
1360                    for (dim, entities) in c.iter().enumerate() {
1361                        for (n, entity) in entities.iter().enumerate() {
1362                            let ecd = e.entity_closure_dofs(dim, n).unwrap();
1363                            let mut len = 0;
1364                            for (sub_dim, sub_entities) in entity.iter().take(dim + 1).enumerate() {
1365                                for sub_entity in sub_entities {
1366                                    let dofs = e.entity_dofs(sub_dim, *sub_entity).unwrap();
1367                                    len += dofs.len();
1368                                    for dof in dofs {
1369                                        assert!(ecd.contains(dof));
1370                                    }
1371                                }
1372                            }
1373                            assert_eq!(ecd.len(), len);
1374                        }
1375                    }
1376                }
1377            }
1378        };
1379    }
1380
1381    test_entity_closure_dofs_lagrange!(Interval, 2);
1382    test_entity_closure_dofs_lagrange!(Interval, 3);
1383    test_entity_closure_dofs_lagrange!(Interval, 4);
1384    test_entity_closure_dofs_lagrange!(Interval, 5);
1385    test_entity_closure_dofs_lagrange!(Triangle, 2);
1386    test_entity_closure_dofs_lagrange!(Triangle, 3);
1387    test_entity_closure_dofs_lagrange!(Triangle, 4);
1388    test_entity_closure_dofs_lagrange!(Triangle, 5);
1389    test_entity_closure_dofs_lagrange!(Quadrilateral, 2);
1390    test_entity_closure_dofs_lagrange!(Quadrilateral, 3);
1391    test_entity_closure_dofs_lagrange!(Quadrilateral, 4);
1392    test_entity_closure_dofs_lagrange!(Quadrilateral, 5);
1393    test_entity_closure_dofs_lagrange!(Tetrahedron, 2);
1394    test_entity_closure_dofs_lagrange!(Tetrahedron, 3);
1395    test_entity_closure_dofs_lagrange!(Tetrahedron, 4);
1396    test_entity_closure_dofs_lagrange!(Tetrahedron, 5);
1397    test_entity_closure_dofs_lagrange!(Hexahedron, 2);
1398    test_entity_closure_dofs_lagrange!(Hexahedron, 3);
1399
1400    macro_rules! test_dof_transformations {
1401        ($cell:ident, $element:ident, $degree:expr $(, $variant:expr)*) => {
1402            paste! {
1403
1404                #[test]
1405                fn [<test_dof_transformations_ $cell:lower _ $element:lower _ $degree>]() {
1406                    let e = [<$element>]::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard, $($variant),*);
1407                    let tdim = reference_cell::dim(ReferenceCellType::[<$cell>]);
1408                    for edim in 1..tdim {
1409                        for entity in &reference_cell::entity_types(ReferenceCellType::[<$cell>])[edim] {
1410                            for t in match edim {
1411                                1 => vec![Transformation::Reflection],
1412                                2 => vec![Transformation::Reflection, Transformation::Rotation],
1413                                _ => { panic!("Shouldn't test this dimension"); },
1414                            } {
1415                                let order = match t {
1416                                    Transformation::Reflection => 2,
1417                                    Transformation::Rotation => match entity {
1418                                        ReferenceCellType::Triangle => 3,
1419                                        ReferenceCellType::Quadrilateral => 4,
1420                                        _ => {
1421                                            panic!("Unsupported entity type: {entity:?}");
1422                                        }
1423                                    },
1424                                };
1425                                match e.dof_transformation(*entity, t).unwrap() {
1426                                    DofTransformation::Identity => {}
1427                                    DofTransformation::Permutation(p) => {
1428                                        let mut data = (0..p.len()).collect::<Vec<_>>();
1429                                        for _ in 0..order {
1430                                            math::apply_permutation(p, &mut data);
1431                                        }
1432                                        for (i, j) in data.iter().enumerate() {
1433                                            assert_eq!(i, *j);
1434                                        }
1435                                    }
1436                                    DofTransformation::Transformation(m, p) => {
1437                                        let mut data = (0..p.len()).map(|i| i as f64).collect::<Vec<_>>();
1438                                        for _ in 0..order {
1439                                            math::apply_perm_and_matrix(m, p, &mut data);
1440                                        }
1441                                        for (i, j) in data.iter().enumerate() {
1442                                            assert_relative_eq!(i as f64, j, epsilon=1e-10);
1443                                        }
1444                                    }
1445                                }
1446                            }
1447                        }
1448                    }
1449                }
1450            }
1451        };
1452    }
1453
1454    test_dof_transformations!(Triangle, lagrange, 1, LagrangeVariant::Equispaced);
1455    test_dof_transformations!(Triangle, lagrange, 2, LagrangeVariant::Equispaced);
1456    test_dof_transformations!(Triangle, lagrange, 3, LagrangeVariant::Equispaced);
1457    test_dof_transformations!(Quadrilateral, lagrange, 1, LagrangeVariant::Equispaced);
1458    test_dof_transformations!(Quadrilateral, lagrange, 2, LagrangeVariant::Equispaced);
1459    test_dof_transformations!(Quadrilateral, lagrange, 3, LagrangeVariant::Equispaced);
1460    test_dof_transformations!(Tetrahedron, lagrange, 1, LagrangeVariant::Equispaced);
1461    test_dof_transformations!(Tetrahedron, lagrange, 2, LagrangeVariant::Equispaced);
1462    test_dof_transformations!(Tetrahedron, lagrange, 3, LagrangeVariant::Equispaced);
1463    test_dof_transformations!(Hexahedron, lagrange, 1, LagrangeVariant::Equispaced);
1464    test_dof_transformations!(Hexahedron, lagrange, 2, LagrangeVariant::Equispaced);
1465    test_dof_transformations!(Hexahedron, lagrange, 3, LagrangeVariant::Equispaced);
1466    test_dof_transformations!(Triangle, nedelec, 1);
1467    test_dof_transformations!(Triangle, nedelec, 2);
1468    test_dof_transformations!(Triangle, nedelec, 3);
1469    test_dof_transformations!(Quadrilateral, nedelec, 1);
1470    test_dof_transformations!(Quadrilateral, nedelec, 2);
1471    test_dof_transformations!(Quadrilateral, nedelec, 3);
1472    test_dof_transformations!(Tetrahedron, nedelec, 1);
1473    test_dof_transformations!(Tetrahedron, nedelec, 2);
1474    test_dof_transformations!(Tetrahedron, nedelec, 3);
1475    test_dof_transformations!(Hexahedron, nedelec, 1);
1476    test_dof_transformations!(Hexahedron, nedelec, 2);
1477    test_dof_transformations!(Triangle, raviart_thomas, 1);
1478    test_dof_transformations!(Triangle, raviart_thomas, 2);
1479    test_dof_transformations!(Triangle, raviart_thomas, 3);
1480    test_dof_transformations!(Quadrilateral, raviart_thomas, 1);
1481    test_dof_transformations!(Quadrilateral, raviart_thomas, 2);
1482    test_dof_transformations!(Quadrilateral, raviart_thomas, 3);
1483    test_dof_transformations!(Tetrahedron, raviart_thomas, 1);
1484    test_dof_transformations!(Tetrahedron, raviart_thomas, 2);
1485    test_dof_transformations!(Tetrahedron, raviart_thomas, 3);
1486    test_dof_transformations!(Hexahedron, raviart_thomas, 1);
1487    test_dof_transformations!(Hexahedron, raviart_thomas, 2);
1488
1489    fn assert_dof_transformation_equal<Array2Impl: ValueArrayImpl<f64, 2>>(
1490        p: &[usize],
1491        m: &Array<Array2Impl, 2>,
1492        expected: Vec<Vec<f64>>,
1493    ) {
1494        let ndofs = p.len();
1495        assert_eq!(m.shape()[0], ndofs);
1496        assert_eq!(m.shape()[1], ndofs);
1497        assert_eq!(expected.len(), ndofs);
1498        for i in 0..ndofs {
1499            let mut col = vec![0.0; ndofs];
1500            col[i] = 1.0;
1501            math::apply_perm_and_matrix(m, p, &mut col);
1502            for (j, c) in col.iter().enumerate() {
1503                assert_relative_eq!(expected[j][i], *c, epsilon = 1e-10);
1504            }
1505        }
1506    }
1507
1508    #[test]
1509    fn test_nedelec1_triangle_dof_transformations() {
1510        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1511        if let DofTransformation::Transformation(m, p) = e
1512            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1513            .unwrap()
1514        {
1515            assert_eq!(p.len(), 1);
1516            assert_eq!(m.shape()[0], 1);
1517            assert_eq!(m.shape()[1], 1);
1518            assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
1519        } else {
1520            panic!("Should be a linear transformation");
1521        }
1522    }
1523
1524    #[test]
1525    fn test_nedelec2_triangle_dof_transformations() {
1526        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1527        if let DofTransformation::Transformation(m, p) = e
1528            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1529            .unwrap()
1530        {
1531            assert_eq!(p.len(), 2);
1532            assert_eq!(m.shape()[0], 2);
1533            assert_eq!(m.shape()[1], 2);
1534            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1535        } else {
1536            panic!("Should be a linear transformation");
1537        }
1538    }
1539
1540    #[test]
1541    fn test_nedelec1_tetrahedron_dof_transformations() {
1542        let e =
1543            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1544        if let DofTransformation::Transformation(m, p) = e
1545            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1546            .unwrap()
1547        {
1548            assert_eq!(p.len(), 1);
1549            assert_eq!(m.shape()[0], 1);
1550            assert_eq!(m.shape()[1], 1);
1551            assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
1552        } else {
1553            panic!("Should be a linear transformation");
1554        }
1555        if let DofTransformation::Identity = e
1556            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1557            .unwrap()
1558        {
1559        } else {
1560            panic!("Should be an identity transformation");
1561        }
1562        if let DofTransformation::Identity = e
1563            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1564            .unwrap()
1565        {
1566        } else {
1567            panic!("Should be an identity transformation");
1568        }
1569    }
1570
1571    #[test]
1572    fn test_nedelec2_tetrahedron_dof_transformations() {
1573        let e =
1574            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
1575        if let DofTransformation::Transformation(m, p) = e
1576            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1577            .unwrap()
1578        {
1579            assert_eq!(p.len(), 2);
1580            assert_eq!(m.shape()[0], 2);
1581            assert_eq!(m.shape()[1], 2);
1582            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1583        } else {
1584            panic!("Should be a linear transformation");
1585        }
1586        if let DofTransformation::Permutation(p) = e
1587            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1588            .unwrap()
1589        {
1590            assert_eq!(p.len(), 2);
1591            let mut indices = vec![0, 1];
1592            math::apply_permutation(p, &mut indices);
1593            assert_eq!(indices[0], 1);
1594            assert_eq!(indices[1], 0);
1595        } else {
1596            panic!("Should be a permutation");
1597        }
1598        if let DofTransformation::Transformation(m, p) = e
1599            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1600            .unwrap()
1601        {
1602            assert_eq!(p.len(), 2);
1603            assert_eq!(m.shape()[0], 2);
1604            assert_eq!(m.shape()[1], 2);
1605            assert_dof_transformation_equal(p, m, vec![vec![-1.0, -1.0], vec![1.0, 0.0]]);
1606        } else {
1607            panic!("Should be a linear transformation");
1608        }
1609    }
1610
1611    #[test]
1612    fn test_nedelec2_quadrilateral_dof_transformations() {
1613        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1614        if let DofTransformation::Transformation(m, p) = e
1615            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1616            .unwrap()
1617        {
1618            assert_eq!(p.len(), 2);
1619            assert_eq!(m.shape()[0], 2);
1620            assert_eq!(m.shape()[1], 2);
1621            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1622        } else {
1623            panic!("Should be a linear transformation");
1624        }
1625    }
1626
1627    #[test]
1628    fn test_nedelec2_hexahedron_dof_transformations() {
1629        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1630        if let DofTransformation::Transformation(m, p) = e
1631            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1632            .unwrap()
1633        {
1634            assert_eq!(p.len(), 2);
1635            assert_eq!(m.shape()[0], 2);
1636            assert_eq!(m.shape()[1], 2);
1637            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1638        } else {
1639            panic!("Should be a linear transformation");
1640        }
1641        if let DofTransformation::Permutation(p) = e
1642            .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Reflection)
1643            .unwrap()
1644        {
1645            assert_eq!(p.len(), 4);
1646            let mut indices = vec![0, 1, 2, 3];
1647            math::apply_permutation(p, &mut indices);
1648            assert_eq!(indices[0], 1);
1649            assert_eq!(indices[1], 0);
1650            assert_eq!(indices[2], 3);
1651            assert_eq!(indices[3], 2);
1652        } else {
1653            panic!("Should be a permutation");
1654        }
1655        if let DofTransformation::Transformation(m, p) = e
1656            .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Rotation)
1657            .unwrap()
1658        {
1659            assert_eq!(p.len(), 4);
1660            assert_eq!(m.shape()[0], 4);
1661            assert_eq!(m.shape()[1], 4);
1662            assert_dof_transformation_equal(
1663                p,
1664                m,
1665                vec![
1666                    vec![0.0, -1.0, 0.0, 0.0],
1667                    vec![1.0, 0.0, 0.0, 0.0],
1668                    vec![0.0, 0.0, 0.0, 1.0],
1669                    vec![0.0, 0.0, 1.0, 0.0],
1670                ],
1671            );
1672        } else {
1673            panic!("Should be a linear transformation");
1674        }
1675    }
1676
1677    #[test]
1678    fn test_lagrange4_tetrahedron_dof_transformations() {
1679        let e = lagrange::create::<f64, f64>(
1680            ReferenceCellType::Tetrahedron,
1681            4,
1682            Continuity::Standard,
1683            LagrangeVariant::Equispaced,
1684        );
1685        if let DofTransformation::Permutation(p) = e
1686            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1687            .unwrap()
1688        {
1689            assert_eq!(p.len(), 3);
1690            let mut indices = vec![0, 1, 2];
1691            math::apply_permutation(p, &mut indices);
1692            assert_eq!(indices[0], 2);
1693            assert_eq!(indices[1], 1);
1694            assert_eq!(indices[2], 0);
1695        } else {
1696            panic!("Should be a permutation");
1697        }
1698        if let DofTransformation::Permutation(p) = e
1699            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1700            .unwrap()
1701        {
1702            assert_eq!(p.len(), 3);
1703            let mut indices = vec![0, 1, 2];
1704            math::apply_permutation(p, &mut indices);
1705            assert_eq!(indices[0], 0);
1706            assert_eq!(indices[1], 2);
1707            assert_eq!(indices[2], 1);
1708        } else {
1709            panic!("Should be a permutation");
1710        }
1711        if let DofTransformation::Permutation(p) = e
1712            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1713            .unwrap()
1714        {
1715            assert_eq!(p.len(), 3);
1716            let mut indices = vec![0, 1, 2];
1717            math::apply_permutation(p, &mut indices);
1718            assert_eq!(indices[0], 1);
1719            assert_eq!(indices[1], 2);
1720            assert_eq!(indices[2], 0);
1721        } else {
1722            panic!("Should be a permutation");
1723        }
1724    }
1725
1726    #[test]
1727    fn test_dof_permuting_triangle() {
1728        let e = lagrange::create::<f64, f64>(
1729            ReferenceCellType::Triangle,
1730            3,
1731            Continuity::Standard,
1732            LagrangeVariant::Equispaced,
1733        );
1734
1735        let mut n = (0..10).collect::<Vec<_>>();
1736        e.apply_dof_permutations(&mut n, 7);
1737        for (i, j) in izip!(&n, [0, 1, 2, 4, 3, 6, 5, 8, 7, 9]) {
1738            assert_eq!(*i, j);
1739        }
1740
1741        let mut n = (0..20).collect::<Vec<_>>();
1742        e.apply_dof_permutations(&mut n, 7);
1743        for (i, j) in izip!(
1744            &n,
1745            [
1746                0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 12, 13, 10, 11, 16, 17, 14, 15, 18, 19
1747            ]
1748        ) {
1749            assert_eq!(*i, j);
1750        }
1751    }
1752
1753    #[test]
1754    fn test_dof_permuting_tetrahedron() {
1755        let e = lagrange::create::<f64, f64>(
1756            ReferenceCellType::Tetrahedron,
1757            3,
1758            Continuity::Standard,
1759            LagrangeVariant::Equispaced,
1760        );
1761
1762        let mut n = (0..20).collect::<Vec<_>>();
1763        e.apply_dof_permutations(&mut n, 63);
1764        for (i, j) in izip!(
1765            &n,
1766            [
1767                0, 1, 2, 3, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 16, 17, 18, 19
1768            ]
1769        ) {
1770            assert_eq!(*i, j);
1771        }
1772    }
1773}