test_push_forward/
test_push_forward.rs

1use approx::assert_relative_eq;
2use itertools::izip;
3use ndelement::{
4    ciarlet::{lagrange, nedelec, raviart_thomas},
5    traits::{FiniteElement, MappedFiniteElement},
6    types::{Continuity, ReferenceCellType},
7};
8use ndgrid::{
9    grid::local_grid::SingleElementGridBuilder,
10    traits::{Builder, GeometryMap, Grid},
11};
12use rlst::{DynArray, rlst_dynamic_array};
13
14/// Test Lagrange push forward
15fn test_lagrange_push_forward() {
16    let mut b = SingleElementGridBuilder::<f64>::new(3, (ReferenceCellType::Triangle, 1));
17    b.add_point(0, &[0.0, 0.0, 0.0]);
18    b.add_point(1, &[1.0, 0.0, 0.0]);
19    b.add_point(2, &[2.0, 0.0, 1.0]);
20    b.add_point(3, &[0.0, 1.0, 0.0]);
21    b.add_cell(0, &[0, 1, 3]);
22    b.add_cell(1, &[1, 2, 3]);
23    let grid = b.create_grid();
24
25    let e = lagrange::create::<f64, f64>(ReferenceCellType::Triangle, 4, Continuity::Standard);
26
27    let npts = 5;
28
29    let mut cell0_points = rlst_dynamic_array!(f64, [2, npts]);
30    for i in 0..npts {
31        cell0_points[[1, i]] = i as f64 / (npts - 1) as f64;
32        cell0_points[[0, i]] = 1.0 - cell0_points[[1, i]];
33    }
34    let mut cell0_table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
35    e.tabulate(&cell0_points, 0, &mut cell0_table);
36
37    let mut points = rlst_dynamic_array!(f64, [2, npts]);
38    for i in 0..npts {
39        points[[0, i]] = 0.0;
40        points[[1, i]] = i as f64 / (npts - 1) as f64;
41    }
42
43    let mut table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
44    e.tabulate(&points, 0, &mut table);
45
46    let gmap = grid.geometry_map(ReferenceCellType::Triangle, 1, &points);
47
48    let mut jacobians = rlst_dynamic_array!(f64, [grid.geometry_dim(), grid.topology_dim(), npts]);
49    let mut jinv = rlst_dynamic_array!(f64, [grid.topology_dim(), grid.geometry_dim(), npts]);
50    let mut jdets = vec![0.0; npts];
51
52    gmap.jacobians_inverses_dets(1, &mut jacobians, &mut jinv, &mut jdets);
53
54    let mut cell1_table = DynArray::<f64, 4>::from_shape(table.shape());
55    e.push_forward(&table, 0, &jacobians, &jdets, &jinv, &mut cell1_table);
56
57    // Check that basis functions are continuous between cells
58    for (cell0_dof, cell1_dof) in izip!(
59        e.entity_closure_dofs(1, 1).unwrap(),
60        e.entity_closure_dofs(1, 0).unwrap()
61    ) {
62        for i in 0..npts {
63            assert_relative_eq!(
64                cell0_table[[0, i, *cell0_dof, 0]],
65                cell1_table[[0, i, *cell1_dof, 0]],
66                epsilon = 1e-10
67            );
68        }
69    }
70}
71
72/// Test Ravaiart-Thomas push forward
73fn test_rt_push_forward() {
74    let mut b = SingleElementGridBuilder::<f64>::new(3, (ReferenceCellType::Triangle, 1));
75    b.add_point(0, &[0.0, 0.0, 0.0]);
76    b.add_point(1, &[1.0, 0.0, 0.0]);
77    b.add_point(2, &[2.0, 0.0, 1.0]);
78    b.add_point(3, &[0.0, 1.0, 0.0]);
79    b.add_cell(0, &[0, 1, 3]);
80    b.add_cell(1, &[1, 2, 3]);
81    let grid = b.create_grid();
82
83    let e =
84        raviart_thomas::create::<f64, f64>(ReferenceCellType::Triangle, 4, Continuity::Standard);
85
86    let npts = 5;
87
88    let mut cell0_points = rlst_dynamic_array!(f64, [2, npts]);
89    for i in 0..npts {
90        cell0_points[[1, i]] = i as f64 / (npts - 1) as f64;
91        cell0_points[[0, i]] = 1.0 - cell0_points[[1, i]];
92    }
93    let mut cell0_table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
94    e.tabulate(&cell0_points, 0, &mut cell0_table);
95
96    let mut points = rlst_dynamic_array!(f64, [2, npts]);
97    for i in 0..npts {
98        points[[0, i]] = 0.0;
99        points[[1, i]] = i as f64 / (npts - 1) as f64;
100    }
101
102    let mut table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
103    e.tabulate(&points, 0, &mut table);
104
105    let gmap = grid.geometry_map(ReferenceCellType::Triangle, 1, &points);
106
107    let mut jacobians = rlst_dynamic_array!(f64, [grid.geometry_dim(), grid.topology_dim(), npts]);
108    let mut jinv = rlst_dynamic_array!(f64, [grid.topology_dim(), grid.geometry_dim(), npts]);
109    let mut jdets = vec![0.0; npts];
110
111    gmap.jacobians_inverses_dets(1, &mut jacobians, &mut jinv, &mut jdets);
112
113    let mut cell1_table =
114        DynArray::<f64, 4>::from_shape([table.shape()[0], table.shape()[1], table.shape()[2], 3]);
115    e.push_forward(&table, 0, &jacobians, &jdets, &jinv, &mut cell1_table);
116
117    // Check that basis functions dotted with normal to edge are continuous between cells
118    for (cell0_dof, cell1_dof) in izip!(
119        e.entity_closure_dofs(1, 0).unwrap(),
120        e.entity_closure_dofs(1, 1).unwrap()
121    ) {
122        for i in 0..npts {
123            assert_relative_eq!(
124                (cell0_table[[0, i, *cell0_dof, 0]] + cell0_table[[0, i, *cell0_dof, 1]])
125                    / f64::sqrt(2.0),
126                (cell1_table[[0, i, *cell1_dof, 0]]
127                    + cell1_table[[0, i, *cell1_dof, 1]]
128                    + 2.0 * cell1_table[[0, i, *cell1_dof, 2]])
129                    / f64::sqrt(6.0),
130                epsilon = 1e-10
131            );
132        }
133    }
134}
135
136/// Test Nedelec push forward
137fn test_nc_push_forward() {
138    let mut b = SingleElementGridBuilder::<f64>::new(3, (ReferenceCellType::Triangle, 1));
139    b.add_point(0, &[0.0, 0.0, 0.0]);
140    b.add_point(1, &[1.0, 0.0, 0.0]);
141    b.add_point(2, &[2.0, 0.0, 1.0]);
142    b.add_point(3, &[0.0, 1.0, 0.0]);
143    b.add_cell(0, &[0, 1, 3]);
144    b.add_cell(1, &[1, 2, 3]);
145    let grid = b.create_grid();
146
147    let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 4, Continuity::Standard);
148
149    let npts = 5;
150
151    let mut cell0_points = rlst_dynamic_array!(f64, [2, npts]);
152    for i in 0..npts {
153        cell0_points[[1, i]] = i as f64 / (npts - 1) as f64;
154        cell0_points[[0, i]] = 1.0 - cell0_points[[1, i]];
155    }
156    let mut cell0_table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
157    e.tabulate(&cell0_points, 0, &mut cell0_table);
158
159    let mut points = rlst_dynamic_array!(f64, [2, npts]);
160    for i in 0..npts {
161        points[[0, i]] = 0.0;
162        points[[1, i]] = i as f64 / (npts - 1) as f64;
163    }
164
165    let mut table = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, npts));
166    e.tabulate(&points, 0, &mut table);
167
168    let gmap = grid.geometry_map(ReferenceCellType::Triangle, 1, &points);
169
170    let mut jacobians = rlst_dynamic_array!(f64, [grid.geometry_dim(), grid.topology_dim(), npts]);
171    let mut jinv = rlst_dynamic_array!(f64, [grid.topology_dim(), grid.geometry_dim(), npts]);
172    let mut jdets = vec![0.0; npts];
173
174    gmap.jacobians_inverses_dets(1, &mut jacobians, &mut jinv, &mut jdets);
175
176    let mut cell1_table =
177        DynArray::<f64, 4>::from_shape([table.shape()[0], table.shape()[1], table.shape()[2], 3]);
178    e.push_forward(&table, 0, &jacobians, &jdets, &jinv, &mut cell1_table);
179
180    // Check that basis functions dotted with tangent to edge are continuous between cells
181    for (cell0_dof, cell1_dof) in izip!(
182        e.entity_closure_dofs(1, 0).unwrap(),
183        e.entity_closure_dofs(1, 1).unwrap()
184    ) {
185        for i in 0..npts {
186            assert_relative_eq!(
187                (cell0_table[[0, i, *cell0_dof, 0]] - cell0_table[[0, i, *cell0_dof, 1]])
188                    / f64::sqrt(2.0),
189                (cell1_table[[0, i, *cell1_dof, 0]] - cell1_table[[0, i, *cell1_dof, 1]])
190                    / f64::sqrt(2.0),
191                epsilon = 1e-10
192            );
193        }
194    }
195}
196
197/// Run tests
198fn main() {
199    println!("Testing Lagrange push forward (identity)");
200    test_lagrange_push_forward();
201
202    println!("Testing Raviart-Thomas push forward (contravariant Piola)");
203    test_rt_push_forward();
204
205    println!("Testing Nedelec push forward (covariant Piola)");
206    test_nc_push_forward();
207}