Skip to content

Commit 4b10e3a

Browse files
authored
Merge pull request #43 from Andlon/compact-quadrature-table
Compact quadrature table
2 parents 132783a + 506ceee commit 4b10e3a

5 files changed

Lines changed: 272 additions & 95 deletions

File tree

examples/poisson2d.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
use eyre::eyre;
2-
use nalgebra::Vector1;
3-
42
use fenris::assembly::global::{
53
apply_homogeneous_dirichlet_bc_csr, apply_homogeneous_dirichlet_bc_rhs, CsrAssembler, VectorAssembler,
64
};
@@ -14,6 +12,7 @@ use fenris::mesh::QuadMesh2d;
1412
use fenris::nalgebra::{DMatrix, DVector, Point2, U1, U2};
1513
use fenris::nalgebra_sparse::CsrMatrix;
1614
use fenris::quadrature;
15+
use nalgebra::Vector1;
1716

1817
fn main() -> eyre::Result<()> {
1918
// TODO: Make it easy to construct triangle meshes as well.

src/assembly/local/quadrature_table.rs

Lines changed: 189 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,64 @@ where
6767
data: NestedVec<Data>,
6868
}
6969

70+
fn unit_data_table_for_weights<T>(points: &NestedVec<T>) -> NestedVec<()> {
71+
let mut data = NestedVec::new();
72+
for i in 0..points.len() {
73+
data.push(&vec![(); points.get(i).unwrap().len()]);
74+
}
75+
data
76+
}
77+
7078
impl<T, GeometryDim> GeneralQuadratureTable<T, GeometryDim>
7179
where
7280
T: Scalar,
7381
GeometryDim: DimName,
7482
DefaultAllocator: Allocator<T, GeometryDim>,
7583
{
7684
pub fn from_points_and_weights(points: NestedVec<OPoint<T, GeometryDim>>, weights: NestedVec<T>) -> Self {
77-
let mut data = NestedVec::new();
78-
for i in 0..points.len() {
79-
data.push(&vec![(); points.get(i).unwrap().len()]);
80-
}
85+
let data = unit_data_table_for_weights(&weights);
8186
Self::from_points_weights_and_data(points, weights, data)
8287
}
8388
}
8489

90+
/// Checks that the provided quadrature rules are consistent, in the sense that
91+
/// the number of elements for each table is identical, and that each rule has
92+
/// consistent numbers of points, weights and data entries.
93+
fn check_rules_consistency<T, D, Data>(points: &NestedVec<OPoint<T, D>>, weights: &NestedVec<T>, data: &NestedVec<Data>)
94+
where
95+
T: Scalar,
96+
D: DimName,
97+
DefaultAllocator: Allocator<T, D>,
98+
{
99+
assert_eq!(
100+
points.len(),
101+
weights.len(),
102+
"Quadrature point and weight tables must have the same number of rules."
103+
);
104+
assert_eq!(
105+
points.len(),
106+
data.len(),
107+
"Quadrature point and data tables must have the same number of rules."
108+
);
109+
110+
// Ensure that each element has a consistent quadrature rule
111+
let iter = izip!(points.iter(), weights.iter(), data.iter());
112+
for (element_index, (element_points, element_weights, element_data)) in iter.enumerate() {
113+
assert_eq!(
114+
element_points.len(),
115+
element_weights.len(),
116+
"Element {} has mismatched number of points and weights.",
117+
element_index
118+
);
119+
assert_eq!(
120+
element_points.len(),
121+
element_data.len(),
122+
"Element {} has mismatched number of points and data.",
123+
element_index
124+
);
125+
}
126+
}
127+
85128
impl<T, GeometryDim, Data> GeneralQuadratureTable<T, GeometryDim, Data>
86129
where
87130
T: Scalar,
@@ -93,26 +136,7 @@ where
93136
weights: NestedVec<T>,
94137
data: NestedVec<Data>,
95138
) -> Self {
96-
assert_eq!(points.len(), weights.len());
97-
assert_eq!(points.len(), data.len());
98-
99-
// Ensure that each element has a consistent quadrature rule
100-
let iter = izip!(points.iter(), weights.iter(), data.iter());
101-
for (element_index, (element_points, element_weights, element_data)) in iter.enumerate() {
102-
assert_eq!(
103-
element_points.len(),
104-
element_weights.len(),
105-
"Element {} has mismatched number of points and weights.",
106-
element_index
107-
);
108-
assert_eq!(
109-
element_points.len(),
110-
element_data.len(),
111-
"Element {} has mismatched number of points and data.",
112-
element_index
113-
);
114-
}
115-
139+
check_rules_consistency(&points, &weights, &data);
116140
Self { points, weights, data }
117141
}
118142

