Skip to main content

test_parallel_grid/
test_parallel_grid.rs

1use mpi::{
2    collective::SystemOperation, environment::Universe, topology::Communicator,
3    traits::CommunicatorCollectives,
4};
5use ndelement::{ciarlet::CiarletElement, map::IdentityMap, types::ReferenceCellType};
6use ndgrid::{
7    SingleElementGrid, SingleElementGridBuilder,
8    grid::ParallelGridImpl,
9    traits::{
10        Builder, Entity, Grid, ParallelBuilder, ParallelGrid, RONExportParallel, RONImportParallel,
11    },
12    types::{GraphPartitioner, Ownership},
13};
14
15/// Test that a graph partitioner works
16fn run_partitioner_test<C: Communicator>(comm: &C, partitioner: GraphPartitioner) {
17    let n = 10;
18
19    let mut b = SingleElementGridBuilder::<f64>::new(2, (ReferenceCellType::Quadrilateral, 1));
20
21    let rank = comm.rank();
22    let grid = if rank == 0 {
23        let mut i = 0;
24        for y in 0..n {
25            for x in 0..n {
26                b.add_point(i, &[x as f64 / (n - 1) as f64, y as f64 / (n - 1) as f64]);
27                i += 1;
28            }
29        }
30
31        let mut i = 0;
32        for y in 0..n - 1 {
33            for x in 0..n - 1 {
34                let sw = n * y + x;
35                b.add_cell(i, &[sw, sw + 1, sw + n, sw + n + 1]);
36                i += 1;
37            }
38        }
39
40        b.create_parallel_grid_root(comm, partitioner)
41    } else {
42        b.create_parallel_grid(comm, 0)
43    };
44
45    // Check that owned cells are sorted ahead of ghost cells
46
47    let cell_count_owned = grid
48        .local_grid()
49        .entity_iter(ReferenceCellType::Quadrilateral)
50        .filter(|entity| entity.is_owned())
51        .count();
52
53    // Now check that the first `cell_count_owned` entities are actually owned.
54    for cell in grid
55        .local_grid()
56        .entity_iter(ReferenceCellType::Quadrilateral)
57        .take(cell_count_owned)
58    {
59        assert!(cell.is_owned())
60    }
61
62    // Now make sure that the indices of the global cells are in consecutive order
63
64    let mut cell_global_count = grid.cell_layout().local_range().0;
65
66    for cell in grid
67        .local_grid()
68        .entity_iter(ReferenceCellType::Quadrilateral)
69        .take(cell_count_owned)
70    {
71        assert_eq!(cell.global_index(), cell_global_count);
72        cell_global_count += 1;
73    }
74
75    // Get the global indices.
76
77    let global_vertices = grid
78        .local_grid()
79        .entity_iter(ReferenceCellType::Point)
80        .filter(|e| matches!(e.ownership(), Ownership::Owned))
81        .map(|e| e.global_index())
82        .collect::<Vec<_>>();
83
84    let nvertices = global_vertices.len();
85
86    let global_cells = grid
87        .local_grid()
88        .entity_iter(ReferenceCellType::Quadrilateral)
89        .filter(|e| matches!(e.ownership(), Ownership::Owned))
90        .map(|e| e.global_index())
91        .collect::<Vec<_>>();
92
93    let ncells = global_cells.len();
94
95    let mut total_cells: usize = 0;
96    let mut total_vertices: usize = 0;
97
98    comm.all_reduce_into(&ncells, &mut total_cells, SystemOperation::sum());
99    comm.all_reduce_into(&nvertices, &mut total_vertices, SystemOperation::sum());
100
101    assert_eq!(total_cells, (n - 1) * (n - 1));
102    assert_eq!(total_vertices, n * n);
103}
104
105fn create_single_element_grid_data(b: &mut SingleElementGridBuilder<f64>, n: usize) {
106    for y in 0..n {
107        for x in 0..n {
108            b.add_point(
109                y * n + x,
110                &[x as f64 / (n - 1) as f64, y as f64 / (n - 1) as f64, 0.0],
111            );
112        }
113    }
114
115    for i in 0..n - 1 {
116        for j in 0..n - 1 {
117            b.add_cell(
118                i * (n - 1) + j,
119                &[j * n + i, j * n + i + 1, j * n + i + n, j * n + i + n + 1],
120            );
121        }
122    }
123}
124
125fn example_single_element_grid<C: Communicator>(
126    comm: &C,
127    n: usize,
128) -> ParallelGridImpl<'_, C, SingleElementGrid<f64, CiarletElement<f64, IdentityMap, f64>>> {
129    let rank = comm.rank();
130
131    let mut b = SingleElementGridBuilder::<f64>::new(3, (ReferenceCellType::Quadrilateral, 1));
132
133    if rank == 0 {
134        create_single_element_grid_data(&mut b, n);
135        b.create_parallel_grid_root(comm, GraphPartitioner::None)
136    } else {
137        b.create_parallel_grid(comm, 0)
138    }
139}
140
141/// Test that grids can be exported as RON in parallel
142fn test_parallel_export<C: Communicator>(comm: &C) {
143    let size = comm.size();
144
145    let n = 10;
146    let grid = example_single_element_grid(comm, n);
147    let filename = format!("_examples_parallel_io_{size}ranks.ron");
148    grid.export_as_ron(&filename);
149}
150
151/// Test that grids can be imported from RON in parallel
152fn test_parallel_import<C: Communicator>(comm: &C) {
153    use ndgrid::traits::ParallelGrid;
154
155    let size = comm.size();
156
157    let filename = format!("_examples_parallel_io_{size}ranks.ron");
158    let grid = ParallelGridImpl::<
159        '_,
160        C,
161        SingleElementGrid<f64, CiarletElement<f64, IdentityMap, f64>>,
162    >::import_from_ron(comm, &filename);
163
164    let n = 10;
165    let grid2 = example_single_element_grid(comm, n);
166
167    assert_eq!(
168        grid.local_grid().entity_count(ReferenceCellType::Point),
169        grid2.local_grid().entity_count(ReferenceCellType::Point)
170    );
171}
172
173/// Test that using non-contiguous numbering does not cause panic
174fn test_noncontiguous_numbering<C: Communicator>(comm: &C) {
175    let rank = comm.rank();
176    let mut b = SingleElementGridBuilder::<f64>::new(3, (ReferenceCellType::Quadrilateral, 1));
177
178    let g = if rank == 0 {
179        let n = 5;
180        for y in 0..n {
181            for x in 0..n {
182                b.add_point(
183                    2 * (y * n + x) + 5,
184                    &[x as f64 / (n - 1) as f64, y as f64 / (n - 1) as f64, 0.0],
185                );
186            }
187        }
188
189        for i in 0..n - 1 {
190            for j in 0..n - 1 {
191                b.add_cell(
192                    3 * (i * (n - 1) + j),
193                    &[
194                        2 * (j * n + i) + 5,
195                        2 * (j * n + i + 1) + 5,
196                        2 * (j * n + i + n) + 5,
197                        2 * (j * n + i + n + 1) + 5,
198                    ],
199                );
200            }
201        }
202
203        b.create_parallel_grid_root(comm, GraphPartitioner::None)
204    } else {
205        b.create_parallel_grid(comm, 0)
206    };
207
208    assert!(g.local_grid().entity_count(ReferenceCellType::Point) > 0);
209}
210
211/// Run tests
212fn main() {
213    let universe: Universe = mpi::initialize().unwrap();
214    let world = universe.world();
215    let rank = world.rank();
216
217    if rank == 0 {
218        println!("Testing non-contiguous numbering");
219    }
220    test_noncontiguous_numbering(&world);
221
222    if rank == 0 {
223        println!("Testing parallel grid export");
224    }
225    test_parallel_export(&world);
226
227    world.barrier();
228
229    if rank == 0 {
230        println!("Testing parallel grid import");
231    }
232    test_parallel_import(&world);
233
234    if rank == 0 {
235        println!("Testing partitioning using GraphPartitioner::None");
236    }
237    run_partitioner_test(&world, GraphPartitioner::None);
238
239    let mut p = vec![];
240    for i in 0..81 {
241        p.push(i % world.size() as usize);
242    }
243    if rank == 0 {
244        println!("Testing partitioning using GraphPartitioner::Manual");
245    }
246    run_partitioner_test(&world, GraphPartitioner::Manual(p));
247
248    if rank == 0 {
249        println!("Testing partitioning using GraphPartitioner::Coupe");
250    }
251    run_partitioner_test(&world, GraphPartitioner::Coupe);
252
253    if rank == 0 {
254        println!("Testing partitioning using GraphPartitioner::Scotch");
255    }
256    run_partitioner_test(&world, GraphPartitioner::Scotch);
257}