Skip to main content

test_parallel_mesh/
test_parallel_mesh.rs

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