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;
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 = <T as RlstScalar>::Real> {
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, [npts, tdim, tdim]];
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([p, t_out, t_in]).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, [npts, tdim, tdim]];
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([p, t_out, t_in]).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    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_lagrange_1() {
954        let e = lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
955        assert_eq!(e.value_size(), 1);
956    }
957
958    #[test]
959    fn test_lagrange_0_interval() {
960        let e =
961            lagrange::create::<f64, f64>(ReferenceCellType::Interval, 0, Continuity::Discontinuous);
962        assert_eq!(e.value_size(), 1);
963        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 4));
964        let mut points = rlst_dynamic_array!(f64, [1, 4]);
965        *points.get_mut([0, 0]).unwrap() = 0.0;
966        *points.get_mut([0, 1]).unwrap() = 0.2;
967        *points.get_mut([0, 2]).unwrap() = 0.4;
968        *points.get_mut([0, 3]).unwrap() = 1.0;
969        e.tabulate(&points, 0, &mut data);
970
971        for pt in 0..4 {
972            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0);
973        }
974        check_dofs(e);
975    }
976
977    #[test]
978    fn test_lagrange_1_interval() {
979        let e = lagrange::create::<f64, f64>(ReferenceCellType::Interval, 1, Continuity::Standard);
980        assert_eq!(e.value_size(), 1);
981        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 4));
982        let mut points = rlst_dynamic_array!(f64, [1, 4]);
983        *points.get_mut([0, 0]).unwrap() = 0.0;
984        *points.get_mut([0, 1]).unwrap() = 0.2;
985        *points.get_mut([0, 2]).unwrap() = 0.4;
986        *points.get_mut([0, 3]).unwrap() = 1.0;
987        e.tabulate(&points, 0, &mut data);
988
989        for pt in 0..4 {
990            let x = *points.get([0, pt]).unwrap();
991            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0 - x);
992            assert_relative_eq!(*data.get([0, pt, 1, 0]).unwrap(), x);
993        }
994        check_dofs(e);
995    }
996
997    #[test]
998    fn test_lagrange_0_triangle() {
999        let e =
1000            lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 0, Continuity::Discontinuous);
1001        assert_eq!(e.value_size(), 1);
1002        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1003
1004        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1005        *points.get_mut([0, 0]).unwrap() = 0.0;
1006        *points.get_mut([1, 0]).unwrap() = 0.0;
1007        *points.get_mut([0, 1]).unwrap() = 1.0;
1008        *points.get_mut([1, 1]).unwrap() = 0.0;
1009        *points.get_mut([0, 2]).unwrap() = 0.0;
1010        *points.get_mut([1, 2]).unwrap() = 1.0;
1011        *points.get_mut([0, 3]).unwrap() = 0.5;
1012        *points.get_mut([1, 3]).unwrap() = 0.0;
1013        *points.get_mut([0, 4]).unwrap() = 0.0;
1014        *points.get_mut([1, 4]).unwrap() = 0.5;
1015        *points.get_mut([0, 5]).unwrap() = 0.5;
1016        *points.get_mut([1, 5]).unwrap() = 0.5;
1017
1018        e.tabulate(&points, 0, &mut data);
1019
1020        for pt in 0..6 {
1021            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0);
1022        }
1023        check_dofs(e);
1024    }
1025
1026    #[test]
1027    fn test_lagrange_1_triangle() {
1028        let e = lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1029        assert_eq!(e.value_size(), 1);
1030        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1031        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1032        *points.get_mut([0, 0]).unwrap() = 0.0;
1033        *points.get_mut([1, 0]).unwrap() = 0.0;
1034        *points.get_mut([0, 1]).unwrap() = 1.0;
1035        *points.get_mut([1, 1]).unwrap() = 0.0;
1036        *points.get_mut([0, 2]).unwrap() = 0.0;
1037        *points.get_mut([1, 2]).unwrap() = 1.0;
1038        *points.get_mut([0, 3]).unwrap() = 0.5;
1039        *points.get_mut([1, 3]).unwrap() = 0.0;
1040        *points.get_mut([0, 4]).unwrap() = 0.0;
1041        *points.get_mut([1, 4]).unwrap() = 0.5;
1042        *points.get_mut([0, 5]).unwrap() = 0.5;
1043        *points.get_mut([1, 5]).unwrap() = 0.5;
1044        e.tabulate(&points, 0, &mut data);
1045
1046        for pt in 0..6 {
1047            let x = *points.get([0, pt]).unwrap();
1048            let y = *points.get([1, pt]).unwrap();
1049            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0 - x - y);
1050            assert_relative_eq!(*data.get([0, pt, 1, 0]).unwrap(), x);
1051            assert_relative_eq!(*data.get([0, pt, 2, 0]).unwrap(), y);
1052        }
1053        check_dofs(e);
1054    }
1055
1056    #[test]
1057    fn test_lagrange_0_quadrilateral() {
1058        let e = lagrange::create::<f64, f64>(
1059            ReferenceCellType::Quadrilateral,
1060            0,
1061            Continuity::Discontinuous,
1062        );
1063        assert_eq!(e.value_size(), 1);
1064        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1065        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1066        *points.get_mut([0, 0]).unwrap() = 0.0;
1067        *points.get_mut([1, 0]).unwrap() = 0.0;
1068        *points.get_mut([0, 1]).unwrap() = 1.0;
1069        *points.get_mut([1, 1]).unwrap() = 0.0;
1070        *points.get_mut([0, 2]).unwrap() = 0.0;
1071        *points.get_mut([1, 2]).unwrap() = 1.0;
1072        *points.get_mut([0, 3]).unwrap() = 0.5;
1073        *points.get_mut([1, 3]).unwrap() = 0.0;
1074        *points.get_mut([0, 4]).unwrap() = 0.0;
1075        *points.get_mut([1, 4]).unwrap() = 0.5;
1076        *points.get_mut([0, 5]).unwrap() = 0.5;
1077        *points.get_mut([1, 5]).unwrap() = 0.5;
1078        e.tabulate(&points, 0, &mut data);
1079
1080        for pt in 0..6 {
1081            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0);
1082        }
1083        check_dofs(e);
1084    }
1085
1086    #[test]
1087    fn test_lagrange_1_quadrilateral() {
1088        let e =
1089            lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 1, Continuity::Standard);
1090        assert_eq!(e.value_size(), 1);
1091        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1092        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1093        *points.get_mut([0, 0]).unwrap() = 0.0;
1094        *points.get_mut([1, 0]).unwrap() = 0.0;
1095        *points.get_mut([0, 1]).unwrap() = 1.0;
1096        *points.get_mut([1, 1]).unwrap() = 0.0;
1097        *points.get_mut([0, 2]).unwrap() = 0.0;
1098        *points.get_mut([1, 2]).unwrap() = 1.0;
1099        *points.get_mut([0, 3]).unwrap() = 1.0;
1100        *points.get_mut([1, 3]).unwrap() = 1.0;
1101        *points.get_mut([0, 4]).unwrap() = 0.25;
1102        *points.get_mut([1, 4]).unwrap() = 0.5;
1103        *points.get_mut([0, 5]).unwrap() = 0.3;
1104        *points.get_mut([1, 5]).unwrap() = 0.2;
1105
1106        e.tabulate(&points, 0, &mut data);
1107
1108        for pt in 0..6 {
1109            let x = *points.get([0, pt]).unwrap();
1110            let y = *points.get([1, pt]).unwrap();
1111            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), (1.0 - x) * (1.0 - y));
1112            assert_relative_eq!(*data.get([0, pt, 1, 0]).unwrap(), x * (1.0 - y));
1113            assert_relative_eq!(*data.get([0, pt, 2, 0]).unwrap(), (1.0 - x) * y);
1114            assert_relative_eq!(*data.get([0, pt, 3, 0]).unwrap(), x * y);
1115        }
1116        check_dofs(e);
1117    }
1118
1119    #[test]
1120    fn test_lagrange_2_quadrilateral() {
1121        let e =
1122            lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 2, Continuity::Standard);
1123        assert_eq!(e.value_size(), 1);
1124        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1125        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1126        *points.get_mut([0, 0]).unwrap() = 0.0;
1127        *points.get_mut([1, 0]).unwrap() = 0.0;
1128        *points.get_mut([0, 1]).unwrap() = 1.0;
1129        *points.get_mut([1, 1]).unwrap() = 0.0;
1130        *points.get_mut([0, 2]).unwrap() = 0.0;
1131        *points.get_mut([1, 2]).unwrap() = 1.0;
1132        *points.get_mut([0, 3]).unwrap() = 1.0;
1133        *points.get_mut([1, 3]).unwrap() = 1.0;
1134        *points.get_mut([0, 4]).unwrap() = 0.25;
1135        *points.get_mut([1, 4]).unwrap() = 0.5;
1136        *points.get_mut([0, 5]).unwrap() = 0.3;
1137        *points.get_mut([1, 5]).unwrap() = 0.2;
1138
1139        e.tabulate(&points, 0, &mut data);
1140
1141        for pt in 0..6 {
1142            let x = *points.get([0, pt]).unwrap();
1143            let y = *points.get([1, pt]).unwrap();
1144            assert_relative_eq!(
1145                *data.get([0, pt, 0, 0]).unwrap(),
1146                (1.0 - x) * (1.0 - 2.0 * x) * (1.0 - y) * (1.0 - 2.0 * y),
1147                epsilon = 1e-14
1148            );
1149            assert_relative_eq!(
1150                *data.get([0, pt, 1, 0]).unwrap(),
1151                x * (2.0 * x - 1.0) * (1.0 - y) * (1.0 - 2.0 * y),
1152                epsilon = 1e-14
1153            );
1154            assert_relative_eq!(
1155                *data.get([0, pt, 2, 0]).unwrap(),
1156                (1.0 - x) * (1.0 - 2.0 * x) * y * (2.0 * y - 1.0),
1157                epsilon = 1e-14
1158            );
1159            assert_relative_eq!(
1160                *data.get([0, pt, 3, 0]).unwrap(),
1161                x * (2.0 * x - 1.0) * y * (2.0 * y - 1.0),
1162                epsilon = 1e-14
1163            );
1164            assert_relative_eq!(
1165                *data.get([0, pt, 4, 0]).unwrap(),
1166                4.0 * x * (1.0 - x) * (1.0 - y) * (1.0 - 2.0 * y),
1167                epsilon = 1e-14
1168            );
1169            assert_relative_eq!(
1170                *data.get([0, pt, 5, 0]).unwrap(),
1171                (1.0 - x) * (1.0 - 2.0 * x) * 4.0 * y * (1.0 - y),
1172                epsilon = 1e-14
1173            );
1174            assert_relative_eq!(
1175                *data.get([0, pt, 6, 0]).unwrap(),
1176                x * (2.0 * x - 1.0) * 4.0 * y * (1.0 - y),
1177                epsilon = 1e-14
1178            );
1179            assert_relative_eq!(
1180                *data.get([0, pt, 7, 0]).unwrap(),
1181                4.0 * x * (1.0 - x) * y * (2.0 * y - 1.0),
1182                epsilon = 1e-14
1183            );
1184            assert_relative_eq!(
1185                *data.get([0, pt, 8, 0]).unwrap(),
1186                4.0 * x * (1.0 - x) * 4.0 * y * (1.0 - y),
1187                epsilon = 1e-14
1188            );
1189        }
1190        check_dofs(e);
1191    }
1192
1193    #[test]
1194    fn test_lagrange_0_tetrahedron() {
1195        let e = lagrange::create::<f64, f64>(
1196            ReferenceCellType::Tetrahedron,
1197            0,
1198            Continuity::Discontinuous,
1199        );
1200        assert_eq!(e.value_size(), 1);
1201        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1202        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1203        *points.get_mut([0, 0]).unwrap() = 0.0;
1204        *points.get_mut([1, 0]).unwrap() = 0.0;
1205        *points.get_mut([2, 0]).unwrap() = 0.0;
1206        *points.get_mut([0, 1]).unwrap() = 1.0;
1207        *points.get_mut([1, 1]).unwrap() = 0.0;
1208        *points.get_mut([2, 1]).unwrap() = 0.0;
1209        *points.get_mut([0, 2]).unwrap() = 0.0;
1210        *points.get_mut([1, 2]).unwrap() = 1.0;
1211        *points.get_mut([2, 2]).unwrap() = 0.0;
1212        *points.get_mut([0, 3]).unwrap() = 0.5;
1213        *points.get_mut([1, 3]).unwrap() = 0.0;
1214        *points.get_mut([2, 3]).unwrap() = 0.5;
1215        *points.get_mut([0, 4]).unwrap() = 0.0;
1216        *points.get_mut([1, 4]).unwrap() = 0.5;
1217        *points.get_mut([2, 4]).unwrap() = 0.5;
1218        *points.get_mut([0, 5]).unwrap() = 0.5;
1219        *points.get_mut([1, 5]).unwrap() = 0.2;
1220        *points.get_mut([2, 5]).unwrap() = 0.3;
1221        e.tabulate(&points, 0, &mut data);
1222
1223        for pt in 0..6 {
1224            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0);
1225        }
1226        check_dofs(e);
1227    }
1228
1229    #[test]
1230    fn test_lagrange_1_tetrahedron() {
1231        let e =
1232            lagrange::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1233        assert_eq!(e.value_size(), 1);
1234        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1235        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1236        *points.get_mut([0, 0]).unwrap() = 0.0;
1237        *points.get_mut([1, 0]).unwrap() = 0.0;
1238        *points.get_mut([2, 0]).unwrap() = 0.0;
1239        *points.get_mut([0, 1]).unwrap() = 1.0;
1240        *points.get_mut([1, 1]).unwrap() = 0.0;
1241        *points.get_mut([2, 1]).unwrap() = 0.0;
1242        *points.get_mut([0, 2]).unwrap() = 0.0;
1243        *points.get_mut([1, 2]).unwrap() = 0.8;
1244        *points.get_mut([2, 2]).unwrap() = 0.2;
1245        *points.get_mut([0, 3]).unwrap() = 0.0;
1246        *points.get_mut([1, 3]).unwrap() = 0.0;
1247        *points.get_mut([2, 3]).unwrap() = 0.8;
1248        *points.get_mut([0, 4]).unwrap() = 0.25;
1249        *points.get_mut([1, 4]).unwrap() = 0.5;
1250        *points.get_mut([2, 4]).unwrap() = 0.1;
1251        *points.get_mut([0, 5]).unwrap() = 0.2;
1252        *points.get_mut([1, 5]).unwrap() = 0.1;
1253        *points.get_mut([2, 5]).unwrap() = 0.15;
1254
1255        e.tabulate(&points, 0, &mut data);
1256
1257        for pt in 0..6 {
1258            let x = *points.get([0, pt]).unwrap();
1259            let y = *points.get([1, pt]).unwrap();
1260            let z = *points.get([2, pt]).unwrap();
1261            assert_relative_eq!(
1262                *data.get([0, pt, 0, 0]).unwrap(),
1263                1.0 - x - y - z,
1264                epsilon = 1e-14
1265            );
1266            assert_relative_eq!(*data.get([0, pt, 1, 0]).unwrap(), x, epsilon = 1e-14);
1267            assert_relative_eq!(*data.get([0, pt, 2, 0]).unwrap(), y, epsilon = 1e-14);
1268            assert_relative_eq!(*data.get([0, pt, 3, 0]).unwrap(), z, epsilon = 1e-14);
1269        }
1270        check_dofs(e);
1271    }
1272
1273    #[test]
1274    fn test_lagrange_0_hexahedron() {
1275        let e = lagrange::create::<f64, f64>(
1276            ReferenceCellType::Hexahedron,
1277            0,
1278            Continuity::Discontinuous,
1279        );
1280        assert_eq!(e.value_size(), 1);
1281        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1282        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1283        *points.get_mut([0, 0]).unwrap() = 0.0;
1284        *points.get_mut([1, 0]).unwrap() = 0.0;
1285        *points.get_mut([2, 0]).unwrap() = 0.0;
1286        *points.get_mut([0, 1]).unwrap() = 1.0;
1287        *points.get_mut([1, 1]).unwrap() = 0.0;
1288        *points.get_mut([2, 1]).unwrap() = 0.0;
1289        *points.get_mut([0, 2]).unwrap() = 0.0;
1290        *points.get_mut([1, 2]).unwrap() = 1.0;
1291        *points.get_mut([2, 2]).unwrap() = 0.0;
1292        *points.get_mut([0, 3]).unwrap() = 0.5;
1293        *points.get_mut([1, 3]).unwrap() = 0.0;
1294        *points.get_mut([2, 3]).unwrap() = 0.5;
1295        *points.get_mut([0, 4]).unwrap() = 0.0;
1296        *points.get_mut([1, 4]).unwrap() = 0.5;
1297        *points.get_mut([2, 4]).unwrap() = 0.5;
1298        *points.get_mut([0, 5]).unwrap() = 0.5;
1299        *points.get_mut([1, 5]).unwrap() = 0.5;
1300        *points.get_mut([2, 5]).unwrap() = 0.5;
1301        e.tabulate(&points, 0, &mut data);
1302
1303        for pt in 0..6 {
1304            assert_relative_eq!(*data.get([0, pt, 0, 0]).unwrap(), 1.0);
1305        }
1306        check_dofs(e);
1307    }
1308
1309    #[test]
1310    fn test_lagrange_1_hexahedron() {
1311        let e =
1312            lagrange::create::<f64, f64>(ReferenceCellType::Hexahedron, 1, Continuity::Standard);
1313        assert_eq!(e.value_size(), 1);
1314        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1315        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1316        *points.get_mut([0, 0]).unwrap() = 0.0;
1317        *points.get_mut([1, 0]).unwrap() = 0.0;
1318        *points.get_mut([2, 0]).unwrap() = 0.0;
1319        *points.get_mut([0, 1]).unwrap() = 1.0;
1320        *points.get_mut([1, 1]).unwrap() = 0.0;
1321        *points.get_mut([2, 1]).unwrap() = 0.0;
1322        *points.get_mut([0, 2]).unwrap() = 0.0;
1323        *points.get_mut([1, 2]).unwrap() = 1.0;
1324        *points.get_mut([2, 2]).unwrap() = 1.0;
1325        *points.get_mut([0, 3]).unwrap() = 1.0;
1326        *points.get_mut([1, 3]).unwrap() = 1.0;
1327        *points.get_mut([2, 3]).unwrap() = 1.0;
1328        *points.get_mut([0, 4]).unwrap() = 0.25;
1329        *points.get_mut([1, 4]).unwrap() = 0.5;
1330        *points.get_mut([2, 4]).unwrap() = 0.1;
1331        *points.get_mut([0, 5]).unwrap() = 0.3;
1332        *points.get_mut([1, 5]).unwrap() = 0.2;
1333        *points.get_mut([2, 5]).unwrap() = 0.4;
1334
1335        e.tabulate(&points, 0, &mut data);
1336
1337        for pt in 0..6 {
1338            let x = *points.get([0, pt]).unwrap();
1339            let y = *points.get([1, pt]).unwrap();
1340            let z = *points.get([2, pt]).unwrap();
1341            assert_relative_eq!(
1342                *data.get([0, pt, 0, 0]).unwrap(),
1343                (1.0 - x) * (1.0 - y) * (1.0 - z),
1344                epsilon = 1e-14
1345            );
1346            assert_relative_eq!(
1347                *data.get([0, pt, 1, 0]).unwrap(),
1348                x * (1.0 - y) * (1.0 - z),
1349                epsilon = 1e-14
1350            );
1351            assert_relative_eq!(
1352                *data.get([0, pt, 2, 0]).unwrap(),
1353                (1.0 - x) * y * (1.0 - z),
1354                epsilon = 1e-14
1355            );
1356            assert_relative_eq!(
1357                *data.get([0, pt, 3, 0]).unwrap(),
1358                x * y * (1.0 - z),
1359                epsilon = 1e-14
1360            );
1361            assert_relative_eq!(
1362                *data.get([0, pt, 4, 0]).unwrap(),
1363                (1.0 - x) * (1.0 - y) * z,
1364                epsilon = 1e-14
1365            );
1366            assert_relative_eq!(
1367                *data.get([0, pt, 5, 0]).unwrap(),
1368                x * (1.0 - y) * z,
1369                epsilon = 1e-14
1370            );
1371            assert_relative_eq!(
1372                *data.get([0, pt, 6, 0]).unwrap(),
1373                (1.0 - x) * y * z,
1374                epsilon = 1e-14
1375            );
1376            assert_relative_eq!(
1377                *data.get([0, pt, 7, 0]).unwrap(),
1378                x * y * z,
1379                epsilon = 1e-14
1380            );
1381        }
1382        check_dofs(e);
1383    }
1384
1385    #[test]
1386    fn test_lagrange_higher_degree_triangle() {
1387        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1388        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Standard);
1389        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 4, Continuity::Standard);
1390        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 5, Continuity::Standard);
1391
1392        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Discontinuous);
1393        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Discontinuous);
1394        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 4, Continuity::Discontinuous);
1395        lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 5, Continuity::Discontinuous);
1396    }
1397
1398    #[test]
1399    fn test_lagrange_higher_degree_interval() {
1400        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 2, Continuity::Standard);
1401        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 3, Continuity::Standard);
1402        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 4, Continuity::Standard);
1403        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 5, Continuity::Standard);
1404
1405        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 2, Continuity::Discontinuous);
1406        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 3, Continuity::Discontinuous);
1407        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 4, Continuity::Discontinuous);
1408        lagrange::create::<f64, f64>(ReferenceCellType::Interval, 5, Continuity::Discontinuous);
1409    }
1410
1411    #[test]
1412    fn test_lagrange_higher_degree_quadrilateral() {
1413        lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 2, Continuity::Standard);
1414        lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 3, Continuity::Standard);
1415        lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 4, Continuity::Standard);
1416        lagrange::create::<f64, f64>(ReferenceCellType::Quadrilateral, 5, Continuity::Standard);
1417
1418        lagrange::create::<f64, f64>(
1419            ReferenceCellType::Quadrilateral,
1420            2,
1421            Continuity::Discontinuous,
1422        );
1423        lagrange::create::<f64, f64>(
1424            ReferenceCellType::Quadrilateral,
1425            3,
1426            Continuity::Discontinuous,
1427        );
1428        lagrange::create::<f64, f64>(
1429            ReferenceCellType::Quadrilateral,
1430            4,
1431            Continuity::Discontinuous,
1432        );
1433        lagrange::create::<f64, f64>(
1434            ReferenceCellType::Quadrilateral,
1435            5,
1436            Continuity::Discontinuous,
1437        );
1438    }
1439
1440    #[test]
1441    fn test_raviart_thomas_1_triangle() {
1442        let e = raviart_thomas::create::<f64, f64>(
1443            ReferenceCellType::Triangle,
1444            1,
1445            Continuity::Standard,
1446        );
1447        assert_eq!(e.value_size(), 2);
1448        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1449        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1450        *points.get_mut([0, 0]).unwrap() = 0.0;
1451        *points.get_mut([1, 0]).unwrap() = 0.0;
1452        *points.get_mut([0, 1]).unwrap() = 1.0;
1453        *points.get_mut([1, 1]).unwrap() = 0.0;
1454        *points.get_mut([0, 2]).unwrap() = 0.0;
1455        *points.get_mut([1, 2]).unwrap() = 1.0;
1456        *points.get_mut([0, 3]).unwrap() = 0.5;
1457        *points.get_mut([1, 3]).unwrap() = 0.0;
1458        *points.get_mut([0, 4]).unwrap() = 0.0;
1459        *points.get_mut([1, 4]).unwrap() = 0.5;
1460        *points.get_mut([0, 5]).unwrap() = 0.5;
1461        *points.get_mut([1, 5]).unwrap() = 0.5;
1462        e.tabulate(&points, 0, &mut data);
1463
1464        for pt in 0..6 {
1465            let x = *points.get([0, pt]).unwrap();
1466            let y = *points.get([1, pt]).unwrap();
1467            for (i, basis_f) in [[-x, -y], [x - 1.0, y], [-x, 1.0 - y]].iter().enumerate() {
1468                for (d, value) in basis_f.iter().enumerate() {
1469                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1470                }
1471            }
1472        }
1473        check_dofs(e);
1474    }
1475
1476    #[test]
1477    fn test_raviart_thomas_2_triangle() {
1478        let e = raviart_thomas::create::<f64, f64>(
1479            ReferenceCellType::Triangle,
1480            2,
1481            Continuity::Standard,
1482        );
1483        assert_eq!(e.value_size(), 2);
1484        check_dofs(e);
1485    }
1486
1487    #[test]
1488    fn test_raviart_thomas_3_triangle() {
1489        let e = raviart_thomas::create::<f64, f64>(
1490            ReferenceCellType::Triangle,
1491            3,
1492            Continuity::Standard,
1493        );
1494        assert_eq!(e.value_size(), 2);
1495        check_dofs(e);
1496    }
1497
1498    #[test]
1499    fn test_raviart_thomas_1_quadrilateral() {
1500        let e = raviart_thomas::create::<f64, f64>(
1501            ReferenceCellType::Quadrilateral,
1502            1,
1503            Continuity::Standard,
1504        );
1505        assert_eq!(e.value_size(), 2);
1506        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1507        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1508        *points.get_mut([0, 0]).unwrap() = 0.0;
1509        *points.get_mut([1, 0]).unwrap() = 0.0;
1510        *points.get_mut([0, 1]).unwrap() = 1.0;
1511        *points.get_mut([1, 1]).unwrap() = 0.0;
1512        *points.get_mut([0, 2]).unwrap() = 0.0;
1513        *points.get_mut([1, 2]).unwrap() = 1.0;
1514        *points.get_mut([0, 3]).unwrap() = 0.5;
1515        *points.get_mut([1, 3]).unwrap() = 0.0;
1516        *points.get_mut([0, 4]).unwrap() = 1.0;
1517        *points.get_mut([1, 4]).unwrap() = 0.5;
1518        *points.get_mut([0, 5]).unwrap() = 0.5;
1519        *points.get_mut([1, 5]).unwrap() = 0.5;
1520        e.tabulate(&points, 0, &mut data);
1521
1522        for pt in 0..6 {
1523            let x = *points.get([0, pt]).unwrap();
1524            let y = *points.get([1, pt]).unwrap();
1525            for (i, basis_f) in [[0.0, 1.0 - y], [x - 1.0, 0.0], [-x, 0.0], [0.0, y]]
1526                .iter()
1527                .enumerate()
1528            {
1529                for (d, value) in basis_f.iter().enumerate() {
1530                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1531                }
1532            }
1533        }
1534        check_dofs(e);
1535    }
1536
1537    #[test]
1538    fn test_raviart_thomas_2_quadrilateral() {
1539        let e = raviart_thomas::create::<f64, f64>(
1540            ReferenceCellType::Quadrilateral,
1541            2,
1542            Continuity::Standard,
1543        );
1544        assert_eq!(e.value_size(), 2);
1545        check_dofs(e);
1546    }
1547
1548    #[test]
1549    fn test_raviart_thomas_3_quadrilateral() {
1550        let e = raviart_thomas::create::<f64, f64>(
1551            ReferenceCellType::Quadrilateral,
1552            3,
1553            Continuity::Standard,
1554        );
1555        assert_eq!(e.value_size(), 2);
1556        check_dofs(e);
1557    }
1558
1559    #[test]
1560    fn test_raviart_thomas_1_tetrahedron() {
1561        let e = raviart_thomas::create::<f64, f64>(
1562            ReferenceCellType::Tetrahedron,
1563            1,
1564            Continuity::Standard,
1565        );
1566        assert_eq!(e.value_size(), 3);
1567        check_dofs(e);
1568    }
1569
1570    #[test]
1571    fn test_raviart_thomas_2_tetrahedron() {
1572        let e = raviart_thomas::create::<f64, f64>(
1573            ReferenceCellType::Tetrahedron,
1574            2,
1575            Continuity::Standard,
1576        );
1577        assert_eq!(e.value_size(), 3);
1578        check_dofs(e);
1579    }
1580
1581    #[test]
1582    fn test_raviart_thomas_3_tetrahedron() {
1583        let e = raviart_thomas::create::<f64, f64>(
1584            ReferenceCellType::Tetrahedron,
1585            3,
1586            Continuity::Standard,
1587        );
1588        assert_eq!(e.value_size(), 3);
1589        check_dofs(e);
1590    }
1591
1592    #[test]
1593    fn test_raviart_thomas_1_hexahedron() {
1594        let e = raviart_thomas::create::<f64, f64>(
1595            ReferenceCellType::Hexahedron,
1596            1,
1597            Continuity::Standard,
1598        );
1599        assert_eq!(e.value_size(), 3);
1600        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1601        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1602        *points.get_mut([0, 0]).unwrap() = 0.0;
1603        *points.get_mut([1, 0]).unwrap() = 0.0;
1604        *points.get_mut([2, 0]).unwrap() = 0.0;
1605        *points.get_mut([0, 1]).unwrap() = 1.0;
1606        *points.get_mut([1, 1]).unwrap() = 0.0;
1607        *points.get_mut([2, 1]).unwrap() = 0.8;
1608        *points.get_mut([0, 2]).unwrap() = 0.0;
1609        *points.get_mut([1, 2]).unwrap() = 1.0;
1610        *points.get_mut([2, 2]).unwrap() = 1.0;
1611        *points.get_mut([0, 3]).unwrap() = 0.5;
1612        *points.get_mut([1, 3]).unwrap() = 0.0;
1613        *points.get_mut([2, 3]).unwrap() = 0.1;
1614        *points.get_mut([0, 4]).unwrap() = 1.0;
1615        *points.get_mut([1, 4]).unwrap() = 0.5;
1616        *points.get_mut([2, 4]).unwrap() = 0.5;
1617        *points.get_mut([0, 5]).unwrap() = 0.5;
1618        *points.get_mut([1, 5]).unwrap() = 0.5;
1619        *points.get_mut([2, 5]).unwrap() = 1.0;
1620        e.tabulate(&points, 0, &mut data);
1621
1622        for pt in 0..6 {
1623            let x = *points.get([0, pt]).unwrap();
1624            let y = *points.get([1, pt]).unwrap();
1625            let z = *points.get([2, pt]).unwrap();
1626            for (i, basis_f) in [
1627                [0.0, 0.0, 1.0 - z],
1628                [0.0, y - 1.0, 0.0],
1629                [1.0 - x, 0.0, 0.0],
1630                [x, 0.0, 0.0],
1631                [0.0, -y, 0.0],
1632                [0.0, 0.0, z],
1633            ]
1634            .iter()
1635            .enumerate()
1636            {
1637                for (d, value) in basis_f.iter().enumerate() {
1638                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1639                }
1640            }
1641        }
1642        check_dofs(e);
1643    }
1644
1645    #[test]
1646    fn test_raviart_thomas_2_hexahedron() {
1647        let e = raviart_thomas::create::<f64, f64>(
1648            ReferenceCellType::Hexahedron,
1649            2,
1650            Continuity::Standard,
1651        );
1652        assert_eq!(e.value_size(), 3);
1653        check_dofs(e);
1654    }
1655
1656    #[test]
1657    fn test_nedelec_1_triangle() {
1658        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1659        assert_eq!(e.value_size(), 2);
1660        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1661        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1662        *points.get_mut([0, 0]).unwrap() = 0.0;
1663        *points.get_mut([1, 0]).unwrap() = 0.0;
1664        *points.get_mut([0, 1]).unwrap() = 1.0;
1665        *points.get_mut([1, 1]).unwrap() = 0.0;
1666        *points.get_mut([0, 2]).unwrap() = 0.0;
1667        *points.get_mut([1, 2]).unwrap() = 1.0;
1668        *points.get_mut([0, 3]).unwrap() = 0.5;
1669        *points.get_mut([1, 3]).unwrap() = 0.0;
1670        *points.get_mut([0, 4]).unwrap() = 0.0;
1671        *points.get_mut([1, 4]).unwrap() = 0.5;
1672        *points.get_mut([0, 5]).unwrap() = 0.5;
1673        *points.get_mut([1, 5]).unwrap() = 0.5;
1674        e.tabulate(&points, 0, &mut data);
1675
1676        for pt in 0..6 {
1677            let x = *points.get([0, pt]).unwrap();
1678            let y = *points.get([1, pt]).unwrap();
1679            for (i, basis_f) in [[-y, x], [y, 1.0 - x], [1.0 - y, x]].iter().enumerate() {
1680                for (d, value) in basis_f.iter().enumerate() {
1681                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1682                }
1683            }
1684        }
1685        check_dofs(e);
1686    }
1687
1688    #[test]
1689    fn test_nedelec_2_triangle() {
1690        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1691        assert_eq!(e.value_size(), 2);
1692        check_dofs(e);
1693    }
1694
1695    #[test]
1696    fn test_nedelec_3_triangle() {
1697        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Standard);
1698        assert_eq!(e.value_size(), 2);
1699        check_dofs(e);
1700    }
1701
1702    #[test]
1703    fn test_nedelec_1_quadrilateral() {
1704        let e =
1705            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 1, Continuity::Standard);
1706        assert_eq!(e.value_size(), 2);
1707        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1708        let mut points = rlst_dynamic_array!(f64, [2, 6]);
1709        *points.get_mut([0, 0]).unwrap() = 0.0;
1710        *points.get_mut([1, 0]).unwrap() = 0.0;
1711        *points.get_mut([0, 1]).unwrap() = 1.0;
1712        *points.get_mut([1, 1]).unwrap() = 0.0;
1713        *points.get_mut([0, 2]).unwrap() = 0.0;
1714        *points.get_mut([1, 2]).unwrap() = 1.0;
1715        *points.get_mut([0, 3]).unwrap() = 0.5;
1716        *points.get_mut([1, 3]).unwrap() = 0.0;
1717        *points.get_mut([0, 4]).unwrap() = 1.0;
1718        *points.get_mut([1, 4]).unwrap() = 0.5;
1719        *points.get_mut([0, 5]).unwrap() = 0.5;
1720        *points.get_mut([1, 5]).unwrap() = 0.5;
1721        e.tabulate(&points, 0, &mut data);
1722
1723        for pt in 0..6 {
1724            let x = *points.get([0, pt]).unwrap();
1725            let y = *points.get([1, pt]).unwrap();
1726            for (i, basis_f) in [[1.0 - y, 0.0], [0.0, 1.0 - x], [0.0, x], [y, 0.0]]
1727                .iter()
1728                .enumerate()
1729            {
1730                for (d, value) in basis_f.iter().enumerate() {
1731                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1732                }
1733            }
1734        }
1735        check_dofs(e);
1736    }
1737
1738    #[test]
1739    fn test_nedelec_2_quadrilateral() {
1740        let e =
1741            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 2, Continuity::Standard);
1742        assert_eq!(e.value_size(), 2);
1743        check_dofs(e);
1744    }
1745
1746    #[test]
1747    fn test_nedelec_3_quadrilateral() {
1748        let e =
1749            nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 3, Continuity::Standard);
1750        assert_eq!(e.value_size(), 2);
1751        check_dofs(e);
1752    }
1753
1754    #[test]
1755    fn test_nedelec_1_tetrahedron() {
1756        let e =
1757            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1758        assert_eq!(e.value_size(), 3);
1759        check_dofs(e);
1760    }
1761
1762    #[test]
1763    fn test_nedelec_2_tetrahedron() {
1764        let e =
1765            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
1766        assert_eq!(e.value_size(), 3);
1767        check_dofs(e);
1768    }
1769
1770    #[test]
1771    fn test_nedelec_3_tetrahedron() {
1772        let e =
1773            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 3, Continuity::Standard);
1774        assert_eq!(e.value_size(), 3);
1775        check_dofs(e);
1776    }
1777
1778    #[test]
1779    fn test_nedelec_1_hexahedron() {
1780        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 1, Continuity::Standard);
1781        assert_eq!(e.value_size(), 3);
1782        let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1783        let mut points = rlst_dynamic_array!(f64, [3, 6]);
1784        *points.get_mut([0, 0]).unwrap() = 0.0;
1785        *points.get_mut([1, 0]).unwrap() = 0.0;
1786        *points.get_mut([2, 0]).unwrap() = 0.0;
1787        *points.get_mut([0, 1]).unwrap() = 1.0;
1788        *points.get_mut([1, 1]).unwrap() = 0.0;
1789        *points.get_mut([2, 1]).unwrap() = 0.8;
1790        *points.get_mut([0, 2]).unwrap() = 0.0;
1791        *points.get_mut([1, 2]).unwrap() = 1.0;
1792        *points.get_mut([2, 2]).unwrap() = 1.0;
1793        *points.get_mut([0, 3]).unwrap() = 0.5;
1794        *points.get_mut([1, 3]).unwrap() = 0.0;
1795        *points.get_mut([2, 3]).unwrap() = 0.1;
1796        *points.get_mut([0, 4]).unwrap() = 1.0;
1797        *points.get_mut([1, 4]).unwrap() = 0.5;
1798        *points.get_mut([2, 4]).unwrap() = 0.5;
1799        *points.get_mut([0, 5]).unwrap() = 0.5;
1800        *points.get_mut([1, 5]).unwrap() = 0.5;
1801        *points.get_mut([2, 5]).unwrap() = 1.0;
1802        e.tabulate(&points, 0, &mut data);
1803
1804        for pt in 0..6 {
1805            let x = *points.get([0, pt]).unwrap();
1806            let y = *points.get([1, pt]).unwrap();
1807            let z = *points.get([2, pt]).unwrap();
1808            for (i, basis_f) in [
1809                [(1.0 - y) * (1.0 - z), 0.0, 0.0],
1810                [0.0, (1.0 - x) * (1.0 - z), 0.0],
1811                [0.0, 0.0, (1.0 - x) * (1.0 - y)],
1812                [0.0, x * (1.0 - z), 0.0],
1813                [0.0, 0.0, x * (1.0 - y)],
1814                [y * (1.0 - z), 0.0, 0.0],
1815                [0.0, 0.0, (1.0 - x) * y],
1816                [0.0, 0.0, x * y],
1817                [(1.0 - y) * z, 0.0, 0.0],
1818                [0.0, (1.0 - x) * z, 0.0],
1819                [0.0, x * z, 0.0],
1820                [y * z, 0.0, 0.0],
1821            ]
1822            .iter()
1823            .enumerate()
1824            {
1825                for (d, value) in basis_f.iter().enumerate() {
1826                    assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1827                }
1828            }
1829        }
1830        check_dofs(e);
1831    }
1832
1833    #[test]
1834    fn test_nedelec_2_hexahedron() {
1835        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1836        assert_eq!(e.value_size(), 3);
1837        check_dofs(e);
1838    }
1839
1840    macro_rules! test_entity_closure_dofs_lagrange {
1841        ($cell:ident, $degree:expr) => {
1842            paste! {
1843                #[test]
1844                fn [<test_entity_closure_dofs_ $cell:lower _ $degree>]() {
1845                    let e = lagrange::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard);
1846                    let c = reference_cell::connectivity(ReferenceCellType::[<$cell>]);
1847
1848                    for (dim, entities) in c.iter().enumerate() {
1849                        for (n, entity) in entities.iter().enumerate() {
1850                            let ecd = e.entity_closure_dofs(dim, n).unwrap();
1851                            let mut len = 0;
1852                            for (sub_dim, sub_entities) in entity.iter().take(dim + 1).enumerate() {
1853                                for sub_entity in sub_entities {
1854                                    let dofs = e.entity_dofs(sub_dim, *sub_entity).unwrap();
1855                                    len += dofs.len();
1856                                    for dof in dofs {
1857                                        assert!(ecd.contains(dof));
1858                                    }
1859                                }
1860                            }
1861                            assert_eq!(ecd.len(), len);
1862                        }
1863                    }
1864                }
1865            }
1866        };
1867    }
1868
1869    test_entity_closure_dofs_lagrange!(Interval, 2);
1870    test_entity_closure_dofs_lagrange!(Interval, 3);
1871    test_entity_closure_dofs_lagrange!(Interval, 4);
1872    test_entity_closure_dofs_lagrange!(Interval, 5);
1873    test_entity_closure_dofs_lagrange!(Triangle, 2);
1874    test_entity_closure_dofs_lagrange!(Triangle, 3);
1875    test_entity_closure_dofs_lagrange!(Triangle, 4);
1876    test_entity_closure_dofs_lagrange!(Triangle, 5);
1877    test_entity_closure_dofs_lagrange!(Quadrilateral, 2);
1878    test_entity_closure_dofs_lagrange!(Quadrilateral, 3);
1879    test_entity_closure_dofs_lagrange!(Quadrilateral, 4);
1880    test_entity_closure_dofs_lagrange!(Quadrilateral, 5);
1881    test_entity_closure_dofs_lagrange!(Tetrahedron, 2);
1882    test_entity_closure_dofs_lagrange!(Tetrahedron, 3);
1883    test_entity_closure_dofs_lagrange!(Tetrahedron, 4);
1884    test_entity_closure_dofs_lagrange!(Tetrahedron, 5);
1885    test_entity_closure_dofs_lagrange!(Hexahedron, 2);
1886    test_entity_closure_dofs_lagrange!(Hexahedron, 3);
1887
1888    macro_rules! test_dof_transformations {
1889        ($cell:ident, $element:ident, $degree:expr) => {
1890            paste! {
1891
1892                #[test]
1893                fn [<test_dof_transformations_ $cell:lower _ $element:lower _ $degree>]() {
1894                    let e = [<$element>]::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard);
1895                    let tdim = reference_cell::dim(ReferenceCellType::[<$cell>]);
1896                    for edim in 1..tdim {
1897                        for entity in &reference_cell::entity_types(ReferenceCellType::[<$cell>])[edim] {
1898                            for t in match edim {
1899                                1 => vec![Transformation::Reflection],
1900                                2 => vec![Transformation::Reflection, Transformation::Rotation],
1901                                _ => { panic!("Shouldn't test this dimension"); },
1902                            } {
1903                                let order = match t {
1904                                    Transformation::Reflection => 2,
1905                                    Transformation::Rotation => match entity {
1906                                        ReferenceCellType::Triangle => 3,
1907                                        ReferenceCellType::Quadrilateral => 4,
1908                                        _ => {
1909                                            panic!("Unsupported entity type: {entity:?}");
1910                                        }
1911                                    },
1912                                };
1913                                match e.dof_transformation(*entity, t).unwrap() {
1914                                    DofTransformation::Identity => {}
1915                                    DofTransformation::Permutation(p) => {
1916                                        let mut data = (0..p.len()).collect::<Vec<_>>();
1917                                        for _ in 0..order {
1918                                            math::apply_permutation(p, &mut data);
1919                                        }
1920                                        for (i, j) in data.iter().enumerate() {
1921                                            assert_eq!(i, *j);
1922                                        }
1923                                    }
1924                                    DofTransformation::Transformation(m, p) => {
1925                                        let mut data = (0..p.len()).map(|i| i as f64).collect::<Vec<_>>();
1926                                        for _ in 0..order {
1927                                            math::apply_perm_and_matrix(m, p, &mut data);
1928                                        }
1929                                        for (i, j) in data.iter().enumerate() {
1930                                            assert_relative_eq!(i as f64, j, epsilon=1e-10);
1931                                        }
1932                                    }
1933                                }
1934                            }
1935                        }
1936                    }
1937                }
1938            }
1939        };
1940    }
1941
1942    test_dof_transformations!(Triangle, lagrange, 1);
1943    test_dof_transformations!(Triangle, lagrange, 2);
1944    test_dof_transformations!(Triangle, lagrange, 3);
1945    test_dof_transformations!(Quadrilateral, lagrange, 1);
1946    test_dof_transformations!(Quadrilateral, lagrange, 2);
1947    test_dof_transformations!(Quadrilateral, lagrange, 3);
1948    test_dof_transformations!(Tetrahedron, lagrange, 1);
1949    test_dof_transformations!(Tetrahedron, lagrange, 2);
1950    test_dof_transformations!(Tetrahedron, lagrange, 3);
1951    test_dof_transformations!(Hexahedron, lagrange, 1);
1952    test_dof_transformations!(Hexahedron, lagrange, 2);
1953    test_dof_transformations!(Hexahedron, lagrange, 3);
1954    test_dof_transformations!(Triangle, nedelec, 1);
1955    test_dof_transformations!(Triangle, nedelec, 2);
1956    test_dof_transformations!(Triangle, nedelec, 3);
1957    test_dof_transformations!(Quadrilateral, nedelec, 1);
1958    test_dof_transformations!(Quadrilateral, nedelec, 2);
1959    test_dof_transformations!(Quadrilateral, nedelec, 3);
1960    test_dof_transformations!(Tetrahedron, nedelec, 1);
1961    test_dof_transformations!(Tetrahedron, nedelec, 2);
1962    test_dof_transformations!(Tetrahedron, nedelec, 3);
1963    test_dof_transformations!(Hexahedron, nedelec, 1);
1964    test_dof_transformations!(Hexahedron, nedelec, 2);
1965    test_dof_transformations!(Triangle, raviart_thomas, 1);
1966    test_dof_transformations!(Triangle, raviart_thomas, 2);
1967    test_dof_transformations!(Triangle, raviart_thomas, 3);
1968    test_dof_transformations!(Quadrilateral, raviart_thomas, 1);
1969    test_dof_transformations!(Quadrilateral, raviart_thomas, 2);
1970    test_dof_transformations!(Quadrilateral, raviart_thomas, 3);
1971    test_dof_transformations!(Tetrahedron, raviart_thomas, 1);
1972    test_dof_transformations!(Tetrahedron, raviart_thomas, 2);
1973    test_dof_transformations!(Tetrahedron, raviart_thomas, 3);
1974    test_dof_transformations!(Hexahedron, raviart_thomas, 1);
1975    test_dof_transformations!(Hexahedron, raviart_thomas, 2);
1976
1977    fn assert_dof_transformation_equal<Array2Impl: ValueArrayImpl<f64, 2>>(
1978        p: &[usize],
1979        m: &Array<Array2Impl, 2>,
1980        expected: Vec<Vec<f64>>,
1981    ) {
1982        let ndofs = p.len();
1983        assert_eq!(m.shape()[0], ndofs);
1984        assert_eq!(m.shape()[1], ndofs);
1985        assert_eq!(expected.len(), ndofs);
1986        for i in 0..ndofs {
1987            let mut col = vec![0.0; ndofs];
1988            col[i] = 1.0;
1989            math::apply_perm_and_matrix(m, p, &mut col);
1990            for (j, c) in col.iter().enumerate() {
1991                assert_relative_eq!(expected[j][i], *c, epsilon = 1e-10);
1992            }
1993        }
1994    }
1995
1996    #[test]
1997    fn test_nedelec1_triangle_dof_transformations() {
1998        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1999        if let DofTransformation::Transformation(m, p) = e
2000            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2001            .unwrap()
2002        {
2003            assert_eq!(p.len(), 1);
2004            assert_eq!(m.shape()[0], 1);
2005            assert_eq!(m.shape()[1], 1);
2006            assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
2007        } else {
2008            panic!("Should be a linear transformation");
2009        }
2010    }
2011
2012    #[test]
2013    fn test_nedelec2_triangle_dof_transformations() {
2014        let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
2015        if let DofTransformation::Transformation(m, p) = e
2016            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2017            .unwrap()
2018        {
2019            assert_eq!(p.len(), 2);
2020            assert_eq!(m.shape()[0], 2);
2021            assert_eq!(m.shape()[1], 2);
2022            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
2023        } else {
2024            panic!("Should be a linear transformation");
2025        }
2026    }
2027
2028    #[test]
2029    fn test_nedelec1_tetrahedron_dof_transformations() {
2030        let e =
2031            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
2032        if let DofTransformation::Transformation(m, p) = e
2033            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2034            .unwrap()
2035        {
2036            assert_eq!(p.len(), 1);
2037            assert_eq!(m.shape()[0], 1);
2038            assert_eq!(m.shape()[1], 1);
2039            assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
2040        } else {
2041            panic!("Should be a linear transformation");
2042        }
2043        if let DofTransformation::Identity = e
2044            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
2045            .unwrap()
2046        {
2047        } else {
2048            panic!("Should be an identity transformation");
2049        }
2050        if let DofTransformation::Identity = e
2051            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
2052            .unwrap()
2053        {
2054        } else {
2055            panic!("Should be an identity transformation");
2056        }
2057    }
2058
2059    #[test]
2060    fn test_nedelec2_tetrahedron_dof_transformations() {
2061        let e =
2062            nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
2063        if let DofTransformation::Transformation(m, p) = e
2064            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2065            .unwrap()
2066        {
2067            assert_eq!(p.len(), 2);
2068            assert_eq!(m.shape()[0], 2);
2069            assert_eq!(m.shape()[1], 2);
2070            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
2071        } else {
2072            panic!("Should be a linear transformation");
2073        }
2074        if let DofTransformation::Permutation(p) = e
2075            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
2076            .unwrap()
2077        {
2078            assert_eq!(p.len(), 2);
2079            let mut indices = vec![0, 1];
2080            math::apply_permutation(p, &mut indices);
2081            assert_eq!(indices[0], 1);
2082            assert_eq!(indices[1], 0);
2083        } else {
2084            panic!("Should be a permutation");
2085        }
2086        if let DofTransformation::Transformation(m, p) = e
2087            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
2088            .unwrap()
2089        {
2090            assert_eq!(p.len(), 2);
2091            assert_eq!(m.shape()[0], 2);
2092            assert_eq!(m.shape()[1], 2);
2093            assert_dof_transformation_equal(p, m, vec![vec![-1.0, -1.0], vec![1.0, 0.0]]);
2094        } else {
2095            panic!("Should be a linear transformation");
2096        }
2097    }
2098
2099    #[test]
2100    fn test_nedelec2_quadrilateral_dof_transformations() {
2101        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
2102        if let DofTransformation::Transformation(m, p) = e
2103            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2104            .unwrap()
2105        {
2106            assert_eq!(p.len(), 2);
2107            assert_eq!(m.shape()[0], 2);
2108            assert_eq!(m.shape()[1], 2);
2109            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
2110        } else {
2111            panic!("Should be a linear transformation");
2112        }
2113    }
2114
2115    #[test]
2116    fn test_nedelec2_hexahedron_dof_transformations() {
2117        let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
2118        if let DofTransformation::Transformation(m, p) = e
2119            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2120            .unwrap()
2121        {
2122            assert_eq!(p.len(), 2);
2123            assert_eq!(m.shape()[0], 2);
2124            assert_eq!(m.shape()[1], 2);
2125            assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
2126        } else {
2127            panic!("Should be a linear transformation");
2128        }
2129        if let DofTransformation::Permutation(p) = e
2130            .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Reflection)
2131            .unwrap()
2132        {
2133            assert_eq!(p.len(), 4);
2134            let mut indices = vec![0, 1, 2, 3];
2135            math::apply_permutation(p, &mut indices);
2136            assert_eq!(indices[0], 1);
2137            assert_eq!(indices[1], 0);
2138            assert_eq!(indices[2], 3);
2139            assert_eq!(indices[3], 2);
2140        } else {
2141            panic!("Should be a permutation");
2142        }
2143        if let DofTransformation::Transformation(m, p) = e
2144            .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Rotation)
2145            .unwrap()
2146        {
2147            assert_eq!(p.len(), 4);
2148            assert_eq!(m.shape()[0], 4);
2149            assert_eq!(m.shape()[1], 4);
2150            assert_dof_transformation_equal(
2151                p,
2152                m,
2153                vec![
2154                    vec![0.0, -1.0, 0.0, 0.0],
2155                    vec![1.0, 0.0, 0.0, 0.0],
2156                    vec![0.0, 0.0, 0.0, 1.0],
2157                    vec![0.0, 0.0, 1.0, 0.0],
2158                ],
2159            );
2160        } else {
2161            panic!("Should be a linear transformation");
2162        }
2163    }
2164
2165    #[test]
2166    fn test_lagrange4_tetrahedron_dof_transformations() {
2167        let e =
2168            lagrange::create::<f64, f64>(ReferenceCellType::Tetrahedron, 4, Continuity::Standard);
2169        if let DofTransformation::Permutation(p) = e
2170            .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
2171            .unwrap()
2172        {
2173            assert_eq!(p.len(), 3);
2174            let mut indices = vec![0, 1, 2];
2175            math::apply_permutation(p, &mut indices);
2176            assert_eq!(indices[0], 2);
2177            assert_eq!(indices[1], 1);
2178            assert_eq!(indices[2], 0);
2179        } else {
2180            panic!("Should be a permutation");
2181        }
2182        if let DofTransformation::Permutation(p) = e
2183            .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
2184            .unwrap()
2185        {
2186            assert_eq!(p.len(), 3);
2187            let mut indices = vec![0, 1, 2];
2188            math::apply_permutation(p, &mut indices);
2189            assert_eq!(indices[0], 0);
2190            assert_eq!(indices[1], 2);
2191            assert_eq!(indices[2], 1);
2192        } else {
2193            panic!("Should be a permutation");
2194        }
2195        if let DofTransformation::Permutation(p) = e
2196            .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
2197            .unwrap()
2198        {
2199            assert_eq!(p.len(), 3);
2200            let mut indices = vec![0, 1, 2];
2201            math::apply_permutation(p, &mut indices);
2202            assert_eq!(indices[0], 1);
2203            assert_eq!(indices[1], 2);
2204            assert_eq!(indices[2], 0);
2205        } else {
2206            panic!("Should be a permutation");
2207        }
2208    }
2209
2210    #[test]
2211    fn test_dof_permuting_triangle() {
2212        let e = lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Standard);
2213
2214        let mut n = (0..10).collect::<Vec<_>>();
2215        e.apply_dof_permutations(&mut n, 7);
2216        for (i, j) in izip!(&n, [0, 1, 2, 4, 3, 6, 5, 8, 7, 9]) {
2217            assert_eq!(*i, j);
2218        }
2219
2220        let mut n = (0..20).collect::<Vec<_>>();
2221        e.apply_dof_permutations(&mut n, 7);
2222        for (i, j) in izip!(
2223            &n,
2224            [
2225                0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 12, 13, 10, 11, 16, 17, 14, 15, 18, 19
2226            ]
2227        ) {
2228            assert_eq!(*i, j);
2229        }
2230    }
2231
2232    #[test]
2233    fn test_dof_permuting_tetrahedron() {
2234        let e =
2235            lagrange::create::<f64, f64>(ReferenceCellType::Tetrahedron, 3, Continuity::Standard);
2236
2237        let mut n = (0..20).collect::<Vec<_>>();
2238        e.apply_dof_permutations(&mut n, 63);
2239        for (i, j) in izip!(
2240            &n,
2241            [
2242                0, 1, 2, 3, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 16, 17, 18, 19
2243            ]
2244        ) {
2245            assert_eq!(*i, j);
2246        }
2247    }
2248}