Skip to content

Commit 65eb7b8

Browse files
committed
Implement CompactQuadratureTable
1 parent 132783a commit 65eb7b8

1 file changed

Lines changed: 139 additions & 20 deletions

File tree

src/assembly/local/quadrature_table.rs

Lines changed: 139 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,42 @@ where
8282
}
8383
}
8484

85+
/// Checks that the provided quadrature rules are consistent, in the sense that
86+
/// the number of elements for each table is identical, and that each rule has
87+
/// consistent numbers of points, weights and data entries.
88+
fn check_rules_consistency<T, D, Data>(
89+
points: &NestedVec<OPoint<T, D>>,
90+
weights: &NestedVec<T>,
91+
data: &NestedVec<Data>
92+
)
93+
where
94+
T: Scalar,
95+
D: DimName,
96+
DefaultAllocator: Allocator<T, D>
97+
{
98+
assert_eq!(points.len(), weights.len(),
99+
"Quadrature point and weight tables must have the same number of rules.");
100+
assert_eq!(points.len(), data.len(),
101+
"Quadrature point and data tables must have the same number of rules.");
102+
103+
// Ensure that each element has a consistent quadrature rule
104+
let iter = izip!(points.iter(), weights.iter(), data.iter());
105+
for (element_index, (element_points, element_weights, element_data)) in iter.enumerate() {
106+
assert_eq!(
107+
element_points.len(),
108+
element_weights.len(),
109+
"Element {} has mismatched number of points and weights.",
110+
element_index
111+
);
112+
assert_eq!(
113+
element_points.len(),
114+
element_data.len(),
115+
"Element {} has mismatched number of points and data.",
116+
element_index
117+
);
118+
}
119+
}
120+
85121
impl<T, GeometryDim, Data> GeneralQuadratureTable<T, GeometryDim, Data>
86122
where
87123
T: Scalar,
@@ -93,26 +129,7 @@ where
93129
weights: NestedVec<T>,
94130
data: NestedVec<Data>,
95131
) -> 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-
132+
check_rules_consistency(&points, &weights, &data);
116133
Self { points, weights, data }
117134
}
118135

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

0 commit comments

Comments
 (0)