@@ -272,3 +296,144 @@ where
272296
weights.clone_from_slice(&self.weights);
273297
}
274298
}
299+
300+
/// A general quadrature table that avoids duplication of identical rules.
301+
///
302+
/// In a nutshell, [`CompactQuadratureTable`] sits in between [`UniformQuadratureTable`]
303+
/// and [`GeneralQuadratureTable`]. Like [`GeneralQuadratureTable`], it can store an arbitrary
304+
/// rule per element, but it uses a layer of indirection so that `M` quadrature rules
305+
/// are associated with `N` elements.
306+
///
307+
/// This can be useful in settings where many elements share the same quadrature data, such
308+
/// as a finite element space with elements of different degrees, or the common case
309+
/// where the elements are the same but different quadrature data is needed in different
310+
/// regions of the mesh.
311+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
312+
pub struct CompactQuadratureTable<T, D, Data = ()>
313+
where
314+
T: Scalar,
315+
D: DimName,
316+
DefaultAllocator: Allocator<T, D>,
317+
{
318+
#[serde(bound(serialize = "OPoint<T, D>: Serialize"))]
319+
#[serde(bound(deserialize = "OPoint<T, D>: Deserialize<'de>"))]
320+
points: NestedVec<OPoint<T, D>>,
321+
weights: NestedVec<T>,
322+
data: NestedVec<Data>,
323+
element_to_rule_map: Vec<usize>,
324+
}
325+
326+
impl<T, D> CompactQuadratureTable<T, D>
327+
where
328+
T: Scalar,
329+
D: DimName,
330+
DefaultAllocator: Allocator<T, D>,
331+
{
332+
pub fn from_points_weights_and_map(
333+
points: NestedVec<OPoint<T, D>>,
334+
weights: NestedVec<T>,
335+
element_to_rule_map: Vec<usize>,
336+
) -> Self {
337+
let data = unit_data_table_for_weights(&weights);
338+
Self::from_quadrature_rules_and_map(points, weights, data, element_to_rule_map)
339+
}
340+
}
341+
342+
impl<T, D, Data> CompactQuadratureTable<T, D, Data>
343+
where
344+
T: Scalar,
345+
D: DimName,
346+
DefaultAllocator: Allocator<T, D>,
347+
{
348+
/// Construct a new table from the given quadrature rules and a map from elements
349+
/// to quadrature rules.
350+
///
351+
/// # Panics
352+
///
353+
/// Panics if `points`, `weights` and `data` are not consistent with each other.
354+
///
355+
/// Panics if the mapping from elements to quadrature rules contains indices that are
356+
/// out of bounds with respect to the number of quadrature rules.
357+
pub fn from_quadrature_rules_and_map(
358+
points: NestedVec<OPoint<T, D>>,
359+
weights: NestedVec<T>,
360+
data: NestedVec<Data>,
361+
element_to_rule_map: Vec<usize>,
362+
) -> Self {
363+
check_rules_consistency(&points, &weights, &data);
364+
let num_rules = points.len();
365+
let rule_indices_in_bounds = element_to_rule_map
366+
.iter()
367+
.all(|rule_index| rule_index < &num_rules);
368+
assert!(
369+
rule_indices_in_bounds,
370+
"Each rule index must correspond to a provided quadrature rule."
371+
);
372+
Self {
373+
element_to_rule_map,
374+
points,
375+
weights,
376+
data,
377+
}
378+
}
379+
380+
fn rule_index_for_element(&self, element_index: usize) -> usize {
381+
self.element_to_rule_map[element_index]
382+
}
383+
}
384+
385+
impl<T, D, Data> QuadratureTable<T, D> for CompactQuadratureTable<T, D, Data>
386+
where
387+
T: Scalar,
388+
D: SmallDim,
389+
Data: Default + Clone,
390+
DefaultAllocator: Allocator<T, D>,
391+
{
392+
type Data = Data;
393+
394+
fn element_quadrature_size(&self, element_index: usize) -> usize {
395+
let rule_index = self.rule_index_for_element(element_index);
396+
self.points
397+
.get(rule_index)
398+
.expect("Internal error: Rule index out of bounds")
399+
.len()
400+
}
401+
402+
fn populate_element_data(&self, element_index: usize, data: &mut [Self::Data]) {
403+
let rule_index = self.rule_index_for_element(element_index);
404+
let data_array = self
405+
.data
406+
.get(rule_index)
407+
.expect("Internal error: Rule index out of bounds");
408+
assert_eq!(
409+
data.len(),
410+
data_array.len(),
411+
"Length mismatch in data array: Stored quadrature data array has different length than output array."
412+
);
413+
data.clone_from_slice(data_array);
414+
}
415+
416+
fn populate_element_quadrature(&self, element_index: usize, points: &mut [OPoint<T, D>], weights: &mut [T]) {
417+
let rule_index = self.rule_index_for_element(element_index);
418+
let points_array = self
419+
.points
420+
.get(rule_index)
421+
.expect("Internal error: Rule index out of bounds");
422+
let weights_array = self
423+
.weights
424+
.get(rule_index)
425+
.expect("Internal error: Rule index out of bounds");
426+
assert_eq!(
427+
points.len(),
428+
points_array.len(),
429+
"Length mismatch in points array: Stored points array has different length than output array."
430+
);
431+
assert_eq!(
432+
weights.len(),
433+
weights_array.len(),
434+
"Length mismatch in points array: Stored points array has different length than output array."
435+
);
436+
points.clone_from_slice(points_array);
437+
weights.clone_from_slice(weights_array);
438+
}
439+
}

