ndgrid/shapes/
cube.rs

1//! Cube grids
2
3#[cfg(feature = "mpi")]
4use crate::{ParallelGridImpl, traits::ParallelBuilder, types::GraphPartitioner};
5use crate::{
6    grid::local_grid::{SingleElementGrid, SingleElementGridBuilder},
7    traits::Builder,
8    types::Scalar,
9};
10#[cfg(feature = "mpi")]
11use mpi::traits::{Communicator, Equivalence};
12use ndelement::{ciarlet::CiarletElement, map::IdentityMap, types::ReferenceCellType};
13
14/// Add points and cells for unit interval to builder
15fn unit_interval_add_points_and_cells<T: Scalar>(b: &mut SingleElementGridBuilder<T>, nx: usize) {
16    for i in 0..nx + 1 {
17        b.add_point(i, &[T::from(i).unwrap() / T::from(nx).unwrap()]);
18    }
19
20    for i in 0..nx {
21        b.add_cell(i, &[i, i + 1]);
22    }
23}
24
25/// Add points and cells for unit square to builder
26fn unit_square_add_points_and_cells<T: Scalar>(
27    b: &mut SingleElementGridBuilder<T>,
28    nx: usize,
29    ny: usize,
30    cell_type: ReferenceCellType,
31) {
32    for i in 0..nx + 1 {
33        for j in 0..ny + 1 {
34            b.add_point(
35                i * (ny + 1) + j,
36                &[
37                    T::from(i).unwrap() / T::from(nx).unwrap(),
38                    T::from(j).unwrap() / T::from(ny).unwrap(),
39                ],
40            );
41        }
42    }
43
44    let dx = ny + 1;
45    let dy = 1;
46    match cell_type {
47        ReferenceCellType::Triangle => {
48            for i in 0..nx {
49                for j in 0..ny {
50                    let origin = i * dx + j * dy;
51                    b.add_cell(2 * (j * nx + i), &[origin, origin + dx, origin + dx + dy]);
52                    b.add_cell(
53                        2 * (j * nx + i) + 1,
54                        &[origin, origin + dx + dy, origin + dy],
55                    );
56                }
57            }
58        }
59        ReferenceCellType::Quadrilateral => {
60            for i in 0..nx {
61                for j in 0..ny {
62                    let origin = i * dx + j * dy;
63                    b.add_cell(
64                        j * nx + i,
65                        &[origin, origin + dx, origin + dy, origin + dx + dy],
66                    );
67                }
68            }
69        }
70        _ => {
71            panic!("Unsupported cell type: {cell_type:?}")
72        }
73    }
74}
75
76/// Add points and cells for unit square boundary to builder
77fn unit_square_boundary_add_points_and_cells<T: Scalar>(
78    b: &mut SingleElementGridBuilder<T>,
79    nx: usize,
80    ny: usize,
81) {
82    let dx = ny + 1;
83    let dy = 1;
84
85    for i in 0..nx + 1 {
86        b.add_point(
87            i * dx,
88            &[T::from(i).unwrap() / T::from(nx).unwrap(), T::zero()],
89        );
90        b.add_point(
91            i * dx + ny * dy,
92            &[T::from(i).unwrap() / T::from(nx).unwrap(), T::one()],
93        );
94    }
95    for j in 1..ny {
96        b.add_point(
97            j * dy,
98            &[T::zero(), T::from(j).unwrap() / T::from(ny).unwrap()],
99        );
100        b.add_point(
101            nx * dx + j * dy,
102            &[T::one(), T::from(j).unwrap() / T::from(ny).unwrap()],
103        );
104    }
105
106    for i in 0..nx {
107        let origin = i * dx;
108        b.add_cell(2 * i, &[origin, origin + dx]);
109        let origin = i * dx + ny * dy;
110        b.add_cell(2 * i + 1, &[origin, origin + dx]);
111    }
112    for j in 0..ny {
113        let origin = j * dy;
114        b.add_cell(2 * nx + 2 * j, &[origin, origin + dy]);
115        let origin = nx * dx + j * dy;
116        b.add_cell(2 * nx + 2 * j + 1, &[origin, origin + dy]);
117    }
118}
119
120/// Add points and cells for unit cube to builder
121fn unit_cube_add_points_and_cells<T: Scalar>(
122    b: &mut SingleElementGridBuilder<T>,
123    nx: usize,
124    ny: usize,
125    nz: usize,
126    cell_type: ReferenceCellType,
127) {
128    for i in 0..nx + 1 {
129        for j in 0..ny + 1 {
130            for k in 0..nz + 1 {
131                b.add_point(
132                    (i * (ny + 1) + j) * (nz + 1) + k,
133                    &[
134                        T::from(i).unwrap() / T::from(nx).unwrap(),
135                        T::from(j).unwrap() / T::from(ny).unwrap(),
136                        T::from(k).unwrap() / T::from(nz).unwrap(),
137                    ],
138                );
139            }
140        }
141    }
142
143    let dx = (ny + 1) * (nz + 1);
144    let dy = nz + 1;
145    let dz = 1;
146    match cell_type {
147        ReferenceCellType::Tetrahedron => {
148            for i in 0..nx {
149                for j in 0..ny {
150                    for k in 0..nz {
151                        let origin = i * dx + j * dy + k * dz;
152                        b.add_cell(
153                            6 * ((j * nx + i) * ny + k),
154                            &[origin, origin + dx, origin + dx + dy, origin + dx + dy + dz],
155                        );
156                        b.add_cell(
157                            6 * ((j * nx + i) * ny + k) + 1,
158                            &[origin, origin + dy, origin + dx + dy, origin + dx + dy + dz],
159                        );
160                        b.add_cell(
161                            6 * ((j * nx + i) * ny + k) + 2,
162                            &[origin, origin + dx, origin + dx + dz, origin + dx + dy + dz],
163                        );
164                        b.add_cell(
165                            6 * ((j * nx + i) * ny + k) + 3,
166                            &[origin, origin + dz, origin + dx + dz, origin + dx + dy + dz],
167                        );
168                        b.add_cell(
169                            6 * ((j * nx + i) * ny + k) + 4,
170                            &[origin, origin + dy, origin + dy + dz, origin + dx + dy + dz],
171                        );
172                        b.add_cell(
173                            6 * ((j * nx + i) * ny + k) + 5,
174                            &[origin, origin + dz, origin + dy + dz, origin + dx + dy + dz],
175                        );
176                    }
177                }
178            }
179        }
180        ReferenceCellType::Hexahedron => {
181            for i in 0..nx {
182                for j in 0..ny {
183                    for k in 0..nz {
184                        let origin = i * dx + j * dy + k * dz;
185                        b.add_cell(
186                            (j * nx + i) * ny + k,
187                            &[
188                                origin,
189                                origin + dx,
190                                origin + dy,
191                                origin + dy,
192                                origin + dz,
193                                origin + dx + dz,
194                                origin + dy + dz,
195                                origin + dy + dz,
196                            ],
197                        );
198                    }
199                }
200            }
201        }
202        _ => {
203            panic!("Unsupported cell type: {cell_type:?}")
204        }
205    }
206}
207
208/// Add points and cells for unit cube boundary to builder
209fn unit_cube_boundary_add_points_and_cells<T: Scalar>(
210    b: &mut SingleElementGridBuilder<T>,
211    nx: usize,
212    ny: usize,
213    nz: usize,
214    cell_type: ReferenceCellType,
215) {
216    for i in 0..nx + 1 {
217        for j in 0..ny + 1 {
218            for k in if i == 0 || i == nx || j == 0 || j == ny {
219                (0..nz + 1).collect::<Vec<_>>()
220            } else {
221                vec![0, nz]
222            } {
223                b.add_point(
224                    (i * (ny + 1) + j) * (nz + 1) + k,
225                    &[
226                        T::from(i).unwrap() / T::from(nx).unwrap(),
227                        T::from(j).unwrap() / T::from(ny).unwrap(),
228                        T::from(k).unwrap() / T::from(nz).unwrap(),
229                    ],
230                );
231            }
232        }
233    }
234
235    let dx = (ny + 1) * (nz + 1);
236    let dy = nz + 1;
237    let dz = 1;
238    match cell_type {
239        ReferenceCellType::Triangle => {
240            let mut cell_n = 0;
241            for i in 0..nx {
242                for j in 0..ny {
243                    let origin = i * dx + j * dy;
244                    b.add_cell(cell_n, &[origin, origin + dx, origin + dx + dy]);
245                    b.add_cell(cell_n + 1, &[origin, origin + dx + dy, origin + dy]);
246                    let origin = i * dx + j * dy + nz * dz;
247                    b.add_cell(cell_n + 2, &[origin, origin + dx, origin + dx + dy]);
248                    b.add_cell(cell_n + 3, &[origin, origin + dx + dy, origin + dy]);
249                    cell_n += 4;
250                }
251            }
252            for i in 0..nx {
253                for k in 0..nz {
254                    let origin = i * dx + k * dz;
255                    b.add_cell(cell_n, &[origin, origin + dx, origin + dx + dz]);
256                    b.add_cell(cell_n + 1, &[origin, origin + dx + dz, origin + dz]);
257                    let origin = i * dx + ny * dy + k * dz;
258                    b.add_cell(cell_n + 2, &[origin, origin + dx, origin + dx + dz]);
259                    b.add_cell(cell_n + 3, &[origin, origin + dx + dz, origin + dz]);
260                    cell_n += 4;
261                }
262            }
263            for j in 0..ny {
264                for k in 0..nz {
265                    let origin = j * dy + k * dz;
266                    b.add_cell(cell_n, &[origin, origin + dy, origin + dy + dz]);
267                    b.add_cell(cell_n + 1, &[origin, origin + dy + dz, origin + dz]);
268                    let origin = nx * dx + j * dy + k * dz;
269                    b.add_cell(cell_n + 2, &[origin, origin + dy, origin + dy + dz]);
270                    b.add_cell(cell_n + 3, &[origin, origin + dy + dz, origin + dz]);
271                    cell_n += 4;
272                }
273            }
274        }
275        ReferenceCellType::Quadrilateral => {
276            let mut cell_n = 0;
277            for i in 0..nx {
278                for j in 0..ny {
279                    let origin = i * dx + j * dy;
280                    b.add_cell(
281                        cell_n,
282                        &[origin, origin + dx, origin + dy, origin + dx + dy],
283                    );
284                    let origin = i * dx + j * dy + nz * dz;
285                    b.add_cell(
286                        cell_n + 1,
287                        &[origin, origin + dx, origin + dy, origin + dx + dy],
288                    );
289                    cell_n += 2;
290                }
291            }
292            for i in 0..nx {
293                for k in 0..nz {
294                    let origin = i * dx + k * dz;
295                    b.add_cell(
296                        cell_n,
297                        &[origin, origin + dx, origin + dz, origin + dx + dz],
298                    );
299                    let origin = i * dx + ny * dy + k * dz;
300                    b.add_cell(
301                        cell_n + 1,
302                        &[origin, origin + dx, origin + dz, origin + dx + dz],
303                    );
304                    cell_n += 2;
305                }
306            }
307            for j in 0..ny {
308                for k in 0..nz {
309                    let origin = j * dy + k * dz;
310                    b.add_cell(
311                        cell_n,
312                        &[origin, origin + dy, origin + dz, origin + dy + dz],
313                    );
314                    let origin = nx * dx + j * dy + k * dz;
315                    b.add_cell(
316                        cell_n + 1,
317                        &[origin, origin + dy, origin + dz, origin + dy + dz],
318                    );
319                    cell_n += 2;
320                }
321            }
322        }
323        _ => {
324            panic!("Unsupported cell type: {cell_type:?}")
325        }
326    }
327}
328
329/// Add points and cells for unit cube edges to builder
330fn unit_cube_edges_add_points_and_cells<T: Scalar>(
331    b: &mut SingleElementGridBuilder<T>,
332    nx: usize,
333    ny: usize,
334    nz: usize,
335) {
336    for i in 0..nx + 1 {
337        for j in if i == 0 || i == nx {
338            (0..ny + 1).collect::<Vec<_>>()
339        } else {
340            vec![0, ny]
341        } {
342            for k in if (i == 0 || i == nx) && (j == 0 || j == ny) {
343                (0..nz + 1).collect::<Vec<_>>()
344            } else {
345                vec![0, nz]
346            } {
347                b.add_point(
348                    (i * (ny + 1) + j) * (nz + 1) + k,
349                    &[
350                        T::from(i).unwrap() / T::from(nx).unwrap(),
351                        T::from(j).unwrap() / T::from(ny).unwrap(),
352                        T::from(k).unwrap() / T::from(nz).unwrap(),
353                    ],
354                );
355            }
356        }
357    }
358
359    let dx = (ny + 1) * (nz + 1);
360    let dy = nz + 1;
361    let dz = 1;
362    let mut cell_n = 0;
363    for i in 0..nx {
364        for origin in [
365            i * dx,
366            i * dx + ny * dy,
367            i * dx + nz * dz,
368            i * dx + ny * dy + nz * dz,
369        ] {
370            b.add_cell(cell_n, &[origin, origin + dx]);
371            cell_n += 1;
372        }
373    }
374    for j in 0..ny {
375        for origin in [
376            j * dy,
377            j * dy + nx * dx,
378            j * dy + nz * dz,
379            j * dy + nx * dx + nz * dz,
380        ] {
381            b.add_cell(cell_n, &[origin, origin + dy]);
382            cell_n += 1;
383        }
384    }
385    for k in 0..nz {
386        for origin in [
387            k * dz,
388            k * dz + nx * dx,
389            k * dz + ny * dy,
390            k * dz + nx * dx + ny * dy,
391        ] {
392            b.add_cell(cell_n, &[origin, origin + dz]);
393            cell_n += 1;
394        }
395    }
396}
397
398/// Create a unit interval grid
399///
400/// The unit interval is the interval between (0,) and (1,)
401pub fn unit_interval<T: Scalar>(
402    nx: usize,
403) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
404    let mut b = SingleElementGridBuilder::new_with_capacity(
405        1,
406        nx + 1,
407        nx,
408        (ReferenceCellType::Interval, 1),
409    );
410    unit_interval_add_points_and_cells(&mut b, nx);
411    b.create_grid()
412}
413
414/// Create a unit interval grid distributed in parallel
415#[cfg(feature = "mpi")]
416pub fn unit_interval_distributed<T: Scalar + Equivalence, C: Communicator>(
417    comm: &C,
418    partitioner: GraphPartitioner,
419    nx: usize,
420) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
421    let mut b = SingleElementGridBuilder::new(1, (ReferenceCellType::Interval, 1));
422    if comm.rank() == 0 {
423        unit_interval_add_points_and_cells(&mut b, nx);
424        b.create_parallel_grid_root(comm, partitioner)
425    } else {
426        b.create_parallel_grid(comm, 0)
427    }
428}
429
430/// Create a unit square grid
431///
432/// The unit square is the square with corners at (0,0), (1,0), (0,1) and (1,1)
433pub fn unit_square<T: Scalar>(
434    nx: usize,
435    ny: usize,
436    cell_type: ReferenceCellType,
437) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
438    let mut b = SingleElementGridBuilder::new_with_capacity(
439        2,
440        (nx + 1) * (ny + 1),
441        match cell_type {
442            ReferenceCellType::Triangle => 2 * nx * ny,
443            ReferenceCellType::Quadrilateral => 2 * nx * ny,
444            _ => {
445                panic!("Unsupported cell type: {cell_type:?}")
446            }
447        },
448        (cell_type, 1),
449    );
450    unit_square_add_points_and_cells(&mut b, nx, ny, cell_type);
451    b.create_grid()
452}
453
454/// Create a unit square grid distributed in parallel
455#[cfg(feature = "mpi")]
456pub fn unit_square_distributed<T: Scalar + Equivalence, C: Communicator>(
457    comm: &C,
458    partitioner: GraphPartitioner,
459    nx: usize,
460    ny: usize,
461    cell_type: ReferenceCellType,
462) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
463    let mut b = SingleElementGridBuilder::new(1, (cell_type, 1));
464    if comm.rank() == 0 {
465        unit_square_add_points_and_cells(&mut b, nx, ny, cell_type);
466        b.create_parallel_grid_root(comm, partitioner)
467    } else {
468        b.create_parallel_grid(comm, 0)
469    }
470}
471
472/// Create a grid of the boundary of a unit square
473///
474/// The unit square is the square with corners at (0,0), (1,0), (0,1) and (1,1)
475pub fn unit_square_boundary<T: Scalar>(
476    nx: usize,
477    ny: usize,
478) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
479    let mut b = SingleElementGridBuilder::new_with_capacity(
480        2,
481        2 * (nx + ny),
482        2 * (nx + ny),
483        (ReferenceCellType::Interval, 1),
484    );
485    unit_square_boundary_add_points_and_cells(&mut b, nx, ny);
486    b.create_grid()
487}
488
489/// Create a grid of the boundary distributed in parallel
490#[cfg(feature = "mpi")]
491pub fn unit_square_boundary_distributed<T: Scalar + Equivalence, C: Communicator>(
492    comm: &C,
493    partitioner: GraphPartitioner,
494    nx: usize,
495    ny: usize,
496    cell_type: ReferenceCellType,
497) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
498    let mut b = SingleElementGridBuilder::new(1, (cell_type, 1));
499    if comm.rank() == 0 {
500        unit_square_boundary_add_points_and_cells(&mut b, nx, ny);
501        b.create_parallel_grid_root(comm, partitioner)
502    } else {
503        b.create_parallel_grid(comm, 0)
504    }
505}
506
507/// Create a unit cube grid
508///
509/// The unit cube is the cube with corners at (0,0,0), (1,0,0), (0,1,0), (1,1,0), (0,0,1),
510/// (1,0,1), (0,1,1) and (1,1,1)
511pub fn unit_cube<T: Scalar>(
512    nx: usize,
513    ny: usize,
514    nz: usize,
515    cell_type: ReferenceCellType,
516) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
517    let mut b = SingleElementGridBuilder::new_with_capacity(
518        3,
519        (nx + 1) * (ny + 1) * (nz + 1),
520        match cell_type {
521            ReferenceCellType::Tetrahedron => 6 * nx * ny * nz,
522            ReferenceCellType::Hexahedron => 2 * nx * ny * nz,
523            _ => {
524                panic!("Unsupported cell type: {cell_type:?}")
525            }
526        },
527        (cell_type, 1),
528    );
529
530    unit_cube_add_points_and_cells(&mut b, nx, ny, nz, cell_type);
531    b.create_grid()
532}
533
534/// Create a unit cube grid distributed in parallel
535#[cfg(feature = "mpi")]
536pub fn unit_cube_distributed<T: Scalar + Equivalence, C: Communicator>(
537    comm: &C,
538    partitioner: GraphPartitioner,
539    nx: usize,
540    ny: usize,
541    nz: usize,
542    cell_type: ReferenceCellType,
543) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
544    let mut b = SingleElementGridBuilder::new(3, (cell_type, 1));
545    if comm.rank() == 0 {
546        unit_cube_add_points_and_cells(&mut b, nx, ny, nz, cell_type);
547        b.create_parallel_grid_root(comm, partitioner)
548    } else {
549        b.create_parallel_grid(comm, 0)
550    }
551}
552
553/// Create a grid of the boundary of a unit cube
554///
555/// The unit cube is the cube with corners at (0,0,0), (1,0,0), (0,1,0), (1,1,0), (0,0,1),
556/// (1,0,1), (0,1,1) and (1,1,1)
557pub fn unit_cube_boundary<T: Scalar>(
558    nx: usize,
559    ny: usize,
560    nz: usize,
561    cell_type: ReferenceCellType,
562) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
563    let mut b = SingleElementGridBuilder::new_with_capacity(
564        3,
565        (nx + 1) * (ny + 1) * (nz + 1) - (nx - 1) * (ny - 1) * (nz - 1),
566        match cell_type {
567            ReferenceCellType::Triangle => 4 * (nx * ny + nx * nz + ny * nz),
568            ReferenceCellType::Quadrilateral => 2 * (nx * ny + nx * nz + ny * nz),
569            _ => {
570                panic!("Unsupported cell type: {cell_type:?}")
571            }
572        },
573        (cell_type, 1),
574    );
575    unit_cube_boundary_add_points_and_cells(&mut b, nx, ny, nz, cell_type);
576    b.create_grid()
577}
578
579/// Create a grid of the boundary of a unit cube distributed in parallel
580#[cfg(feature = "mpi")]
581pub fn unit_cube_boundary_distributed<T: Scalar + Equivalence, C: Communicator>(
582    comm: &C,
583    partitioner: GraphPartitioner,
584    nx: usize,
585    ny: usize,
586    nz: usize,
587    cell_type: ReferenceCellType,
588) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
589    let mut b = SingleElementGridBuilder::new(3, (cell_type, 1));
590    if comm.rank() == 0 {
591        unit_cube_boundary_add_points_and_cells(&mut b, nx, ny, nz, cell_type);
592        b.create_parallel_grid_root(comm, partitioner)
593    } else {
594        b.create_parallel_grid(comm, 0)
595    }
596}
597
598/// Create a grid of the edges of a unit cube
599///
600/// The unit cube is the cube with corners at (0,0,0), (1,0,0), (0,1,0), (1,1,0), (0,0,1),
601/// (1,0,1), (0,1,1) and (1,1,1)
602pub fn unit_cube_edges<T: Scalar>(
603    nx: usize,
604    ny: usize,
605    nz: usize,
606) -> SingleElementGrid<T, CiarletElement<T, IdentityMap, T>> {
607    let mut b = SingleElementGridBuilder::new_with_capacity(
608        3,
609        4 * (nx + ny + nz + 1),
610        4 * (nx + ny + nz),
611        (ReferenceCellType::Interval, 1),
612    );
613    unit_cube_edges_add_points_and_cells(&mut b, nx, ny, nz);
614    b.create_grid()
615}
616
617/// Create a grid of the edges of a unit cube distributed in parallel
618#[cfg(feature = "mpi")]
619pub fn unit_cube_edges_distributed<T: Scalar + Equivalence, C: Communicator>(
620    comm: &C,
621    partitioner: GraphPartitioner,
622    nx: usize,
623    ny: usize,
624    nz: usize,
625) -> ParallelGridImpl<'_, C, SingleElementGrid<T, CiarletElement<T, IdentityMap, T>>> {
626    let mut b = SingleElementGridBuilder::new(3, (ReferenceCellType::Interval, 1));
627    if comm.rank() == 0 {
628        unit_cube_edges_add_points_and_cells(&mut b, nx, ny, nz);
629        b.create_parallel_grid_root(comm, partitioner)
630    } else {
631        b.create_parallel_grid(comm, 0)
632    }
633}
634
635#[cfg(test)]
636mod test {
637    use super::*;
638    use crate::traits::{Entity, Geometry, Grid, Point};
639    use approx::*;
640    use itertools::izip;
641
642    fn max(values: &[f64]) -> f64 {
643        let mut out = values[0];
644        for i in &values[1..] {
645            if *i > out {
646                out = *i;
647            }
648        }
649        out
650    }
651
652    fn check_volume(
653        grid: &impl Grid<T = f64, EntityDescriptor = ReferenceCellType>,
654        expected_volume: f64,
655    ) {
656        let mut volume = 0.0;
657        let gdim = grid.geometry_dim();
658        for t in grid.cell_types() {
659            for cell in grid.entity_iter(*t) {
660                let g = cell.geometry();
661                let mut point = vec![0.0; gdim];
662                let mut min_p = vec![10.0; gdim];
663                let mut max_p = vec![-10.0; gdim];
664                for p in g.points() {
665                    p.coords(&mut point);
666                    for (j, v) in point.iter().enumerate() {
667                        if *v < min_p[j] {
668                            min_p[j] = *v;
669                        }
670                        if *v > max_p[j] {
671                            max_p[j] = *v;
672                        }
673                    }
674                }
675                volume += match cell.entity_type() {
676                    ReferenceCellType::Interval => {
677                        max(&izip!(min_p, max_p).map(|(i, j)| j - i).collect::<Vec<_>>())
678                    }
679                    ReferenceCellType::Triangle => match gdim {
680                        2 => (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]) / 2.0,
681                        3 => max(&[
682                            (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]) / 2.0,
683                            (max_p[0] - min_p[0]) * (max_p[2] - min_p[2]) / 2.0,
684                            (max_p[1] - min_p[1]) * (max_p[2] - min_p[2]) / 2.0,
685                        ]),
686                        _ => {
687                            panic!("Unsupported dimension");
688                        }
689                    },
690                    ReferenceCellType::Quadrilateral => match gdim {
691                        2 => (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]),
692                        3 => max(&[
693                            (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]),
694                            (max_p[0] - min_p[0]) * (max_p[2] - min_p[2]),
695                            (max_p[1] - min_p[1]) * (max_p[2] - min_p[2]),
696                        ]),
697                        _ => {
698                            panic!("Unsupported dimension");
699                        }
700                    },
701                    ReferenceCellType::Tetrahedron => {
702                        (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]) * (max_p[2] - min_p[2]) / 6.0
703                    }
704                    ReferenceCellType::Hexahedron => {
705                        (max_p[0] - min_p[0]) * (max_p[1] - min_p[1]) * (max_p[2] - min_p[2])
706                    }
707                    _ => {
708                        panic!("Unsupported cell");
709                    }
710                };
711            }
712        }
713        assert_relative_eq!(volume, expected_volume, epsilon = 1e-10);
714    }
715
716    #[test]
717    fn test_unit_interval() {
718        check_volume(&unit_interval::<f64>(1), 1.0);
719        check_volume(&unit_interval::<f64>(2), 1.0);
720        check_volume(&unit_interval::<f64>(4), 1.0);
721        check_volume(&unit_interval::<f64>(7), 1.0);
722    }
723
724    #[test]
725    fn test_unit_square_triangle() {
726        check_volume(&unit_square::<f64>(1, 1, ReferenceCellType::Triangle), 1.0);
727        check_volume(&unit_square::<f64>(2, 2, ReferenceCellType::Triangle), 1.0);
728        check_volume(&unit_square::<f64>(4, 5, ReferenceCellType::Triangle), 1.0);
729        check_volume(&unit_square::<f64>(7, 6, ReferenceCellType::Triangle), 1.0);
730    }
731
732    #[test]
733    fn test_unit_square_quadrilateral() {
734        check_volume(
735            &unit_square::<f64>(1, 1, ReferenceCellType::Quadrilateral),
736            1.0,
737        );
738        check_volume(
739            &unit_square::<f64>(2, 2, ReferenceCellType::Quadrilateral),
740            1.0,
741        );
742        check_volume(
743            &unit_square::<f64>(4, 5, ReferenceCellType::Quadrilateral),
744            1.0,
745        );
746        check_volume(
747            &unit_square::<f64>(7, 6, ReferenceCellType::Quadrilateral),
748            1.0,
749        );
750    }
751
752    #[test]
753    fn test_unit_square_boundary() {
754        check_volume(&unit_square_boundary::<f64>(1, 1), 4.0);
755        check_volume(&unit_square_boundary::<f64>(2, 2), 4.0);
756        check_volume(&unit_square_boundary::<f64>(4, 5), 4.0);
757        check_volume(&unit_square_boundary::<f64>(7, 6), 4.0);
758    }
759
760    #[test]
761    fn test_unit_cube_boundary_triangle() {
762        check_volume(
763            &unit_cube_boundary::<f64>(1, 1, 1, ReferenceCellType::Triangle),
764            6.0,
765        );
766        check_volume(
767            &unit_cube_boundary::<f64>(2, 2, 2, ReferenceCellType::Triangle),
768            6.0,
769        );
770        check_volume(
771            &unit_cube_boundary::<f64>(4, 5, 5, ReferenceCellType::Triangle),
772            6.0,
773        );
774        check_volume(
775            &unit_cube_boundary::<f64>(7, 6, 4, ReferenceCellType::Triangle),
776            6.0,
777        );
778    }
779
780    #[test]
781    fn test_unit_cube_boundary_quadrilateral() {
782        check_volume(
783            &unit_cube_boundary::<f64>(1, 1, 1, ReferenceCellType::Quadrilateral),
784            6.0,
785        );
786        check_volume(
787            &unit_cube_boundary::<f64>(2, 2, 2, ReferenceCellType::Quadrilateral),
788            6.0,
789        );
790        check_volume(
791            &unit_cube_boundary::<f64>(4, 5, 5, ReferenceCellType::Quadrilateral),
792            6.0,
793        );
794        check_volume(
795            &unit_cube_boundary::<f64>(7, 6, 4, ReferenceCellType::Quadrilateral),
796            6.0,
797        );
798    }
799
800    #[test]
801    fn test_unit_cube_tetrahedron() {
802        check_volume(
803            &unit_cube::<f64>(1, 1, 1, ReferenceCellType::Tetrahedron),
804            1.0,
805        );
806        check_volume(
807            &unit_cube::<f64>(2, 2, 2, ReferenceCellType::Tetrahedron),
808            1.0,
809        );
810        check_volume(
811            &unit_cube::<f64>(4, 5, 5, ReferenceCellType::Tetrahedron),
812            1.0,
813        );
814        check_volume(
815            &unit_cube::<f64>(7, 6, 4, ReferenceCellType::Tetrahedron),
816            1.0,
817        );
818    }
819    #[test]
820    fn test_unit_cube_hexahedron() {
821        check_volume(
822            &unit_cube::<f64>(1, 1, 1, ReferenceCellType::Hexahedron),
823            1.0,
824        );
825        check_volume(
826            &unit_cube::<f64>(2, 2, 2, ReferenceCellType::Hexahedron),
827            1.0,
828        );
829        check_volume(
830            &unit_cube::<f64>(4, 5, 5, ReferenceCellType::Hexahedron),
831            1.0,
832        );
833        check_volume(
834            &unit_cube::<f64>(7, 6, 4, ReferenceCellType::Hexahedron),
835            1.0,
836        );
837    }
838
839    #[test]
840    fn test_unit_cube_edges() {
841        check_volume(&unit_cube_edges::<f64>(1, 1, 1), 12.0);
842        check_volume(&unit_cube_edges::<f64>(2, 2, 2), 12.0);
843        check_volume(&unit_cube_edges::<f64>(4, 5, 5), 12.0);
844        check_volume(&unit_cube_edges::<f64>(7, 6, 4), 12.0);
845    }
846}