ndfunctionspace/function_space/
parallel.rs1use crate::{
3 function_space::FunctionSpaceImpl,
4 traits::{FunctionSpace, ParallelFunctionSpace},
5};
6use mpi::{
7 collective::SystemOperation,
8 traits::{Communicator, CommunicatorCollectives, Equivalence},
9};
10use ndelement::{
11 traits::{ElementFamily, MappedFiniteElement},
12 types::ReferenceCellType,
13};
14use ndmesh::{
15 traits::{Entity, Mesh, ParallelMesh},
16 types::Ownership,
17};
18use rlst::{RlstScalar, distributed_tools::array_tools::all_to_allv};
19use std::fmt::Debug;
20use std::hash::Hash;
21
22pub struct ProcessFunctionSpace<S: FunctionSpace> {
24 space: S,
25 global_size: usize,
26 global_dof_indices: Vec<usize>,
27 ownership: Vec<Ownership>,
28}
29
30impl<S: FunctionSpace> ProcessFunctionSpace<S> {
31 fn new(
32 space: S,
33 global_size: usize,
34 global_dof_indices: Vec<usize>,
35 ownership: Vec<Ownership>,
36 ) -> Self {
37 Self {
38 space,
39 global_size,
40 global_dof_indices,
41 ownership,
42 }
43 }
44}
45
46impl<S: FunctionSpace> FunctionSpace for ProcessFunctionSpace<S> {
47 type T = S::T;
48 type TMesh = S::TMesh;
49 type EntityDescriptor = S::EntityDescriptor;
50 type Mesh = S::Mesh;
51 type FiniteElement = S::FiniteElement;
52
53 fn mesh(&self) -> &S::Mesh {
54 self.space.mesh()
55 }
56
57 fn elements(&self) -> &[S::FiniteElement] {
58 self.space.elements()
59 }
60
61 fn entities_by_element(&self, element_index: usize) -> Option<&[usize]> {
62 self.space.entities_by_element(element_index)
63 }
64
65 fn entity_dofs(
66 &self,
67 entity_type: S::EntityDescriptor,
68 entity_number: usize,
69 ) -> Option<&[usize]> {
70 self.space.entity_dofs(entity_type, entity_number)
71 }
72
73 fn entity_closure_dofs(
74 &self,
75 entity_type: S::EntityDescriptor,
76 entity_number: usize,
77 ) -> Option<&[usize]> {
78 self.space.entity_closure_dofs(entity_type, entity_number)
79 }
80
81 fn process_size(&self) -> usize {
82 self.space.process_size()
83 }
84
85 fn process_owned_size(&self) -> usize {
86 self.ownership
87 .iter()
88 .filter(|x| matches!(x, Ownership::Owned))
89 .count()
90 }
91
92 fn global_size(&self) -> usize {
93 self.global_size
94 }
95
96 fn global_dof_index(&self, process_dof_index: usize) -> usize {
97 self.global_dof_indices[process_dof_index]
98 }
99
100 fn ownership(&self, process_dof_index: usize) -> Ownership {
101 self.ownership[process_dof_index]
102 }
103}
104
105pub struct ParallelFunctionSpaceImpl<
107 'a,
108 T: RlstScalar,
109 TMesh: RlstScalar,
110 E: Debug + PartialEq + Eq + Clone + Copy + Hash,
111 G: Mesh<EntityDescriptor = E, T = TMesh>,
112 PG: ParallelMesh<LocalMesh = G>,
113 F: MappedFiniteElement<CellType = E, T = T>,
114 S: FunctionSpace<Mesh = G, EntityDescriptor = E, FiniteElement = F>,
115> {
116 mesh: &'a PG,
117 process_space: ProcessFunctionSpace<S>,
118 global_size: usize,
119}
120
121#[derive(Debug, Clone, PartialEq)]
123enum DofIndex {
124 None,
126 Owned,
128 Ghost(usize, usize, usize),
130}
131
132#[derive(Equivalence, Debug, Clone, PartialEq)]
133struct GhostEntity {
134 entity_type: ReferenceCellType,
135 entity_index: usize,
136}
137
138impl GhostEntity {
139 fn new(entity_type: ReferenceCellType, entity_index: usize) -> Self {
140 Self {
141 entity_type,
142 entity_index,
143 }
144 }
145}
146
147impl<
148 'a,
149 T: RlstScalar,
150 TMesh: RlstScalar,
151 G: Mesh<EntityDescriptor = ReferenceCellType, T = TMesh>,
152 PG: ParallelMesh<LocalMesh = G>,
153 F: MappedFiniteElement<CellType = ReferenceCellType, T = T>,
154>
155 ParallelFunctionSpaceImpl<
156 'a,
157 T,
158 TMesh,
159 ReferenceCellType,
160 G,
161 PG,
162 F,
163 FunctionSpaceImpl<'a, T, TMesh, ReferenceCellType, G, F>,
164 >
165{
166 pub fn new<EF: ElementFamily<FiniteElement = F, CellType = ReferenceCellType>>(
168 mesh: &'a PG,
169 family: &EF,
170 ) -> Self {
171 let comm = mesh.comm();
172 let local_mesh = mesh.local_mesh();
173 let serial_space = FunctionSpaceImpl::new(local_mesh, family);
174
175 let mut dof_indices = vec![DofIndex::None; serial_space.process_size()];
176
177 let mut ask_for = vec![vec![]; comm.size() as usize];
179
180 for dim in 0..=local_mesh.topology_dim() {
182 for entity_type in local_mesh.entity_types(dim) {
183 for entity in local_mesh.entity_iter(*entity_type) {
184 let dofs = serial_space
185 .entity_dofs(*entity_type, entity.local_index())
186 .unwrap();
187 if !dofs.is_empty() {
188 match entity.ownership() {
189 Ownership::Owned => {
190 for j in dofs {
191 dof_indices[*j] = DofIndex::Owned
192 }
193 }
194 Ownership::Ghost(process, entity_index) => {
195 for (i, j) in dofs.iter().enumerate() {
196 dof_indices[*j] =
197 DofIndex::Ghost(process, ask_for[process].len(), i);
198 }
199 ask_for[process].push((*entity_type, entity_index));
200 }
201 _ => {
202 panic!("Invalid ownership");
203 }
204 }
205 }
206 }
207 }
208 }
209
210 let mut ndofs = 0;
212 let mut process_dof_indices = vec![None; serial_space.process_size()];
213 for (i, j) in dof_indices.iter().enumerate() {
214 if let DofIndex::Owned = j {
215 process_dof_indices[i] = Some(ndofs);
216 ndofs += 1;
217 }
218 }
219
220 let mut global_size = 0;
222 let mut offset = 0;
223 comm.exclusive_scan_into(&ndofs, &mut offset, SystemOperation::sum());
224 comm.all_reduce_into(&ndofs, &mut global_size, SystemOperation::sum());
225
226 let counts = ask_for.iter().map(|i| i.len()).collect::<Vec<_>>();
228 let ask_for_flat = ask_for
229 .iter()
230 .flatten()
231 .map(|(t, i)| GhostEntity::new(*t, *i))
232 .collect::<Vec<_>>();
233
234 let (recv_counts, recv_data) = all_to_allv(comm, &counts, &ask_for_flat);
235
236 let asked_for_local = recv_data
238 .iter()
239 .map(|entity| {
240 serial_space
241 .entity_dofs(entity.entity_type, entity.entity_index)
242 .unwrap()[0]
243 })
244 .collect::<Vec<_>>();
245 let asked_for_global = asked_for_local
246 .iter()
247 .map(|i| {
248 let dof = process_dof_indices[*i].unwrap();
249 dof + offset
250 })
251 .collect::<Vec<_>>();
252
253 let (_, process_ghost_data) = all_to_allv(comm, &recv_counts, &asked_for_local);
255 let (_, global_ghost_data) = all_to_allv(comm, &recv_counts, &asked_for_global);
256
257 let mut start = 0;
258 let process_ghost_indices = counts
259 .iter()
260 .map(|c| {
261 let data = &process_ghost_data[start..start + c];
262 start += c;
263 data.to_vec()
264 })
265 .collect::<Vec<_>>();
266 let mut start = 0;
267 let global_ghost_indices = counts
268 .iter()
269 .map(|c| {
270 let data = &global_ghost_data[start..start + c];
271 start += c;
272 data.to_vec()
273 })
274 .collect::<Vec<_>>();
275
276 let offset = 1;
279 let global_dof_indices = dof_indices
280 .iter()
281 .enumerate()
282 .map(|(i, index)| match index {
283 DofIndex::Owned => offset + process_dof_indices[i].unwrap(),
284 DofIndex::Ghost(process, index, number) => {
285 global_ghost_indices[*process][*index] + *number
286 }
287 DofIndex::None => {
288 panic!("Unset DOF index");
289 }
290 })
291 .collect::<Vec<_>>();
292 let ownership = dof_indices
293 .iter()
294 .map(|i| match i {
295 DofIndex::Owned => Ownership::Owned,
296 DofIndex::Ghost(process, index, number) => {
297 Ownership::Ghost(*process, process_ghost_indices[*process][*index] + *number)
298 }
299 DofIndex::None => {
300 panic!("Unset DOF index");
301 }
302 })
303 .collect::<Vec<_>>();
304
305 let process_space =
306 ProcessFunctionSpace::new(serial_space, global_size, global_dof_indices, ownership);
307 Self {
308 mesh,
309 process_space,
310 global_size,
311 }
312 }
313}
314
315impl<
316 'a,
317 T: RlstScalar,
318 TMesh: RlstScalar,
319 E: Debug + PartialEq + Eq + Clone + Copy + Hash,
320 G: Mesh<EntityDescriptor = E, T = TMesh>,
321 PG: ParallelMesh<LocalMesh = G>,
322 F: MappedFiniteElement<CellType = E, T = T>,
323 S: FunctionSpace<Mesh = G, EntityDescriptor = E, FiniteElement = F>,
324> ParallelFunctionSpace for ParallelFunctionSpaceImpl<'a, T, TMesh, E, G, PG, F, S>
325{
326 type ProcessSpace = ProcessFunctionSpace<S>;
327 type C = PG::C;
328
329 fn comm(&self) -> &PG::C {
330 self.mesh.comm()
331 }
332
333 fn process_space(&self) -> &ProcessFunctionSpace<S> {
334 &self.process_space
335 }
336
337 fn global_size(&self) -> usize {
338 self.global_size
339 }
340}