src/element/tetrahedron.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use numeric_literals::replace_float_literals;
22

3-
use crate::connectivity::{Tet10Connectivity, Tet4Connectivity};
3+
use crate::connectivity::{Tet10Connectivity, Tet20Connectivity, Tet4Connectivity};
44
use crate::element::{ElementConnectivity, FiniteElement, FixedNodesReferenceFiniteElement};
55
use crate::nalgebra::{
66
distance, Matrix1x4, Matrix3, Matrix3x4, OMatrix, OPoint, Point3, Scalar, Vector3, U1, U10, U20, U3, U4,
@@ -52,6 +52,30 @@ where
5252
}
5353
}
5454

55+
impl<T> ElementConnectivity<T> for Tet20Connectivity
56+
where
57+
T: Real,
58+
{
59+
type Element = Tet20Element<T>;
60+
type GeometryDim = U3;
61+
type ReferenceDim = U3;
62+
63+
fn element(&self, vertices: &[OPoint<T, Self::GeometryDim>]) -> Option<Self::Element> {
64+
let mut tet20_vertices = [Point3::origin(); 20];
65+
for (i, v) in tet20_vertices.iter_mut().enumerate() {
66+
*v = vertices.get(self.0[i])?.clone();
67+
}
68+
69+
let mut tet4_vertices = [Point3::origin(); 4];
70+
tet4_vertices.copy_from_slice(&tet20_vertices[0..4]);
71+
72+
Some(Tet20Element {
73+
tet4: Tet4Element::from_vertices(tet4_vertices),
74+
vertices: tet20_vertices,
75+
})
76+
}
77+
}
78+
5579
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5680
pub struct Tet10Element<T>
5781
where

0 commit comments

Comments
 (0)