1use approx::assert_relative_eq;
4use ndelement::{
5 ciarlet::{LagrangeElementFamily, LagrangeVariant, RaviartThomasElementFamily},
6 traits::{FiniteElement, MappedFiniteElement},
7 types::{Continuity, ReferenceCellType},
8};
9use ndfunctionspace::{FunctionSpaceImpl, traits::FunctionSpace};
10use ndmesh::{
11 shapes::regular_sphere,
12 traits::{Entity, GeometryMap, Mesh},
13};
14use quadraturerules::{Domain, QuadratureRule, single_integral_quadrature};
15use rlst::{DynArray, rlst_dynamic_array};
16
17fn test_lagrange_mass_matrix() {
19 let mesh = regular_sphere(0, ReferenceCellType::Triangle);
20
21 let family =
22 LagrangeElementFamily::<f64>::new(1, Continuity::Standard, LagrangeVariant::Equispaced);
23 let space = FunctionSpaceImpl::new(&mesh, &family);
24
25 let mut mass_matrix = rlst_dynamic_array!(f64, [space.local_size(), space.local_size()]);
26
27 let element = &space.elements()[0];
28
29 let (p, w) = single_integral_quadrature(
30 QuadratureRule::XiaoGimbutas,
31 Domain::Triangle,
32 2 * element.lagrange_superdegree(),
33 )
34 .unwrap();
35 let npts = w.len();
36 let mut pts = rlst_dynamic_array!(f64, [2, npts]);
37 for i in 0..w.len() {
38 for j in 0..2 {
39 *pts.get_mut([j, i]).unwrap() = p[3 * i + j];
40 }
41 }
42 let wts = w.iter().map(|i| *i / 2.0).collect::<Vec<_>>();
43
44 let mut table = DynArray::<f64, 4>::from_shape(element.tabulate_array_shape(0, npts));
45 element.tabulate(&pts, 0, &mut table);
46
47 let gmap = mesh.geometry_map(ReferenceCellType::Triangle, 1, &pts);
48 let mut jacobians = rlst_dynamic_array!(f64, [mesh.geometry_dim(), mesh.topology_dim(), npts]);
49 let mut jinv = rlst_dynamic_array!(f64, [mesh.topology_dim(), mesh.geometry_dim(), npts]);
50 let mut jdets = vec![0.0; npts];
51
52 for cell in mesh.entity_iter(ReferenceCellType::Triangle) {
53 let dofs = space
54 .entity_closure_dofs(ReferenceCellType::Triangle, cell.local_index())
55 .unwrap();
56 gmap.jacobians_inverses_dets(cell.local_index(), &mut jacobians, &mut jinv, &mut jdets);
57 for (test_i, test_dof) in dofs.iter().enumerate() {
58 for (trial_i, trial_dof) in dofs.iter().enumerate() {
59 *mass_matrix.get_mut([*test_dof, *trial_dof]).unwrap() += wts
60 .iter()
61 .enumerate()
62 .map(|(i, w)| {
63 jdets[i]
64 * *w
65 * *table.get([0, i, test_i, 0]).unwrap()
66 * *table.get([0, i, trial_i, 0]).unwrap()
67 })
68 .sum::<f64>();
69 }
70 }
71 }
72
73 for i in 0..6 {
75 assert_relative_eq!(mass_matrix[[i, i]], 0.5773502691896255, epsilon = 1e-10);
76 }
77 for i in 0..6 {
78 for j in 0..6 {
79 if i != j && mass_matrix[[i, j]].abs() > 0.001 {
80 assert_relative_eq!(mass_matrix[[i, j]], 0.1443375672974061, epsilon = 1e-10);
81 }
82 }
83 }
84}
85
86fn test_rt_mass_matrix() {
88 let mesh = regular_sphere(0, ReferenceCellType::Triangle);
89
90 let family = RaviartThomasElementFamily::<f64>::new(1, Continuity::Standard);
91 let space = FunctionSpaceImpl::new(&mesh, &family);
92
93 let mut mass_matrix = rlst_dynamic_array!(f64, [space.local_size(), space.local_size()]);
94
95 let element = &space.elements()[0];
96
97 let (p, w) = single_integral_quadrature(
98 QuadratureRule::XiaoGimbutas,
99 Domain::Triangle,
100 2 * element.lagrange_superdegree(),
101 )
102 .unwrap();
103 let npts = w.len();
104 let mut pts = rlst_dynamic_array!(f64, [2, npts]);
105 for i in 0..w.len() {
106 for j in 0..2 {
107 *pts.get_mut([j, i]).unwrap() = p[3 * i + j];
108 }
109 }
110 let wts = w.iter().map(|i| *i / 2.0).collect::<Vec<_>>();
111
112 let mut table = DynArray::<f64, 4>::from_shape(element.tabulate_array_shape(0, npts));
113 element.tabulate(&pts, 0, &mut table);
114 let mut pushed_table = rlst_dynamic_array!(
115 f64,
116 [table.shape()[0], table.shape()[1], table.shape()[2], 3]
117 );
118
119 let gmap = mesh.geometry_map(ReferenceCellType::Triangle, 1, &pts);
120 let mut jacobians = rlst_dynamic_array!(f64, [mesh.geometry_dim(), mesh.topology_dim(), npts]);
121 let mut jinv = rlst_dynamic_array!(f64, [mesh.topology_dim(), mesh.geometry_dim(), npts]);
122 let mut jdets = vec![0.0; npts];
123
124 for cell in mesh.entity_iter(ReferenceCellType::Triangle) {
125 let dofs = space
126 .entity_closure_dofs(ReferenceCellType::Triangle, cell.local_index())
127 .unwrap();
128 gmap.jacobians_inverses_dets(cell.local_index(), &mut jacobians, &mut jinv, &mut jdets);
129 element.push_forward(&table, 0, &jacobians, &jdets, &jinv, &mut pushed_table);
130
131 for (test_i, test_dof) in dofs.iter().enumerate() {
132 for (trial_i, trial_dof) in dofs.iter().enumerate() {
133 *mass_matrix.get_mut([*test_dof, *trial_dof]).unwrap() += wts
134 .iter()
135 .enumerate()
136 .map(|(i, w)| {
137 jdets[i]
138 * *w
139 * (0..3)
140 .map(|j| {
141 *pushed_table.get([0, i, test_i, j]).unwrap()
142 * *pushed_table.get([0, i, trial_i, j]).unwrap()
143 })
144 .sum::<f64>()
145 })
146 .sum::<f64>();
147 }
148 }
149 }
150
151 for i in 0..12 {
153 assert_relative_eq!(mass_matrix[[i, i]], 0.4811252243246884, epsilon = 1e-10);
154 }
155 for i in 0..12 {
156 for j in 0..12 {
157 if i != j && mass_matrix[[i, j]].abs() > 0.001 {
158 assert_relative_eq!(
159 mass_matrix[[i, j]].abs(),
160 0.0481125224324689,
161 epsilon = 1e-10
162 );
163 }
164 }
165 }
166}
167
168fn main() {
170 println!("Testing Lagrange mass matrix");
171 test_lagrange_mass_matrix();
172
173 println!("Testing Raviart-Thomas mass matrix");
174 test_rt_mass_matrix();
175}