1extern crate blas_src;
30extern crate lapack_src;
31
32use crate::math;
33use crate::polynomials::{legendre_shape, polynomial_count, tabulate_legendre_polynomials};
34use crate::reference_cell;
35use crate::traits::{FiniteElement, Map, MappedFiniteElement};
36use crate::types::{Continuity, DofTransformation, ReferenceCellType, Transformation};
37use itertools::izip;
38use rlst::RlstScalar;
39use rlst::{Array, DynArray, Inverse, MutableArrayImpl, ValueArrayImpl, rlst_dynamic_array};
40use std::collections::HashMap;
41use std::fmt::{Debug, Formatter};
42
43pub mod lagrange;
44pub mod nedelec;
45pub mod raviart_thomas;
46pub use lagrange::{LagrangeElementFamily, Variant as LagrangeVariant};
47pub use nedelec::NedelecFirstKindElementFamily;
48pub use raviart_thomas::RaviartThomasElementFamily;
49
50type EntityPoints<T> = [Vec<DynArray<T, 2>>; 4];
51type EntityWeights<T> = [Vec<DynArray<T, 3>>; 4];
52
53fn compute_derivative_count(nderivs: usize, cell_type: ReferenceCellType) -> usize {
55 match cell_type {
56 ReferenceCellType::Point => 0,
57 ReferenceCellType::Interval => nderivs + 1,
58 ReferenceCellType::Triangle => (nderivs + 1) * (nderivs + 2) / 2,
59 ReferenceCellType::Quadrilateral => (nderivs + 1) * (nderivs + 2) / 2,
60 ReferenceCellType::Tetrahedron => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
61 ReferenceCellType::Hexahedron => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
62 ReferenceCellType::Prism => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
63 ReferenceCellType::Pyramid => (nderivs + 1) * (nderivs + 2) * (nderivs + 3) / 6,
64 }
65}
66
67pub struct CiarletElement<T: RlstScalar, M: Map, TGeo: RlstScalar> {
69 family_name: String,
70 cell_type: ReferenceCellType,
71 degree: usize,
72 embedded_superdegree: usize,
73 value_shape: Vec<usize>,
74 value_size: usize,
75 continuity: Continuity,
76 dim: usize,
77 coefficients: DynArray<T, 3>,
78 entity_dofs: [Vec<Vec<usize>>; 4],
79 entity_closure_dofs: [Vec<Vec<usize>>; 4],
80 interpolation_points: EntityPoints<TGeo>,
81 interpolation_weights: EntityWeights<T>,
82 map: M,
83 dof_transformations: HashMap<(ReferenceCellType, Transformation), DofTransformation<T>>,
84}
85
86impl<T: RlstScalar, M: Map, TGeo: RlstScalar> Debug for CiarletElement<T, M, TGeo> {
87 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
88 f.debug_tuple("CiarletElement")
89 .field(&self.family_name)
90 .field(&self.cell_type)
91 .field(&self.degree)
92 .finish()
93 }
94}
95
96impl<T: RlstScalar, M: Map, TGeo: RlstScalar> CiarletElement<T, M, TGeo>
97where
98 DynArray<T, 2>: Inverse<Output = DynArray<T, 2>>,
99{
100 #[allow(clippy::too_many_arguments)]
108 pub fn create(
109 family_name: String,
110 cell_type: ReferenceCellType,
111 degree: usize,
112 value_shape: Vec<usize>,
113 polynomial_coeffs: DynArray<T, 3>,
114 interpolation_points: EntityPoints<TGeo>,
115 interpolation_weights: EntityWeights<T>,
116 continuity: Continuity,
117 embedded_superdegree: usize,
118 map: M,
119 ) -> Self {
120 let mut dim = 0;
121 let mut npts = 0;
122
123 for emats in &interpolation_weights {
124 for mat in emats {
125 dim += mat.shape()[0];
126 npts += mat.shape()[2];
127 }
128 }
129 let tdim = reference_cell::dim(cell_type);
130
131 let mut value_size = 1;
132 for i in &value_shape {
133 value_size *= *i;
134 }
135
136 for matrices in &interpolation_weights {
137 for mat in matrices {
138 if mat.shape()[1] != value_size {
139 panic!("Incompatible value size");
140 }
141 }
142 }
143
144 let new_pts = if continuity == Continuity::Discontinuous {
146 let mut new_pts: EntityPoints<TGeo> = [vec![], vec![], vec![], vec![]];
147 let mut all_pts = rlst_dynamic_array!(TGeo, [tdim, npts]);
148 for (i, pts_i) in interpolation_points.iter().take(tdim).enumerate() {
149 for _pts in pts_i {
150 new_pts[i].push(rlst_dynamic_array!(TGeo, [tdim, 0]));
151 }
152 }
153 let mut col = 0;
154 for pts_i in interpolation_points.iter() {
155 for pts in pts_i {
156 let ncols = pts.shape()[1];
157 all_pts
158 .r_mut()
159 .into_subview([0, col], [tdim, ncols])
160 .fill_from(pts);
161 col += ncols;
162 }
163 }
164 new_pts[tdim].push(all_pts);
165 new_pts
166 } else {
167 interpolation_points
168 };
169 let new_wts = if continuity == Continuity::Discontinuous {
170 let mut new_wts = [vec![], vec![], vec![], vec![]];
171 let mut pn = 0;
172 let mut dn = 0;
173 let mut all_mat = rlst_dynamic_array!(T, [dim, value_size, npts]);
174 for (i, mi) in interpolation_weights.iter().take(tdim).enumerate() {
175 for _mat in mi {
176 new_wts[i].push(rlst_dynamic_array!(T, [0, value_size, 0]));
177 }
178 }
179 for mi in interpolation_weights.iter() {
180 for mat in mi {
181 let dim0 = mat.shape()[0];
182 let dim2 = mat.shape()[2];
183 all_mat
184 .r_mut()
185 .into_subview([dn, 0, pn], [dim0, value_size, dim2])
186 .fill_from(mat);
187 dn += dim0;
188 pn += dim2;
189 }
190 }
191 new_wts[tdim].push(all_mat);
192 new_wts
193 } else {
194 interpolation_weights
195 };
196
197 let pdim = polynomial_count(cell_type, embedded_superdegree);
199 let mut d_matrix = rlst_dynamic_array!(T, [value_size, pdim, dim]);
200
201 let mut dof = 0;
202 for d in 0..4 {
203 for (e, pts) in new_pts[d].iter().enumerate() {
204 if pts.shape()[1] > 0 {
205 let mut table = rlst_dynamic_array!(T, [1, pdim, pts.shape()[1]]);
206 tabulate_legendre_polynomials(
207 cell_type,
208 pts,
209 embedded_superdegree,
210 0,
211 &mut table,
212 );
213 let mat = &new_wts[d][e];
214 for i in 0..mat.shape()[0] {
215 for l in 0..pdim {
216 for j in 0..value_size {
217 *d_matrix.get_mut([j, l, dof + i]).unwrap() = mat
219 .r()
220 .slice::<2>(0, i)
221 .slice(0, j)
222 .inner(&table.r().slice::<2>(0, 0).slice(0, l))
223 .unwrap();
224 }
225 }
226 }
227 dof += mat.shape()[0];
228 }
229 }
230 }
231
232 let mut dual_mat = rlst::rlst_dynamic_array!(T, [dim, dim]);
234
235 for i in 0..dim {
236 for j in 0..dim {
237 *dual_mat.get_mut([i, j]).unwrap() = (0..value_size)
238 .map(|k| {
239 (0..pdim)
240 .map(|l| {
241 *polynomial_coeffs.get([i, k, l]).unwrap()
242 * *d_matrix.get([k, l, j]).unwrap()
243 })
244 .sum::<T>()
245 })
246 .sum::<T>();
247 }
248 }
249
250 let inverse = dual_mat.inverse().unwrap();
251
252 let mut coefficients = rlst_dynamic_array!(T, [dim, value_size, pdim]);
253 for i in 0..dim {
254 for j in 0..value_size {
255 for k in 0..pdim {
256 *coefficients.get_mut([i, j, k]).unwrap() = inverse
258 .r()
259 .slice::<1>(0, i)
260 .inner(&polynomial_coeffs.r().slice::<2>(1, j).slice(1, k))
261 .unwrap();
262 }
263 }
264 }
265
266 let mut entity_dofs = [vec![], vec![], vec![], vec![]];
268 let mut dof = 0;
269 for i in 0..4 {
270 for wts in &new_wts[i] {
271 let dofs = (dof..dof + wts.shape()[0]).collect::<Vec<_>>();
272 entity_dofs[i].push(dofs);
273 dof += wts.shape()[0];
274 }
275 }
276 let connectivity = reference_cell::connectivity(cell_type);
277 let mut entity_closure_dofs = [vec![], vec![], vec![], vec![]];
278 for (edim, (ecdofs, connectivity_edim)) in
279 izip!(entity_closure_dofs.iter_mut(), &connectivity).enumerate()
280 {
281 for connectivity_edim_eindex in connectivity_edim {
282 let mut cdofs = vec![];
283 for (edim2, connectivity_edim_eindex_edim2) in
284 connectivity_edim_eindex.iter().take(edim + 1).enumerate()
285 {
286 for index in connectivity_edim_eindex_edim2 {
287 for i in &entity_dofs[edim2][*index] {
288 cdofs.push(*i)
289 }
290 }
291 }
292 ecdofs.push(cdofs);
293 }
294 }
295
296 let mut dof_transformations = HashMap::new();
298 for (entity, entity_id, transform, f) in match cell_type {
299 ReferenceCellType::Point => vec![],
300 ReferenceCellType::Interval => vec![],
301 ReferenceCellType::Triangle => vec![(
302 ReferenceCellType::Interval,
303 0,
304 Transformation::Reflection,
305 (|x| vec![x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
306 )],
307 ReferenceCellType::Quadrilateral => vec![(
308 ReferenceCellType::Interval,
309 0,
310 Transformation::Reflection,
311 (|x| vec![TGeo::one() - x[0], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
312 )],
313 ReferenceCellType::Tetrahedron => vec![
314 (
315 ReferenceCellType::Interval,
316 0,
317 Transformation::Reflection,
318 (|x| vec![x[0], x[2], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
319 ),
320 (
321 ReferenceCellType::Triangle,
322 0,
323 Transformation::Rotation,
324 (|x| vec![x[1], x[2], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
325 ),
326 (
327 ReferenceCellType::Triangle,
328 0,
329 Transformation::Reflection,
330 (|x| vec![x[0], x[2], x[1]]) as fn(&[TGeo]) -> Vec<TGeo>,
331 ),
332 ],
333 ReferenceCellType::Hexahedron => vec![
334 (
335 ReferenceCellType::Interval,
336 0,
337 Transformation::Reflection,
338 (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
339 ),
340 (
341 ReferenceCellType::Quadrilateral,
342 0,
343 Transformation::Rotation,
344 (|x| vec![x[1], TGeo::one() - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
345 ),
346 (
347 ReferenceCellType::Quadrilateral,
348 0,
349 Transformation::Reflection,
350 (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
351 ),
352 ],
353 ReferenceCellType::Prism => vec![
354 (
355 ReferenceCellType::Interval,
356 0,
357 Transformation::Reflection,
358 (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
359 ),
360 (
361 ReferenceCellType::Triangle,
362 0,
363 Transformation::Rotation,
364 (|x| vec![x[1], TGeo::one() - x[1] - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
365 ),
366 (
367 ReferenceCellType::Triangle,
368 0,
369 Transformation::Reflection,
370 (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
371 ),
372 (
373 ReferenceCellType::Quadrilateral,
374 1,
375 Transformation::Rotation,
376 (|x| vec![x[2], TGeo::one() - x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
377 ),
378 (
379 ReferenceCellType::Quadrilateral,
380 1,
381 Transformation::Reflection,
382 (|x| vec![x[2], x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
383 ),
384 ],
385 ReferenceCellType::Pyramid => vec![
386 (
387 ReferenceCellType::Interval,
388 0,
389 Transformation::Reflection,
390 (|x| vec![TGeo::one() - x[0], x[1], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
391 ),
392 (
393 ReferenceCellType::Triangle,
394 1,
395 Transformation::Rotation,
396 (|x| vec![x[2], x[1], TGeo::one() - x[2] - x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
397 ),
398 (
399 ReferenceCellType::Triangle,
400 1,
401 Transformation::Reflection,
402 (|x| vec![x[2], x[1], x[0]]) as fn(&[TGeo]) -> Vec<TGeo>,
403 ),
404 (
405 ReferenceCellType::Quadrilateral,
406 0,
407 Transformation::Rotation,
408 (|x| vec![x[1], TGeo::one() - x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
409 ),
410 (
411 ReferenceCellType::Quadrilateral,
412 0,
413 Transformation::Reflection,
414 (|x| vec![x[1], x[0], x[2]]) as fn(&[TGeo]) -> Vec<TGeo>,
415 ),
416 ],
417 } {
418 let edim = reference_cell::dim(entity);
419 let ref_pts = &new_pts[edim][entity_id];
420 let npts = ref_pts.shape()[1];
421
422 let finv = match transform {
423 Transformation::Reflection => {
424 (|x, f| f(x)) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
425 }
426 Transformation::Rotation => match entity {
427 ReferenceCellType::Interval => {
428 (|x, f| f(x)) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
429 }
430 ReferenceCellType::Triangle => {
431 (|x, f| f(&f(x))) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
432 }
433 ReferenceCellType::Quadrilateral => {
434 (|x, f| f(&f(&f(x)))) as fn(&[TGeo], fn(&[TGeo]) -> Vec<TGeo>) -> Vec<TGeo>
435 }
436 _ => panic!("Unsupported entity: {entity:?}"),
437 },
438 };
439
440 let mut pts = DynArray::<TGeo, 2>::from_shape(ref_pts.shape());
441 for p in 0..npts {
442 for (i, c) in finv(
443 &ref_pts.data().unwrap()[ref_pts.shape()[0] * p..ref_pts.shape()[0] * (p + 1)],
444 f,
445 )
446 .iter()
447 .enumerate()
448 {
449 *pts.get_mut([i, p]).unwrap() = *c
450 }
451 }
452
453 let wts = &new_wts[edim][entity_id];
454 let edofs = &entity_dofs[edim][entity_id];
455 let endofs = edofs.len();
456 let mut j = rlst_dynamic_array![TGeo, [tdim, tdim, npts]];
457 for t_in in 0..tdim {
458 for (t_out, (a, b)) in izip!(
459 f(&vec![TGeo::zero(); tdim]),
460 f(&{
461 let mut axis = vec![TGeo::zero(); tdim];
462 axis[t_in] = TGeo::one();
463 axis
464 })
465 )
466 .enumerate()
467 {
468 for p in 0..npts {
469 *j.get_mut([t_out, t_in, p]).unwrap() = b - a;
470 }
471 }
472 }
473 let jdet = vec![
475 match transform {
476 Transformation::Reflection => -TGeo::one(),
477 Transformation::Rotation => TGeo::one(),
478 };
479 npts
480 ];
481 let mut jinv = rlst_dynamic_array![TGeo, [tdim, tdim, npts]];
482 for t_in in 0..tdim {
483 for (t_out, (a, b)) in izip!(
484 finv(&vec![TGeo::zero(); tdim], f),
485 finv(
486 &{
487 let mut axis = vec![TGeo::zero(); tdim];
488 axis[t_in] = TGeo::one();
489 axis
490 },
491 f
492 )
493 )
494 .enumerate()
495 {
496 for p in 0..npts {
497 *jinv.get_mut([t_out, t_in, p]).unwrap() = TGeo::from(b - a).unwrap();
498 }
499 }
500 }
501
502 let mut table = DynArray::<T, 3>::from_shape(legendre_shape(
503 cell_type,
504 &pts,
505 embedded_superdegree,
506 0,
507 ));
508 tabulate_legendre_polynomials(cell_type, &pts, embedded_superdegree, 0, &mut table);
509
510 let mut data = rlst_dynamic_array!(T, [1, npts, endofs, value_size]);
511 for p in 0..npts {
512 for j in 0..value_size {
513 for (b, dof) in edofs.iter().enumerate() {
514 *data.get_mut([0, p, b, j]).unwrap() = coefficients
516 .r()
517 .slice::<2>(0, *dof)
518 .slice(0, j)
519 .inner(&table.r().slice::<2>(0, 0).slice(1, p))
520 .unwrap();
521 }
522 }
523 }
524
525 let mut pushed_data = rlst_dynamic_array!(T, [1, npts, endofs, value_size]);
526 map.push_forward(&data, 0, &j, &jdet, &jinv, &mut pushed_data);
527
528 let mut dof_transform = rlst_dynamic_array!(T, [edofs.len(), edofs.len()]);
529 for i in 0..edofs.len() {
530 for j in 0..edofs.len() {
531 *dof_transform.get_mut([i, j]).unwrap() = (0..value_size)
532 .map(|l| {
533 (0..npts)
534 .map(|m| {
535 *wts.get([j, l, m]).unwrap()
536 * *pushed_data.get([0, m, i, l]).unwrap()
537 })
538 .sum::<T>()
539 })
540 .sum::<T>();
541 }
542 }
543
544 let perm = math::prepare_matrix(&mut dof_transform);
545
546 let mut is_identity = true;
548 'outer: for j in 0..edofs.len() {
549 for i in 0..edofs.len() {
550 if (*dof_transform.get([i, j]).unwrap()
551 - if i == j { T::one() } else { T::zero() })
552 .abs()
553 > T::from(1e-8).unwrap().re()
554 {
555 is_identity = false;
556 break 'outer;
557 }
558 }
559 }
560
561 if is_identity {
562 let mut is_unpermuted = true;
563 for (i, p) in perm.iter().enumerate() {
564 if i != *p {
565 is_unpermuted = false;
566 break;
567 }
568 }
569 if is_unpermuted {
570 dof_transformations.insert((entity, transform), DofTransformation::Identity);
571 } else {
572 dof_transformations
573 .insert((entity, transform), DofTransformation::Permutation(perm));
574 }
575 } else {
576 dof_transformations.insert(
577 (entity, transform),
578 DofTransformation::Transformation(dof_transform, perm),
579 );
580 }
581 }
582 CiarletElement::<T, M, TGeo> {
583 family_name,
584 cell_type,
585 degree,
586 embedded_superdegree,
587 value_shape,
588 value_size,
589 continuity,
590 dim,
591 coefficients,
592 entity_dofs,
593 entity_closure_dofs,
594 interpolation_points: new_pts,
595 interpolation_weights: new_wts,
596 map,
597 dof_transformations,
598 }
599 }
600
601 pub fn degree(&self) -> usize {
603 self.degree
604 }
605 pub fn continuity(&self) -> Continuity {
607 self.continuity
608 }
609 pub fn interpolation_points(&self) -> &EntityPoints<TGeo> {
611 &self.interpolation_points
612 }
613 pub fn interpolation_weights(&self) -> &EntityWeights<T> {
615 &self.interpolation_weights
616 }
617}
618
619impl<T: RlstScalar, M: Map, TGeoInternal: RlstScalar> FiniteElement
620 for CiarletElement<T, M, TGeoInternal>
621{
622 type CellType = ReferenceCellType;
623 type T = T;
624
625 fn value_shape(&self) -> &[usize] {
626 &self.value_shape
627 }
628
629 fn value_size(&self) -> usize {
630 self.value_size
631 }
632
633 fn cell_type(&self) -> ReferenceCellType {
634 self.cell_type
635 }
636
637 fn dim(&self) -> usize {
638 self.dim
639 }
640
641 fn tabulate<
642 TGeo: RlstScalar,
643 Array2Impl: ValueArrayImpl<TGeo, 2>,
644 Array4MutImpl: MutableArrayImpl<T, 4>,
645 >(
646 &self,
647 points: &Array<Array2Impl, 2>,
648 nderivs: usize,
649 data: &mut Array<Array4MutImpl, 4>,
650 ) {
651 let mut table = DynArray::<T, 3>::from_shape(legendre_shape(
652 self.cell_type,
653 points,
654 self.embedded_superdegree,
655 nderivs,
656 ));
657 tabulate_legendre_polynomials(
658 self.cell_type,
659 points,
660 self.embedded_superdegree,
661 nderivs,
662 &mut table,
663 );
664
665 for d in 0..table.shape()[0] {
666 for p in 0..points.shape()[1] {
667 for j in 0..self.value_size {
668 for b in 0..self.dim {
669 *data.get_mut([d, p, b, j]).unwrap() = self
671 .coefficients
672 .r()
673 .slice::<2>(0, b)
674 .slice(0, j)
675 .inner(&table.r().slice::<2>(0, d).slice(1, p))
676 .unwrap();
677 }
678 }
679 }
680 }
681 }
682
683 fn tabulate_array_shape(&self, nderivs: usize, npoints: usize) -> [usize; 4] {
684 let deriv_count = compute_derivative_count(nderivs, self.cell_type());
685 let point_count = npoints;
686 let basis_count = self.dim();
687 let value_size = self.value_size();
688 [deriv_count, point_count, basis_count, value_size]
689 }
690
691 fn entity_dofs(&self, entity_dim: usize, entity_number: usize) -> Option<&[usize]> {
692 if entity_dim < 4 && entity_number < self.entity_dofs[entity_dim].len() {
693 Some(&self.entity_dofs[entity_dim][entity_number])
694 } else {
695 None
696 }
697 }
698
699 fn entity_closure_dofs(&self, entity_dim: usize, entity_number: usize) -> Option<&[usize]> {
700 if entity_dim < 4 && entity_number < self.entity_closure_dofs[entity_dim].len() {
701 Some(&self.entity_closure_dofs[entity_dim][entity_number])
702 } else {
703 None
704 }
705 }
706}
707
708impl<T: RlstScalar, M: Map, TGeoInternal: RlstScalar> MappedFiniteElement
709 for CiarletElement<T, M, TGeoInternal>
710{
711 type TransformationType = Transformation;
712
713 fn lagrange_superdegree(&self) -> usize {
714 self.embedded_superdegree
715 }
716
717 fn push_forward<
718 TGeo: RlstScalar,
719 Array3GeoImpl: ValueArrayImpl<TGeo, 3>,
720 Array4Impl: ValueArrayImpl<T, 4>,
721 Array4MutImpl: MutableArrayImpl<T, 4>,
722 >(
723 &self,
724 reference_values: &Array<Array4Impl, 4>,
725 nderivs: usize,
726 jacobians: &Array<Array3GeoImpl, 3>,
727 jacobian_determinants: &[TGeo],
728 inverse_jacobians: &Array<Array3GeoImpl, 3>,
729 physical_values: &mut Array<Array4MutImpl, 4>,
730 ) {
731 self.map.push_forward(
732 reference_values,
733 nderivs,
734 jacobians,
735 jacobian_determinants,
736 inverse_jacobians,
737 physical_values,
738 )
739 }
740
741 fn pull_back<
742 TGeo: RlstScalar,
743 Array3GeoImpl: ValueArrayImpl<TGeo, 3>,
744 Array4Impl: ValueArrayImpl<T, 4>,
745 Array4MutImpl: MutableArrayImpl<T, 4>,
746 >(
747 &self,
748 physical_values: &Array<Array4Impl, 4>,
749 nderivs: usize,
750 jacobians: &Array<Array3GeoImpl, 3>,
751 jacobian_determinants: &[TGeo],
752 inverse_jacobians: &Array<Array3GeoImpl, 3>,
753 reference_values: &mut Array<Array4MutImpl, 4>,
754 ) {
755 self.map.pull_back(
756 physical_values,
757 nderivs,
758 jacobians,
759 jacobian_determinants,
760 inverse_jacobians,
761 reference_values,
762 )
763 }
764
765 fn physical_value_shape(&self, gdim: usize) -> Vec<usize> {
766 self.map.physical_value_shape(gdim)
767 }
768
769 fn dof_transformation(
770 &self,
771 entity: ReferenceCellType,
772 transformation: Transformation,
773 ) -> Option<&DofTransformation<T>> {
774 self.dof_transformations.get(&(entity, transformation))
775 }
776
777 fn apply_dof_permutations<Type>(&self, data: &mut [Type], cell_orientation: i32) {
778 debug_assert_eq!(data.len() % self.dim, 0);
779 let block_size = data.len() / self.dim;
780 let tdim = reference_cell::dim(self.cell_type);
781 let mut n = 0;
782 if tdim > 1
783 && let Some(perm) = match self
784 .dof_transformations
785 .get(&(ReferenceCellType::Interval, Transformation::Reflection))
786 {
787 Some(DofTransformation::Permutation(p)) => Some(p),
788 Some(DofTransformation::Transformation(_, p)) => Some(p),
789 _ => None,
790 }
791 {
792 for dofs in &self.entity_dofs[1] {
793 #[cfg(debug_assertions)]
794 for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
795 assert_eq!(i + 1, *j);
796 }
797 if (cell_orientation >> n) & 1 == 1 {
798 math::apply_permutation(
799 perm,
800 &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
801 )
802 }
803 n += 1
804 }
805 }
806 if tdim > 2 {
807 for (ftype, dofs) in izip!(
808 &reference_cell::entity_types(self.cell_type)[2],
809 &self.entity_dofs[2]
810 ) {
811 #[cfg(debug_assertions)]
812 for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
813 assert_eq!(i + 1, *j);
814 }
815 if let Some(perm) = match self
816 .dof_transformations
817 .get(&(*ftype, Transformation::Rotation))
818 {
819 Some(DofTransformation::Permutation(p)) => Some(p),
820 Some(DofTransformation::Transformation(_, p)) => Some(p),
821 _ => None,
822 } {
823 for _ in 0..(cell_orientation >> n) & 3 {
824 math::apply_permutation(
825 perm,
826 &mut data
827 [block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
828 )
829 }
830 }
831 n += 2;
832 if let Some(perm) = match self
833 .dof_transformations
834 .get(&(*ftype, Transformation::Reflection))
835 {
836 Some(DofTransformation::Permutation(p)) => Some(p),
837 Some(DofTransformation::Transformation(_, p)) => Some(p),
838 _ => None,
839 } && (cell_orientation >> n) & 1 == 1
840 {
841 math::apply_permutation(
842 perm,
843 &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
844 )
845 }
846 n += 1;
847 }
848 }
849 }
850
851 fn apply_dof_transformations(&self, data: &mut [T], cell_orientation: i32) {
852 debug_assert_eq!(data.len() % self.dim, 0);
853 let block_size = data.len() / self.dim;
854 let tdim = reference_cell::dim(self.cell_type);
855 let mut n = 0;
856 if tdim > 1
857 && let Some(mat) = match self
858 .dof_transformations
859 .get(&(ReferenceCellType::Interval, Transformation::Reflection))
860 {
861 Some(DofTransformation::Transformation(m, _)) => Some(m),
862 _ => None,
863 }
864 {
865 for dofs in &self.entity_dofs[1] {
866 #[cfg(debug_assertions)]
867 for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
868 assert_eq!(i + 1, *j);
869 }
870 if (cell_orientation >> n) & 1 == 1 {
871 math::apply_matrix(
872 mat,
873 &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
874 )
875 }
876 n += 1
877 }
878 }
879 if tdim > 2 {
880 for (ftype, dofs) in izip!(
881 &reference_cell::entity_types(self.cell_type)[2],
882 &self.entity_dofs[2]
883 ) {
884 #[cfg(debug_assertions)]
885 for (i, j) in izip!(&dofs[..dofs.len() - 1], &dofs[1..]) {
886 assert_eq!(i + 1, *j);
887 }
888 if let Some(mat) = match self
889 .dof_transformations
890 .get(&(*ftype, Transformation::Rotation))
891 {
892 Some(DofTransformation::Transformation(m, _)) => Some(m),
893 _ => None,
894 } {
895 for _ in 0..(cell_orientation >> n) & 3 {
896 math::apply_matrix(
897 mat,
898 &mut data
899 [block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
900 )
901 }
902 }
903 n += 2;
904 if let Some(mat) = match self
905 .dof_transformations
906 .get(&(*ftype, Transformation::Reflection))
907 {
908 Some(DofTransformation::Transformation(m, _)) => Some(m),
909 _ => None,
910 } && (cell_orientation >> n) & 1 == 1
911 {
912 math::apply_matrix(
913 mat,
914 &mut data[block_size * dofs[0]..block_size * (dofs[dofs.len() - 1] + 1)],
915 )
916 }
917 n += 1;
918 }
919 }
920 }
921}
922
923#[cfg(test)]
924mod test {
925 use super::*;
926 use approx::*;
927 use paste::paste;
928 use rlst::rlst_dynamic_array;
929
930 pub fn check_dofs(e: impl FiniteElement<CellType = ReferenceCellType>) {
931 let mut ndofs = 0;
932 for (dim, entity_count) in match e.cell_type() {
933 ReferenceCellType::Point => vec![1],
934 ReferenceCellType::Interval => vec![2, 1],
935 ReferenceCellType::Triangle => vec![3, 3, 1],
936 ReferenceCellType::Quadrilateral => vec![4, 4, 1],
937 ReferenceCellType::Tetrahedron => vec![4, 6, 4, 1],
938 ReferenceCellType::Hexahedron => vec![8, 12, 6, 1],
939 ReferenceCellType::Prism => vec![6, 9, 5, 1],
940 ReferenceCellType::Pyramid => vec![5, 8, 5, 1],
941 }
942 .iter()
943 .enumerate()
944 {
945 for entity in 0..*entity_count {
946 ndofs += e.entity_dofs(dim, entity).unwrap().len();
947 }
948 }
949 assert_eq!(ndofs, e.dim());
950 }
951
952 #[test]
953 fn test_raviart_thomas_1_triangle() {
954 let e = raviart_thomas::create::<f64, f64>(
955 ReferenceCellType::Triangle,
956 1,
957 Continuity::Standard,
958 );
959 assert_eq!(e.value_size(), 2);
960 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
961 let mut points = rlst_dynamic_array!(f64, [2, 6]);
962 *points.get_mut([0, 0]).unwrap() = 0.0;
963 *points.get_mut([1, 0]).unwrap() = 0.0;
964 *points.get_mut([0, 1]).unwrap() = 1.0;
965 *points.get_mut([1, 1]).unwrap() = 0.0;
966 *points.get_mut([0, 2]).unwrap() = 0.0;
967 *points.get_mut([1, 2]).unwrap() = 1.0;
968 *points.get_mut([0, 3]).unwrap() = 0.5;
969 *points.get_mut([1, 3]).unwrap() = 0.0;
970 *points.get_mut([0, 4]).unwrap() = 0.0;
971 *points.get_mut([1, 4]).unwrap() = 0.5;
972 *points.get_mut([0, 5]).unwrap() = 0.5;
973 *points.get_mut([1, 5]).unwrap() = 0.5;
974 e.tabulate(&points, 0, &mut data);
975
976 for pt in 0..6 {
977 let x = *points.get([0, pt]).unwrap();
978 let y = *points.get([1, pt]).unwrap();
979 for (i, basis_f) in [[-x, -y], [x - 1.0, y], [-x, 1.0 - y]].iter().enumerate() {
980 for (d, value) in basis_f.iter().enumerate() {
981 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
982 }
983 }
984 }
985 check_dofs(e);
986 }
987
988 #[test]
989 fn test_raviart_thomas_2_triangle() {
990 let e = raviart_thomas::create::<f64, f64>(
991 ReferenceCellType::Triangle,
992 2,
993 Continuity::Standard,
994 );
995 assert_eq!(e.value_size(), 2);
996 check_dofs(e);
997 }
998
999 #[test]
1000 fn test_raviart_thomas_3_triangle() {
1001 let e = raviart_thomas::create::<f64, f64>(
1002 ReferenceCellType::Triangle,
1003 3,
1004 Continuity::Standard,
1005 );
1006 assert_eq!(e.value_size(), 2);
1007 check_dofs(e);
1008 }
1009
1010 #[test]
1011 fn test_raviart_thomas_1_quadrilateral() {
1012 let e = raviart_thomas::create::<f64, f64>(
1013 ReferenceCellType::Quadrilateral,
1014 1,
1015 Continuity::Standard,
1016 );
1017 assert_eq!(e.value_size(), 2);
1018 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1019 let mut points = rlst_dynamic_array!(f64, [2, 6]);
1020 *points.get_mut([0, 0]).unwrap() = 0.0;
1021 *points.get_mut([1, 0]).unwrap() = 0.0;
1022 *points.get_mut([0, 1]).unwrap() = 1.0;
1023 *points.get_mut([1, 1]).unwrap() = 0.0;
1024 *points.get_mut([0, 2]).unwrap() = 0.0;
1025 *points.get_mut([1, 2]).unwrap() = 1.0;
1026 *points.get_mut([0, 3]).unwrap() = 0.5;
1027 *points.get_mut([1, 3]).unwrap() = 0.0;
1028 *points.get_mut([0, 4]).unwrap() = 1.0;
1029 *points.get_mut([1, 4]).unwrap() = 0.5;
1030 *points.get_mut([0, 5]).unwrap() = 0.5;
1031 *points.get_mut([1, 5]).unwrap() = 0.5;
1032 e.tabulate(&points, 0, &mut data);
1033
1034 for pt in 0..6 {
1035 let x = *points.get([0, pt]).unwrap();
1036 let y = *points.get([1, pt]).unwrap();
1037 for (i, basis_f) in [[0.0, 1.0 - y], [x - 1.0, 0.0], [-x, 0.0], [0.0, y]]
1038 .iter()
1039 .enumerate()
1040 {
1041 for (d, value) in basis_f.iter().enumerate() {
1042 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1043 }
1044 }
1045 }
1046 check_dofs(e);
1047 }
1048
1049 #[test]
1050 fn test_raviart_thomas_2_quadrilateral() {
1051 let e = raviart_thomas::create::<f64, f64>(
1052 ReferenceCellType::Quadrilateral,
1053 2,
1054 Continuity::Standard,
1055 );
1056 assert_eq!(e.value_size(), 2);
1057 check_dofs(e);
1058 }
1059
1060 #[test]
1061 fn test_raviart_thomas_3_quadrilateral() {
1062 let e = raviart_thomas::create::<f64, f64>(
1063 ReferenceCellType::Quadrilateral,
1064 3,
1065 Continuity::Standard,
1066 );
1067 assert_eq!(e.value_size(), 2);
1068 check_dofs(e);
1069 }
1070
1071 #[test]
1072 fn test_raviart_thomas_1_tetrahedron() {
1073 let e = raviart_thomas::create::<f64, f64>(
1074 ReferenceCellType::Tetrahedron,
1075 1,
1076 Continuity::Standard,
1077 );
1078 assert_eq!(e.value_size(), 3);
1079 check_dofs(e);
1080 }
1081
1082 #[test]
1083 fn test_raviart_thomas_2_tetrahedron() {
1084 let e = raviart_thomas::create::<f64, f64>(
1085 ReferenceCellType::Tetrahedron,
1086 2,
1087 Continuity::Standard,
1088 );
1089 assert_eq!(e.value_size(), 3);
1090 check_dofs(e);
1091 }
1092
1093 #[test]
1094 fn test_raviart_thomas_3_tetrahedron() {
1095 let e = raviart_thomas::create::<f64, f64>(
1096 ReferenceCellType::Tetrahedron,
1097 3,
1098 Continuity::Standard,
1099 );
1100 assert_eq!(e.value_size(), 3);
1101 check_dofs(e);
1102 }
1103
1104 #[test]
1105 fn test_raviart_thomas_1_hexahedron() {
1106 let e = raviart_thomas::create::<f64, f64>(
1107 ReferenceCellType::Hexahedron,
1108 1,
1109 Continuity::Standard,
1110 );
1111 assert_eq!(e.value_size(), 3);
1112 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1113 let mut points = rlst_dynamic_array!(f64, [3, 6]);
1114 *points.get_mut([0, 0]).unwrap() = 0.0;
1115 *points.get_mut([1, 0]).unwrap() = 0.0;
1116 *points.get_mut([2, 0]).unwrap() = 0.0;
1117 *points.get_mut([0, 1]).unwrap() = 1.0;
1118 *points.get_mut([1, 1]).unwrap() = 0.0;
1119 *points.get_mut([2, 1]).unwrap() = 0.8;
1120 *points.get_mut([0, 2]).unwrap() = 0.0;
1121 *points.get_mut([1, 2]).unwrap() = 1.0;
1122 *points.get_mut([2, 2]).unwrap() = 1.0;
1123 *points.get_mut([0, 3]).unwrap() = 0.5;
1124 *points.get_mut([1, 3]).unwrap() = 0.0;
1125 *points.get_mut([2, 3]).unwrap() = 0.1;
1126 *points.get_mut([0, 4]).unwrap() = 1.0;
1127 *points.get_mut([1, 4]).unwrap() = 0.5;
1128 *points.get_mut([2, 4]).unwrap() = 0.5;
1129 *points.get_mut([0, 5]).unwrap() = 0.5;
1130 *points.get_mut([1, 5]).unwrap() = 0.5;
1131 *points.get_mut([2, 5]).unwrap() = 1.0;
1132 e.tabulate(&points, 0, &mut data);
1133
1134 for pt in 0..6 {
1135 let x = *points.get([0, pt]).unwrap();
1136 let y = *points.get([1, pt]).unwrap();
1137 let z = *points.get([2, pt]).unwrap();
1138 for (i, basis_f) in [
1139 [0.0, 0.0, 1.0 - z],
1140 [0.0, y - 1.0, 0.0],
1141 [1.0 - x, 0.0, 0.0],
1142 [x, 0.0, 0.0],
1143 [0.0, -y, 0.0],
1144 [0.0, 0.0, z],
1145 ]
1146 .iter()
1147 .enumerate()
1148 {
1149 for (d, value) in basis_f.iter().enumerate() {
1150 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1151 }
1152 }
1153 }
1154 check_dofs(e);
1155 }
1156
1157 #[test]
1158 fn test_raviart_thomas_2_hexahedron() {
1159 let e = raviart_thomas::create::<f64, f64>(
1160 ReferenceCellType::Hexahedron,
1161 2,
1162 Continuity::Standard,
1163 );
1164 assert_eq!(e.value_size(), 3);
1165 check_dofs(e);
1166 }
1167
1168 #[test]
1169 fn test_nedelec_1_triangle() {
1170 let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1171 assert_eq!(e.value_size(), 2);
1172 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1173 let mut points = rlst_dynamic_array!(f64, [2, 6]);
1174 *points.get_mut([0, 0]).unwrap() = 0.0;
1175 *points.get_mut([1, 0]).unwrap() = 0.0;
1176 *points.get_mut([0, 1]).unwrap() = 1.0;
1177 *points.get_mut([1, 1]).unwrap() = 0.0;
1178 *points.get_mut([0, 2]).unwrap() = 0.0;
1179 *points.get_mut([1, 2]).unwrap() = 1.0;
1180 *points.get_mut([0, 3]).unwrap() = 0.5;
1181 *points.get_mut([1, 3]).unwrap() = 0.0;
1182 *points.get_mut([0, 4]).unwrap() = 0.0;
1183 *points.get_mut([1, 4]).unwrap() = 0.5;
1184 *points.get_mut([0, 5]).unwrap() = 0.5;
1185 *points.get_mut([1, 5]).unwrap() = 0.5;
1186 e.tabulate(&points, 0, &mut data);
1187
1188 for pt in 0..6 {
1189 let x = *points.get([0, pt]).unwrap();
1190 let y = *points.get([1, pt]).unwrap();
1191 for (i, basis_f) in [[-y, x], [y, 1.0 - x], [1.0 - y, x]].iter().enumerate() {
1192 for (d, value) in basis_f.iter().enumerate() {
1193 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1194 }
1195 }
1196 }
1197 check_dofs(e);
1198 }
1199
1200 #[test]
1201 fn test_nedelec_2_triangle() {
1202 let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1203 assert_eq!(e.value_size(), 2);
1204 check_dofs(e);
1205 }
1206
1207 #[test]
1208 fn test_nedelec_3_triangle() {
1209 let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 3, Continuity::Standard);
1210 assert_eq!(e.value_size(), 2);
1211 check_dofs(e);
1212 }
1213
1214 #[test]
1215 fn test_nedelec_1_quadrilateral() {
1216 let e =
1217 nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 1, Continuity::Standard);
1218 assert_eq!(e.value_size(), 2);
1219 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1220 let mut points = rlst_dynamic_array!(f64, [2, 6]);
1221 *points.get_mut([0, 0]).unwrap() = 0.0;
1222 *points.get_mut([1, 0]).unwrap() = 0.0;
1223 *points.get_mut([0, 1]).unwrap() = 1.0;
1224 *points.get_mut([1, 1]).unwrap() = 0.0;
1225 *points.get_mut([0, 2]).unwrap() = 0.0;
1226 *points.get_mut([1, 2]).unwrap() = 1.0;
1227 *points.get_mut([0, 3]).unwrap() = 0.5;
1228 *points.get_mut([1, 3]).unwrap() = 0.0;
1229 *points.get_mut([0, 4]).unwrap() = 1.0;
1230 *points.get_mut([1, 4]).unwrap() = 0.5;
1231 *points.get_mut([0, 5]).unwrap() = 0.5;
1232 *points.get_mut([1, 5]).unwrap() = 0.5;
1233 e.tabulate(&points, 0, &mut data);
1234
1235 for pt in 0..6 {
1236 let x = *points.get([0, pt]).unwrap();
1237 let y = *points.get([1, pt]).unwrap();
1238 for (i, basis_f) in [[1.0 - y, 0.0], [0.0, 1.0 - x], [0.0, x], [y, 0.0]]
1239 .iter()
1240 .enumerate()
1241 {
1242 for (d, value) in basis_f.iter().enumerate() {
1243 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1244 }
1245 }
1246 }
1247 check_dofs(e);
1248 }
1249
1250 #[test]
1251 fn test_nedelec_2_quadrilateral() {
1252 let e =
1253 nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 2, Continuity::Standard);
1254 assert_eq!(e.value_size(), 2);
1255 check_dofs(e);
1256 }
1257
1258 #[test]
1259 fn test_nedelec_3_quadrilateral() {
1260 let e =
1261 nedelec::create::<f64, f64>(ReferenceCellType::Quadrilateral, 3, Continuity::Standard);
1262 assert_eq!(e.value_size(), 2);
1263 check_dofs(e);
1264 }
1265
1266 #[test]
1267 fn test_nedelec_1_tetrahedron() {
1268 let e =
1269 nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1270 assert_eq!(e.value_size(), 3);
1271 check_dofs(e);
1272 }
1273
1274 #[test]
1275 fn test_nedelec_2_tetrahedron() {
1276 let e =
1277 nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
1278 assert_eq!(e.value_size(), 3);
1279 check_dofs(e);
1280 }
1281
1282 #[test]
1283 fn test_nedelec_3_tetrahedron() {
1284 let e =
1285 nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 3, Continuity::Standard);
1286 assert_eq!(e.value_size(), 3);
1287 check_dofs(e);
1288 }
1289
1290 #[test]
1291 fn test_nedelec_1_hexahedron() {
1292 let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 1, Continuity::Standard);
1293 assert_eq!(e.value_size(), 3);
1294 let mut data = DynArray::<f64, 4>::from_shape(e.tabulate_array_shape(0, 6));
1295 let mut points = rlst_dynamic_array!(f64, [3, 6]);
1296 *points.get_mut([0, 0]).unwrap() = 0.0;
1297 *points.get_mut([1, 0]).unwrap() = 0.0;
1298 *points.get_mut([2, 0]).unwrap() = 0.0;
1299 *points.get_mut([0, 1]).unwrap() = 1.0;
1300 *points.get_mut([1, 1]).unwrap() = 0.0;
1301 *points.get_mut([2, 1]).unwrap() = 0.8;
1302 *points.get_mut([0, 2]).unwrap() = 0.0;
1303 *points.get_mut([1, 2]).unwrap() = 1.0;
1304 *points.get_mut([2, 2]).unwrap() = 1.0;
1305 *points.get_mut([0, 3]).unwrap() = 0.5;
1306 *points.get_mut([1, 3]).unwrap() = 0.0;
1307 *points.get_mut([2, 3]).unwrap() = 0.1;
1308 *points.get_mut([0, 4]).unwrap() = 1.0;
1309 *points.get_mut([1, 4]).unwrap() = 0.5;
1310 *points.get_mut([2, 4]).unwrap() = 0.5;
1311 *points.get_mut([0, 5]).unwrap() = 0.5;
1312 *points.get_mut([1, 5]).unwrap() = 0.5;
1313 *points.get_mut([2, 5]).unwrap() = 1.0;
1314 e.tabulate(&points, 0, &mut data);
1315
1316 for pt in 0..6 {
1317 let x = *points.get([0, pt]).unwrap();
1318 let y = *points.get([1, pt]).unwrap();
1319 let z = *points.get([2, pt]).unwrap();
1320 for (i, basis_f) in [
1321 [(1.0 - y) * (1.0 - z), 0.0, 0.0],
1322 [0.0, (1.0 - x) * (1.0 - z), 0.0],
1323 [0.0, 0.0, (1.0 - x) * (1.0 - y)],
1324 [0.0, x * (1.0 - z), 0.0],
1325 [0.0, 0.0, x * (1.0 - y)],
1326 [y * (1.0 - z), 0.0, 0.0],
1327 [0.0, 0.0, (1.0 - x) * y],
1328 [0.0, 0.0, x * y],
1329 [(1.0 - y) * z, 0.0, 0.0],
1330 [0.0, (1.0 - x) * z, 0.0],
1331 [0.0, x * z, 0.0],
1332 [y * z, 0.0, 0.0],
1333 ]
1334 .iter()
1335 .enumerate()
1336 {
1337 for (d, value) in basis_f.iter().enumerate() {
1338 assert_relative_eq!(*data.get([0, pt, i, d]).unwrap(), value, epsilon = 1e-14);
1339 }
1340 }
1341 }
1342 check_dofs(e);
1343 }
1344
1345 #[test]
1346 fn test_nedelec_2_hexahedron() {
1347 let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1348 assert_eq!(e.value_size(), 3);
1349 check_dofs(e);
1350 }
1351
1352 macro_rules! test_entity_closure_dofs_lagrange {
1353 ($cell:ident, $degree:expr) => {
1354 paste! {
1355 #[test]
1356 fn [<test_entity_closure_dofs_ $cell:lower _ $degree>]() {
1357 let e = lagrange::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard, LagrangeVariant::Equispaced);
1358 let c = reference_cell::connectivity(ReferenceCellType::[<$cell>]);
1359
1360 for (dim, entities) in c.iter().enumerate() {
1361 for (n, entity) in entities.iter().enumerate() {
1362 let ecd = e.entity_closure_dofs(dim, n).unwrap();
1363 let mut len = 0;
1364 for (sub_dim, sub_entities) in entity.iter().take(dim + 1).enumerate() {
1365 for sub_entity in sub_entities {
1366 let dofs = e.entity_dofs(sub_dim, *sub_entity).unwrap();
1367 len += dofs.len();
1368 for dof in dofs {
1369 assert!(ecd.contains(dof));
1370 }
1371 }
1372 }
1373 assert_eq!(ecd.len(), len);
1374 }
1375 }
1376 }
1377 }
1378 };
1379 }
1380
1381 test_entity_closure_dofs_lagrange!(Interval, 2);
1382 test_entity_closure_dofs_lagrange!(Interval, 3);
1383 test_entity_closure_dofs_lagrange!(Interval, 4);
1384 test_entity_closure_dofs_lagrange!(Interval, 5);
1385 test_entity_closure_dofs_lagrange!(Triangle, 2);
1386 test_entity_closure_dofs_lagrange!(Triangle, 3);
1387 test_entity_closure_dofs_lagrange!(Triangle, 4);
1388 test_entity_closure_dofs_lagrange!(Triangle, 5);
1389 test_entity_closure_dofs_lagrange!(Quadrilateral, 2);
1390 test_entity_closure_dofs_lagrange!(Quadrilateral, 3);
1391 test_entity_closure_dofs_lagrange!(Quadrilateral, 4);
1392 test_entity_closure_dofs_lagrange!(Quadrilateral, 5);
1393 test_entity_closure_dofs_lagrange!(Tetrahedron, 2);
1394 test_entity_closure_dofs_lagrange!(Tetrahedron, 3);
1395 test_entity_closure_dofs_lagrange!(Tetrahedron, 4);
1396 test_entity_closure_dofs_lagrange!(Tetrahedron, 5);
1397 test_entity_closure_dofs_lagrange!(Hexahedron, 2);
1398 test_entity_closure_dofs_lagrange!(Hexahedron, 3);
1399
1400 macro_rules! test_dof_transformations {
1401 ($cell:ident, $element:ident, $degree:expr $(, $variant:expr)*) => {
1402 paste! {
1403
1404 #[test]
1405 fn [<test_dof_transformations_ $cell:lower _ $element:lower _ $degree>]() {
1406 let e = [<$element>]::create::<f64, f64>(ReferenceCellType::[<$cell>], [<$degree>], Continuity::Standard, $($variant),*);
1407 let tdim = reference_cell::dim(ReferenceCellType::[<$cell>]);
1408 for edim in 1..tdim {
1409 for entity in &reference_cell::entity_types(ReferenceCellType::[<$cell>])[edim] {
1410 for t in match edim {
1411 1 => vec![Transformation::Reflection],
1412 2 => vec![Transformation::Reflection, Transformation::Rotation],
1413 _ => { panic!("Shouldn't test this dimension"); },
1414 } {
1415 let order = match t {
1416 Transformation::Reflection => 2,
1417 Transformation::Rotation => match entity {
1418 ReferenceCellType::Triangle => 3,
1419 ReferenceCellType::Quadrilateral => 4,
1420 _ => {
1421 panic!("Unsupported entity type: {entity:?}");
1422 }
1423 },
1424 };
1425 match e.dof_transformation(*entity, t).unwrap() {
1426 DofTransformation::Identity => {}
1427 DofTransformation::Permutation(p) => {
1428 let mut data = (0..p.len()).collect::<Vec<_>>();
1429 for _ in 0..order {
1430 math::apply_permutation(p, &mut data);
1431 }
1432 for (i, j) in data.iter().enumerate() {
1433 assert_eq!(i, *j);
1434 }
1435 }
1436 DofTransformation::Transformation(m, p) => {
1437 let mut data = (0..p.len()).map(|i| i as f64).collect::<Vec<_>>();
1438 for _ in 0..order {
1439 math::apply_perm_and_matrix(m, p, &mut data);
1440 }
1441 for (i, j) in data.iter().enumerate() {
1442 assert_relative_eq!(i as f64, j, epsilon=1e-10);
1443 }
1444 }
1445 }
1446 }
1447 }
1448 }
1449 }
1450 }
1451 };
1452 }
1453
1454 test_dof_transformations!(Triangle, lagrange, 1, LagrangeVariant::Equispaced);
1455 test_dof_transformations!(Triangle, lagrange, 2, LagrangeVariant::Equispaced);
1456 test_dof_transformations!(Triangle, lagrange, 3, LagrangeVariant::Equispaced);
1457 test_dof_transformations!(Quadrilateral, lagrange, 1, LagrangeVariant::Equispaced);
1458 test_dof_transformations!(Quadrilateral, lagrange, 2, LagrangeVariant::Equispaced);
1459 test_dof_transformations!(Quadrilateral, lagrange, 3, LagrangeVariant::Equispaced);
1460 test_dof_transformations!(Tetrahedron, lagrange, 1, LagrangeVariant::Equispaced);
1461 test_dof_transformations!(Tetrahedron, lagrange, 2, LagrangeVariant::Equispaced);
1462 test_dof_transformations!(Tetrahedron, lagrange, 3, LagrangeVariant::Equispaced);
1463 test_dof_transformations!(Hexahedron, lagrange, 1, LagrangeVariant::Equispaced);
1464 test_dof_transformations!(Hexahedron, lagrange, 2, LagrangeVariant::Equispaced);
1465 test_dof_transformations!(Hexahedron, lagrange, 3, LagrangeVariant::Equispaced);
1466 test_dof_transformations!(Triangle, nedelec, 1);
1467 test_dof_transformations!(Triangle, nedelec, 2);
1468 test_dof_transformations!(Triangle, nedelec, 3);
1469 test_dof_transformations!(Quadrilateral, nedelec, 1);
1470 test_dof_transformations!(Quadrilateral, nedelec, 2);
1471 test_dof_transformations!(Quadrilateral, nedelec, 3);
1472 test_dof_transformations!(Tetrahedron, nedelec, 1);
1473 test_dof_transformations!(Tetrahedron, nedelec, 2);
1474 test_dof_transformations!(Tetrahedron, nedelec, 3);
1475 test_dof_transformations!(Hexahedron, nedelec, 1);
1476 test_dof_transformations!(Hexahedron, nedelec, 2);
1477 test_dof_transformations!(Triangle, raviart_thomas, 1);
1478 test_dof_transformations!(Triangle, raviart_thomas, 2);
1479 test_dof_transformations!(Triangle, raviart_thomas, 3);
1480 test_dof_transformations!(Quadrilateral, raviart_thomas, 1);
1481 test_dof_transformations!(Quadrilateral, raviart_thomas, 2);
1482 test_dof_transformations!(Quadrilateral, raviart_thomas, 3);
1483 test_dof_transformations!(Tetrahedron, raviart_thomas, 1);
1484 test_dof_transformations!(Tetrahedron, raviart_thomas, 2);
1485 test_dof_transformations!(Tetrahedron, raviart_thomas, 3);
1486 test_dof_transformations!(Hexahedron, raviart_thomas, 1);
1487 test_dof_transformations!(Hexahedron, raviart_thomas, 2);
1488
1489 fn assert_dof_transformation_equal<Array2Impl: ValueArrayImpl<f64, 2>>(
1490 p: &[usize],
1491 m: &Array<Array2Impl, 2>,
1492 expected: Vec<Vec<f64>>,
1493 ) {
1494 let ndofs = p.len();
1495 assert_eq!(m.shape()[0], ndofs);
1496 assert_eq!(m.shape()[1], ndofs);
1497 assert_eq!(expected.len(), ndofs);
1498 for i in 0..ndofs {
1499 let mut col = vec![0.0; ndofs];
1500 col[i] = 1.0;
1501 math::apply_perm_and_matrix(m, p, &mut col);
1502 for (j, c) in col.iter().enumerate() {
1503 assert_relative_eq!(expected[j][i], *c, epsilon = 1e-10);
1504 }
1505 }
1506 }
1507
1508 #[test]
1509 fn test_nedelec1_triangle_dof_transformations() {
1510 let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 1, Continuity::Standard);
1511 if let DofTransformation::Transformation(m, p) = e
1512 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1513 .unwrap()
1514 {
1515 assert_eq!(p.len(), 1);
1516 assert_eq!(m.shape()[0], 1);
1517 assert_eq!(m.shape()[1], 1);
1518 assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
1519 } else {
1520 panic!("Should be a linear transformation");
1521 }
1522 }
1523
1524 #[test]
1525 fn test_nedelec2_triangle_dof_transformations() {
1526 let e = nedelec::create::<f64, f64>(ReferenceCellType::Triangle, 2, Continuity::Standard);
1527 if let DofTransformation::Transformation(m, p) = e
1528 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1529 .unwrap()
1530 {
1531 assert_eq!(p.len(), 2);
1532 assert_eq!(m.shape()[0], 2);
1533 assert_eq!(m.shape()[1], 2);
1534 assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1535 } else {
1536 panic!("Should be a linear transformation");
1537 }
1538 }
1539
1540 #[test]
1541 fn test_nedelec1_tetrahedron_dof_transformations() {
1542 let e =
1543 nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 1, Continuity::Standard);
1544 if let DofTransformation::Transformation(m, p) = e
1545 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1546 .unwrap()
1547 {
1548 assert_eq!(p.len(), 1);
1549 assert_eq!(m.shape()[0], 1);
1550 assert_eq!(m.shape()[1], 1);
1551 assert_dof_transformation_equal(p, m, vec![vec![-1.0]]);
1552 } else {
1553 panic!("Should be a linear transformation");
1554 }
1555 if let DofTransformation::Identity = e
1556 .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1557 .unwrap()
1558 {
1559 } else {
1560 panic!("Should be an identity transformation");
1561 }
1562 if let DofTransformation::Identity = e
1563 .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1564 .unwrap()
1565 {
1566 } else {
1567 panic!("Should be an identity transformation");
1568 }
1569 }
1570
1571 #[test]
1572 fn test_nedelec2_tetrahedron_dof_transformations() {
1573 let e =
1574 nedelec::create::<f64, f64>(ReferenceCellType::Tetrahedron, 2, Continuity::Standard);
1575 if let DofTransformation::Transformation(m, p) = e
1576 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1577 .unwrap()
1578 {
1579 assert_eq!(p.len(), 2);
1580 assert_eq!(m.shape()[0], 2);
1581 assert_eq!(m.shape()[1], 2);
1582 assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1583 } else {
1584 panic!("Should be a linear transformation");
1585 }
1586 if let DofTransformation::Permutation(p) = e
1587 .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1588 .unwrap()
1589 {
1590 assert_eq!(p.len(), 2);
1591 let mut indices = vec![0, 1];
1592 math::apply_permutation(p, &mut indices);
1593 assert_eq!(indices[0], 1);
1594 assert_eq!(indices[1], 0);
1595 } else {
1596 panic!("Should be a permutation");
1597 }
1598 if let DofTransformation::Transformation(m, p) = e
1599 .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1600 .unwrap()
1601 {
1602 assert_eq!(p.len(), 2);
1603 assert_eq!(m.shape()[0], 2);
1604 assert_eq!(m.shape()[1], 2);
1605 assert_dof_transformation_equal(p, m, vec![vec![-1.0, -1.0], vec![1.0, 0.0]]);
1606 } else {
1607 panic!("Should be a linear transformation");
1608 }
1609 }
1610
1611 #[test]
1612 fn test_nedelec2_quadrilateral_dof_transformations() {
1613 let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1614 if let DofTransformation::Transformation(m, p) = e
1615 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1616 .unwrap()
1617 {
1618 assert_eq!(p.len(), 2);
1619 assert_eq!(m.shape()[0], 2);
1620 assert_eq!(m.shape()[1], 2);
1621 assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1622 } else {
1623 panic!("Should be a linear transformation");
1624 }
1625 }
1626
1627 #[test]
1628 fn test_nedelec2_hexahedron_dof_transformations() {
1629 let e = nedelec::create::<f64, f64>(ReferenceCellType::Hexahedron, 2, Continuity::Standard);
1630 if let DofTransformation::Transformation(m, p) = e
1631 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1632 .unwrap()
1633 {
1634 assert_eq!(p.len(), 2);
1635 assert_eq!(m.shape()[0], 2);
1636 assert_eq!(m.shape()[1], 2);
1637 assert_dof_transformation_equal(p, m, vec![vec![-1.0, 0.0], vec![0.0, 1.0]]);
1638 } else {
1639 panic!("Should be a linear transformation");
1640 }
1641 if let DofTransformation::Permutation(p) = e
1642 .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Reflection)
1643 .unwrap()
1644 {
1645 assert_eq!(p.len(), 4);
1646 let mut indices = vec![0, 1, 2, 3];
1647 math::apply_permutation(p, &mut indices);
1648 assert_eq!(indices[0], 1);
1649 assert_eq!(indices[1], 0);
1650 assert_eq!(indices[2], 3);
1651 assert_eq!(indices[3], 2);
1652 } else {
1653 panic!("Should be a permutation");
1654 }
1655 if let DofTransformation::Transformation(m, p) = e
1656 .dof_transformation(ReferenceCellType::Quadrilateral, Transformation::Rotation)
1657 .unwrap()
1658 {
1659 assert_eq!(p.len(), 4);
1660 assert_eq!(m.shape()[0], 4);
1661 assert_eq!(m.shape()[1], 4);
1662 assert_dof_transformation_equal(
1663 p,
1664 m,
1665 vec![
1666 vec![0.0, -1.0, 0.0, 0.0],
1667 vec![1.0, 0.0, 0.0, 0.0],
1668 vec![0.0, 0.0, 0.0, 1.0],
1669 vec![0.0, 0.0, 1.0, 0.0],
1670 ],
1671 );
1672 } else {
1673 panic!("Should be a linear transformation");
1674 }
1675 }
1676
1677 #[test]
1678 fn test_lagrange4_tetrahedron_dof_transformations() {
1679 let e = lagrange::create::<f64, f64>(
1680 ReferenceCellType::Tetrahedron,
1681 4,
1682 Continuity::Standard,
1683 LagrangeVariant::Equispaced,
1684 );
1685 if let DofTransformation::Permutation(p) = e
1686 .dof_transformation(ReferenceCellType::Interval, Transformation::Reflection)
1687 .unwrap()
1688 {
1689 assert_eq!(p.len(), 3);
1690 let mut indices = vec![0, 1, 2];
1691 math::apply_permutation(p, &mut indices);
1692 assert_eq!(indices[0], 2);
1693 assert_eq!(indices[1], 1);
1694 assert_eq!(indices[2], 0);
1695 } else {
1696 panic!("Should be a permutation");
1697 }
1698 if let DofTransformation::Permutation(p) = e
1699 .dof_transformation(ReferenceCellType::Triangle, Transformation::Reflection)
1700 .unwrap()
1701 {
1702 assert_eq!(p.len(), 3);
1703 let mut indices = vec![0, 1, 2];
1704 math::apply_permutation(p, &mut indices);
1705 assert_eq!(indices[0], 0);
1706 assert_eq!(indices[1], 2);
1707 assert_eq!(indices[2], 1);
1708 } else {
1709 panic!("Should be a permutation");
1710 }
1711 if let DofTransformation::Permutation(p) = e
1712 .dof_transformation(ReferenceCellType::Triangle, Transformation::Rotation)
1713 .unwrap()
1714 {
1715 assert_eq!(p.len(), 3);
1716 let mut indices = vec![0, 1, 2];
1717 math::apply_permutation(p, &mut indices);
1718 assert_eq!(indices[0], 1);
1719 assert_eq!(indices[1], 2);
1720 assert_eq!(indices[2], 0);
1721 } else {
1722 panic!("Should be a permutation");
1723 }
1724 }
1725
1726 #[test]
1727 fn test_dof_permuting_triangle() {
1728 let e = lagrange::create::<f64, f64>(
1729 ReferenceCellType::Triangle,
1730 3,
1731 Continuity::Standard,
1732 LagrangeVariant::Equispaced,
1733 );
1734
1735 let mut n = (0..10).collect::<Vec<_>>();
1736 e.apply_dof_permutations(&mut n, 7);
1737 for (i, j) in izip!(&n, [0, 1, 2, 4, 3, 6, 5, 8, 7, 9]) {
1738 assert_eq!(*i, j);
1739 }
1740
1741 let mut n = (0..20).collect::<Vec<_>>();
1742 e.apply_dof_permutations(&mut n, 7);
1743 for (i, j) in izip!(
1744 &n,
1745 [
1746 0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 12, 13, 10, 11, 16, 17, 14, 15, 18, 19
1747 ]
1748 ) {
1749 assert_eq!(*i, j);
1750 }
1751 }
1752
1753 #[test]
1754 fn test_dof_permuting_tetrahedron() {
1755 let e = lagrange::create::<f64, f64>(
1756 ReferenceCellType::Tetrahedron,
1757 3,
1758 Continuity::Standard,
1759 LagrangeVariant::Equispaced,
1760 );
1761
1762 let mut n = (0..20).collect::<Vec<_>>();
1763 e.apply_dof_permutations(&mut n, 63);
1764 for (i, j) in izip!(
1765 &n,
1766 [
1767 0, 1, 2, 3, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 16, 17, 18, 19
1768 ]
1769 ) {
1770 assert_eq!(*i, j);
1771 }
1772 }
1773}