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
15fn 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 let cell_count_owned = grid
48 .local_grid()
49 .entity_iter(ReferenceCellType::Quadrilateral)
50 .filter(|entity| entity.is_owned())
51 .count();
52
53 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 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 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
141fn 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
151fn 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
173fn 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
211fn 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}