Skip to main content

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