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
16fn 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 let cell_count_owned = mesh
48 .local_mesh()
49 .entity_iter(ReferenceCellType::Quadrilateral)
50 .filter(|entity| entity.is_owned())
51 .count();
52
53 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 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 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
138fn 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
148fn 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
170fn 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
208fn 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}