1use crate::algs::communicator::{CommTag, Communicator, Wait};
4use crate::algs::completion::{complete_section_with_ownership, complete_sieve};
5use crate::algs::point_sf::{PointSF, balance_partition_boundary_ownership};
6use crate::algs::wire::{WirePointRepr, cast_slice, cast_slice_mut};
7use crate::data::atlas::Atlas;
8use crate::data::coordinates::{Coordinates, HighOrderCoordinates};
9use crate::data::discretization::Discretization;
10use crate::data::global_map::LocalToGlobalMap;
11use crate::data::mixed_section::{MixedSectionStore, TaggedSection};
12use crate::data::section::Section;
13use crate::data::storage::Storage;
14use crate::io::MeshData;
15use crate::mesh_error::MeshSieveError;
16use crate::overlap::delta::{CellTypeDelta, CopyDelta};
17use crate::overlap::overlap::{Overlap, OvlId};
18use crate::overlap::overlap::{ensure_closure_of_support, expand_one_layer_mesh};
19use crate::topology::cell_type::CellType;
20use crate::topology::labels::LabelSet;
21use crate::topology::ownership::PointOwnership;
22use crate::topology::periodic::PointEquivalence;
23use crate::topology::point::PointId;
24use crate::topology::sieve::{MeshSieve, OrientedSieve, Sieve};
25use crate::topology::validation::debug_validate_overlap_ownership_topology;
26use bytemuck::Zeroable;
27use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
28
29pub fn distribute_mesh<M, C>(
76 mesh: &M,
77 parts: &[usize],
78 comm: &C,
79) -> Result<(MeshSieve, Overlap), MeshSieveError>
80where
81 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
82 C: Communicator + Sync,
83{
84 let my_rank = comm.rank();
85
86 let mut max_id = 0u64;
88 let pts: Vec<PointId> = mesh
89 .points()
90 .inspect(|p| max_id = max_id.max(p.get()))
91 .collect();
92 if parts.len() < max_id as usize {
93 return Err(MeshSieveError::PartitionIndexOutOfBounds(parts.len()));
94 }
95
96 let mut foreign_pts: Vec<(PointId, usize)> = Vec::new();
98 foreign_pts.reserve(pts.len() / 2);
99
100 for p in &pts {
101 let owner = owner_of(parts, *p)?;
102 if owner != my_rank {
103 foreign_pts.push((*p, owner));
104 }
105 }
106
107 let mut overlap = Overlap::default();
109 overlap.try_add_links_structural_bulk(foreign_pts)?;
110
111 #[cfg(any(
112 debug_assertions,
113 feature = "strict-invariants",
114 feature = "check-invariants"
115 ))]
116 overlap.validate_invariants()?;
117
118 let mut local = MeshSieve::default();
120 for src in &pts {
121 if owner_of(parts, *src)? == my_rank {
122 for (dst, orient) in mesh.cone_o(*src) {
123 if owner_of(parts, dst)? == my_rank {
124 local.add_arrow_o(*src, dst, (), orient)?;
125 }
126 }
127 }
128 }
129
130 Ok((local, overlap))
131}
132
133pub fn resolve_overlap_identity(overlap: &mut Overlap) {
135 let mut to_resolve = Vec::new();
136 for src in overlap.base_points() {
137 if let OvlId::Local(p) = src {
138 for (dst, rem) in overlap.cone(src) {
139 if let OvlId::Part(r) = dst {
140 debug_assert_eq!(rem.rank, r);
141 to_resolve.push((p, r));
142 }
143 }
144 }
145 }
146 for (p, r) in to_resolve {
147 overlap
148 .resolve_remote_point(p, r, p)
149 .expect("resolve_remote_point failed");
150 }
151}
152
153#[derive(Clone, Copy, Debug)]
155pub struct DistributionConfig {
156 pub overlap_depth: usize,
158 pub synchronize_sections: bool,
160 pub balance_boundary_ownership: bool,
162}
163
164impl Default for DistributionConfig {
165 fn default() -> Self {
166 Self {
167 overlap_depth: 1,
168 synchronize_sections: true,
169 balance_boundary_ownership: false,
170 }
171 }
172}
173
174#[derive(Debug)]
176pub struct DistributedMeshData<V, St, CtSt>
177where
178 St: Storage<V> + Clone,
179 CtSt: Storage<CellType> + Clone,
180{
181 pub sieve: MeshSieve,
183 pub overlap: Overlap,
185 pub point_owners: Vec<usize>,
187 pub ownership: PointOwnership,
189 pub cell_parts: Vec<usize>,
191 pub coordinates: Option<Coordinates<V, St>>,
193 pub sections: BTreeMap<String, Section<V, St>>,
195 pub mixed_sections: MixedSectionStore,
197 pub labels: Option<LabelSet>,
199 pub cell_types: Option<Section<CellType, CtSt>>,
201 pub discretization: Option<Discretization>,
203}
204
205impl<V, St, CtSt> DistributedMeshData<V, St, CtSt>
206where
207 St: Storage<V> + Clone,
208 CtSt: Storage<CellType> + Clone,
209{
210 pub fn build_global_map_for_section<C>(
212 &self,
213 section: &Section<V, St>,
214 comm: &C,
215 ) -> Result<LocalToGlobalMap, MeshSieveError>
216 where
217 C: Communicator + Sync,
218 {
219 LocalToGlobalMap::from_section_with_ownership(
220 section,
221 &self.overlap,
222 &self.ownership,
223 comm,
224 comm.rank(),
225 )
226 }
227
228 pub fn build_global_section_maps<C>(
230 &self,
231 comm: &C,
232 ) -> Result<BTreeMap<String, LocalToGlobalMap>, MeshSieveError>
233 where
234 C: Communicator + Sync,
235 {
236 let mut maps = BTreeMap::new();
237 for (name, section) in &self.sections {
238 let map = self.build_global_map_for_section(section, comm)?;
239 maps.insert(name.clone(), map);
240 }
241 Ok(maps)
242 }
243
244 pub fn distribute_fields<C>(&mut self, comm: &C) -> Result<(), MeshSieveError>
246 where
247 C: Communicator + Sync,
248 V: Clone + Default + Send + PartialEq + bytemuck::Pod + 'static,
249 {
250 let sf = PointSF::with_ownership(&self.overlap, &self.ownership, comm, comm.rank());
251 sf.validate()?;
252
253 if let Some(coords) = &mut self.coordinates {
254 sf.complete_section(coords.section_mut())?;
255 if let Some(high_order) = coords.high_order_mut() {
256 sf.complete_section(high_order.section_mut())?;
257 }
258 }
259
260 for section in self.sections.values_mut() {
261 sf.complete_section(section)?;
262 }
263
264 for (_name, section) in self.mixed_sections.iter_mut() {
265 complete_tagged_section_with_ownership(
266 section,
267 &self.overlap,
268 &self.ownership,
269 comm,
270 comm.rank(),
271 )?;
272 }
273
274 if let Some(cell_types) = &mut self.cell_types {
275 complete_section_with_ownership::<CellType, _, CellTypeDelta, C>(
276 cell_types,
277 &self.overlap,
278 &self.ownership,
279 comm,
280 comm.rank(),
281 )?;
282 }
283
284 if let Some(labels) = &mut self.labels {
285 complete_labels_with_ownership(
286 labels,
287 &self.overlap,
288 &self.ownership,
289 comm,
290 comm.rank(),
291 )?;
292 }
293
294 Ok(())
295 }
296}
297
298pub trait CellPartitioner<M>
300where
301 M: Sieve<Point = PointId, Payload = ()>,
302{
303 fn partition_cells(
305 &self,
306 mesh: &M,
307 cells: &[PointId],
308 n_parts: usize,
309 ) -> Result<Vec<usize>, MeshSieveError>;
310}
311
312pub struct ProvidedPartition<'a> {
314 pub parts: &'a [usize],
315}
316
317impl<M> CellPartitioner<M> for ProvidedPartition<'_>
318where
319 M: Sieve<Point = PointId, Payload = ()>,
320{
321 fn partition_cells(
322 &self,
323 _mesh: &M,
324 cells: &[PointId],
325 _n_parts: usize,
326 ) -> Result<Vec<usize>, MeshSieveError> {
327 if self.parts.len() != cells.len() {
328 return Err(MeshSieveError::PartitionIndexOutOfBounds(self.parts.len()));
329 }
330 Ok(self.parts.to_vec())
331 }
332}
333
334pub struct CustomPartitioner<F>(pub F);
336
337impl<M, F> CellPartitioner<M> for CustomPartitioner<F>
338where
339 M: Sieve<Point = PointId, Payload = ()>,
340 F: Fn(&M, &[PointId], usize) -> Result<Vec<usize>, MeshSieveError>,
341{
342 fn partition_cells(
343 &self,
344 mesh: &M,
345 cells: &[PointId],
346 n_parts: usize,
347 ) -> Result<Vec<usize>, MeshSieveError> {
348 (self.0)(mesh, cells, n_parts)
349 }
350}
351
352#[cfg(feature = "metis-support")]
354pub struct MetisPartitioner;
355
356#[cfg(feature = "metis-support")]
357impl<M> CellPartitioner<M> for MetisPartitioner
358where
359 M: Sieve<Point = PointId, Payload = ()>,
360{
361 fn partition_cells(
362 &self,
363 mesh: &M,
364 cells: &[PointId],
365 n_parts: usize,
366 ) -> Result<Vec<usize>, MeshSieveError> {
367 let dual = crate::algs::dual_graph::build_dual(mesh, cells.to_vec());
368 let partition = dual.metis_partition(
369 n_parts
370 .try_into()
371 .map_err(|_| MeshSieveError::PartitionIndexOutOfBounds(n_parts))?,
372 );
373 Ok(partition.part.into_iter().map(|p| p as usize).collect())
374 }
375}
376
377pub fn distribute_with_overlap<M, V, St, CtSt, C, P>(
394 mesh_data: &MeshData<M, V, St, CtSt>,
395 cells: &[PointId],
396 partitioner: &P,
397 config: DistributionConfig,
398 comm: &C,
399) -> Result<DistributedMeshData<V, St, CtSt>, MeshSieveError>
400where
401 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
402 V: Clone + Default + Send + PartialEq + bytemuck::Pod + 'static,
403 St: Storage<V> + Clone,
404 CtSt: Storage<CellType> + Clone,
405 C: Communicator + Sync,
406 P: CellPartitioner<M>,
407{
408 distribute_with_overlap_periodic(mesh_data, cells, partitioner, config, comm, None)
409}
410
411pub fn distribute_with_overlap_periodic<M, V, St, CtSt, C, P>(
417 mesh_data: &MeshData<M, V, St, CtSt>,
418 cells: &[PointId],
419 partitioner: &P,
420 config: DistributionConfig,
421 comm: &C,
422 periodic: Option<&PointEquivalence>,
423) -> Result<DistributedMeshData<V, St, CtSt>, MeshSieveError>
424where
425 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
426 V: Clone + Default + Send + PartialEq + bytemuck::Pod + 'static,
427 St: Storage<V> + Clone,
428 CtSt: Storage<CellType> + Clone,
429 C: Communicator + Sync,
430 P: CellPartitioner<M>,
431{
432 let my_rank = comm.rank();
433 let n_ranks = comm.size().max(1);
434 let cell_parts = partitioner.partition_cells(&mesh_data.sieve, cells, n_ranks)?;
435 if cell_parts.len() != cells.len() {
436 return Err(MeshSieveError::PartitionIndexOutOfBounds(cell_parts.len()));
437 }
438 if cell_parts.iter().any(|&p| p >= n_ranks) {
439 let bad = cell_parts.iter().copied().max().unwrap_or(0);
440 return Err(MeshSieveError::PartitionIndexOutOfBounds(bad));
441 }
442
443 let points: Vec<PointId> = mesh_data.sieve.points().collect();
444 let max_id = points.iter().map(|p| p.get()).max().unwrap_or(0) as usize;
445 let mut point_owners = assign_point_owners(mesh_data, cells, &cell_parts, max_id)?;
446 if config.balance_boundary_ownership {
447 let sharing = build_point_sharing(mesh_data, cells, &cell_parts)?;
448 balance_partition_boundary_ownership(&mut point_owners, &sharing, n_ranks)?;
449 }
450
451 let periodic_classes = periodic
452 .map(|eq| build_periodic_classes(&points, eq))
453 .unwrap_or_default();
454 let periodic_remote_map =
455 build_periodic_remote_map(&periodic_classes, &point_owners, point_owners.len());
456
457 let adjacency = build_adjacency(&mesh_data.sieve, max_id, &periodic_classes);
458 let use_comm_completion = should_use_comm_completion(comm);
459
460 let mut owned_set = BTreeSet::new();
461 for &p in &points {
462 let idx = (p.get() - 1) as usize;
463 if point_owners.get(idx).copied().unwrap_or(0) == my_rank {
464 owned_set.insert(p);
465 }
466 }
467
468 let mut overlap = Overlap::default();
469 let mut frontier: BTreeMap<usize, BTreeSet<PointId>> = BTreeMap::new();
470 if config.overlap_depth > 0 {
471 for &p in &owned_set {
472 let p_idx = (p.get() - 1) as usize;
473 for q in adjacency.get(p_idx).into_iter().flat_map(|s| s.iter()) {
474 let q_idx = (q.get() - 1) as usize;
475 let owner_q = point_owners.get(q_idx).copied().unwrap_or(0);
476 if owner_q != my_rank {
477 overlap.try_add_link_structural_one(p, owner_q)?;
478 frontier.entry(owner_q).or_default().insert(p);
479 }
480 }
481 }
482
483 ensure_closure_of_support(&mut overlap, &mesh_data.sieve);
484
485 for _layer in 0..config.overlap_depth {
486 if frontier.is_empty() {
487 break;
488 }
489 let mut next_frontier: BTreeMap<usize, BTreeSet<PointId>> = BTreeMap::new();
490 for (&nbr, seeds) in &frontier {
491 if seeds.is_empty() {
492 continue;
493 }
494 let before: BTreeSet<_> = overlap.links_to(nbr).map(|(p, _)| p).collect();
495 expand_one_layer_mesh(&mut overlap, &mesh_data.sieve, seeds.iter().copied(), nbr);
496 let after: BTreeSet<_> = overlap.links_to(nbr).map(|(p, _)| p).collect();
497 let added: BTreeSet<_> = after.difference(&before).copied().collect();
498 if !added.is_empty() {
499 next_frontier.insert(nbr, added);
500 }
501 }
502 frontier = next_frontier;
503 }
504 }
505
506 resolve_overlap_via_exchange(
507 &mut overlap,
508 comm,
509 my_rank,
510 Some(&periodic_remote_map),
511 use_comm_completion,
512 )?;
513 ensure_periodic_remote_links(&mut overlap)?;
514
515 let mut local_set = owned_set.clone();
516 for nbr in overlap.neighbor_ranks() {
517 for (p, remote) in overlap.links_to(nbr) {
518 local_set.insert(p);
519 if let Some(remote_point) = remote {
520 local_set.insert(remote_point);
521 }
522 }
523 }
524
525 let local_sieve = if use_comm_completion {
526 let mut local = build_local_sieve(mesh_data, &local_set)?;
527 complete_sieve(&mut local, &overlap, comm, my_rank)?;
528 local
529 } else {
530 build_local_sieve(mesh_data, &local_set)?
531 };
532
533 let local_points: BTreeSet<_> = local_sieve.points().collect();
534 let owned_points: BTreeSet<_> = local_points
535 .iter()
536 .copied()
537 .filter(|p| {
538 let idx = (p.get() - 1) as usize;
539 point_owners.get(idx).copied().unwrap_or(0) == my_rank
540 })
541 .collect();
542
543 let ownership =
544 PointOwnership::from_local_set(local_points.iter().copied(), &point_owners, my_rank)?;
545
546 debug_validate_overlap_ownership_topology(&local_sieve, &ownership, Some(&overlap), my_rank)?;
547
548 let section_points = if config.synchronize_sections {
549 &local_points
550 } else {
551 &owned_points
552 };
553 let copy_all_sections = config.synchronize_sections && !use_comm_completion;
554
555 let labels = mesh_data
556 .labels
557 .as_ref()
558 .map(|l| l.filtered_to_points(section_points.iter().copied()));
559
560 let coordinates = match &mesh_data.coordinates {
561 Some(coords) => {
562 let mut section = if copy_all_sections {
563 build_local_section_full(coords.section(), section_points)?
564 } else {
565 build_local_section_owned(coords.section(), section_points, &owned_points)?
566 };
567 if config.synchronize_sections && use_comm_completion {
568 complete_section_with_ownership::<V, St, CopyDelta, C>(
569 &mut section,
570 &overlap,
571 &ownership,
572 comm,
573 my_rank,
574 )?;
575 }
576 let mut out = Coordinates::from_section(
577 coords.topological_dimension(),
578 coords.embedding_dimension(),
579 section,
580 )?;
581 if let Some(high_order) = coords.high_order() {
582 let mut ho_section = if copy_all_sections {
583 build_local_section_full(high_order.section(), section_points)?
584 } else {
585 build_local_section_owned(high_order.section(), section_points, &owned_points)?
586 };
587 if config.synchronize_sections && use_comm_completion {
588 complete_section_with_ownership::<V, St, CopyDelta, C>(
589 &mut ho_section,
590 &overlap,
591 &ownership,
592 comm,
593 my_rank,
594 )?;
595 }
596 let ho = HighOrderCoordinates::from_section(high_order.dimension(), ho_section)?;
597 out.set_high_order(ho)?;
598 }
599 Some(out)
600 }
601 None => None,
602 };
603
604 let mut sections = BTreeMap::new();
605 for (name, section) in &mesh_data.sections {
606 let mut local_section = if copy_all_sections {
607 build_local_section_full(section, section_points)?
608 } else {
609 build_local_section_owned(section, section_points, &owned_points)?
610 };
611 if config.synchronize_sections && use_comm_completion {
612 complete_section_with_ownership::<V, St, CopyDelta, C>(
613 &mut local_section,
614 &overlap,
615 &ownership,
616 comm,
617 my_rank,
618 )?;
619 }
620 sections.insert(name.clone(), local_section);
621 }
622
623 let mut mixed_sections = MixedSectionStore::default();
624 for (name, section) in mesh_data.mixed_sections.iter() {
625 let mut local_section = if copy_all_sections {
626 build_local_tagged_section(section, section_points, section_points)?
627 } else {
628 build_local_tagged_section(section, section_points, &owned_points)?
629 };
630 if config.synchronize_sections && use_comm_completion {
631 complete_tagged_section_with_ownership(
632 &mut local_section,
633 &overlap,
634 &ownership,
635 comm,
636 my_rank,
637 )?;
638 }
639 mixed_sections.insert_tagged(name.clone(), local_section);
640 }
641
642 let cell_types = match &mesh_data.cell_types {
643 Some(section) => {
644 let local_section = if config.synchronize_sections {
645 build_local_section_full(section, section_points)?
646 } else {
647 build_local_section_owned(section, section_points, &owned_points)?
648 };
649 Some(local_section)
650 }
651 None => None,
652 };
653
654 Ok(DistributedMeshData {
655 sieve: local_sieve,
656 overlap,
657 point_owners,
658 ownership,
659 cell_parts,
660 coordinates,
661 sections,
662 mixed_sections,
663 labels,
664 cell_types,
665 discretization: mesh_data.discretization.clone(),
666 })
667}
668
669fn assign_point_owners<M, V, St, CtSt>(
670 mesh_data: &MeshData<M, V, St, CtSt>,
671 cells: &[PointId],
672 cell_parts: &[usize],
673 max_id: usize,
674) -> Result<Vec<usize>, MeshSieveError>
675where
676 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
677 St: Storage<V> + Clone,
678 CtSt: Storage<CellType> + Clone,
679{
680 let mut owners = vec![0usize; max_id];
681 let mut seen = vec![false; max_id];
682 for (cell, &part) in cells.iter().zip(cell_parts.iter()) {
683 for (p, _) in mesh_data.sieve.closure_o(std::iter::once(*cell)) {
684 let idx = p
685 .get()
686 .checked_sub(1)
687 .ok_or(MeshSieveError::PartitionIndexOutOfBounds(p.get() as usize))?
688 as usize;
689 if idx >= owners.len() {
690 return Err(MeshSieveError::PartitionIndexOutOfBounds(idx));
691 }
692 if !seen[idx] || part < owners[idx] {
693 owners[idx] = part;
694 seen[idx] = true;
695 }
696 }
697 }
698 Ok(owners)
699}
700
701fn build_point_sharing<M, V, St, CtSt>(
702 mesh_data: &MeshData<M, V, St, CtSt>,
703 cells: &[PointId],
704 cell_parts: &[usize],
705) -> Result<BTreeMap<PointId, BTreeSet<usize>>, MeshSieveError>
706where
707 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
708 St: Storage<V> + Clone,
709 CtSt: Storage<CellType> + Clone,
710{
711 let mut sharing: BTreeMap<PointId, BTreeSet<usize>> = BTreeMap::new();
712 for (cell, &part) in cells.iter().zip(cell_parts.iter()) {
713 for (p, _) in mesh_data.sieve.closure_o(std::iter::once(*cell)) {
714 sharing.entry(p).or_default().insert(part);
715 }
716 }
717 Ok(sharing)
718}
719
720fn build_adjacency<M>(
721 mesh: &M,
722 max_id: usize,
723 periodic_classes: &BTreeMap<PointId, Vec<PointId>>,
724) -> Vec<BTreeSet<PointId>>
725where
726 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
727{
728 let mut adjacency = vec![BTreeSet::new(); max_id];
729 for src in mesh.points() {
730 let src_idx = (src.get() - 1) as usize;
731 for (dst, _) in mesh.cone_o(src) {
732 let dst_idx = (dst.get() - 1) as usize;
733 if src_idx < max_id {
734 adjacency[src_idx].insert(dst);
735 }
736 if dst_idx < max_id {
737 adjacency[dst_idx].insert(src);
738 }
739 }
740 }
741 apply_periodic_adjacency(&mut adjacency, periodic_classes, max_id);
742 adjacency
743}
744
745fn build_periodic_classes(
746 points: &[PointId],
747 periodic: &PointEquivalence,
748) -> BTreeMap<PointId, Vec<PointId>> {
749 let mut eq = periodic.clone();
750 eq.classes(points.iter().copied())
751}
752
753fn apply_periodic_adjacency(
754 adjacency: &mut [BTreeSet<PointId>],
755 periodic_classes: &BTreeMap<PointId, Vec<PointId>>,
756 max_id: usize,
757) {
758 for class in periodic_classes.values() {
759 if class.len() < 2 {
760 continue;
761 }
762 for i in 0..class.len() {
763 for j in (i + 1)..class.len() {
764 let a = class[i];
765 let b = class[j];
766 let a_idx = (a.get().saturating_sub(1)) as usize;
767 let b_idx = (b.get().saturating_sub(1)) as usize;
768 if a_idx < max_id {
769 adjacency[a_idx].insert(b);
770 }
771 if b_idx < max_id {
772 adjacency[b_idx].insert(a);
773 }
774 }
775 }
776 }
777}
778
779fn build_periodic_remote_map(
780 periodic_classes: &BTreeMap<PointId, Vec<PointId>>,
781 owners: &[usize],
782 max_id: usize,
783) -> BTreeMap<(PointId, usize), PointId> {
784 let mut remote_map = BTreeMap::new();
785 for class in periodic_classes.values() {
786 if class.len() < 2 {
787 continue;
788 }
789 let mut rank_to_point: BTreeMap<usize, PointId> = BTreeMap::new();
790 for &p in class {
791 let idx = (p.get().saturating_sub(1)) as usize;
792 if idx >= max_id {
793 continue;
794 }
795 let owner = owners.get(idx).copied().unwrap_or(0);
796 rank_to_point
797 .entry(owner)
798 .and_modify(|existing| {
799 if p < *existing {
800 *existing = p;
801 }
802 })
803 .or_insert(p);
804 }
805 for &p in class {
806 let idx = (p.get().saturating_sub(1)) as usize;
807 if idx >= max_id {
808 continue;
809 }
810 let owner = owners.get(idx).copied().unwrap_or(0);
811 for (&rank, &remote_point) in &rank_to_point {
812 if rank != owner {
813 remote_map.insert((p, rank), remote_point);
814 }
815 }
816 }
817 }
818 remote_map
819}
820
821fn ensure_periodic_remote_links(overlap: &mut Overlap) -> Result<(), MeshSieveError> {
822 let mut extras: Vec<(PointId, usize)> = Vec::new();
823 for nbr in overlap.neighbor_ranks() {
824 for (local, remote) in overlap.links_to(nbr) {
825 if let Some(remote_point) = remote
826 && remote_point != local
827 {
828 extras.push((remote_point, nbr));
829 }
830 }
831 }
832 extras.sort_unstable();
833 extras.dedup();
834 for (local, nbr) in extras {
835 let existing = overlap
842 .links_to(nbr)
843 .find(|(point, _)| *point == local)
844 .map(|(_, remote)| remote);
845 match existing {
846 Some(Some(_)) => continue,
847 Some(None) => overlap.resolve_remote_point(local, nbr, local)?,
848 None => {
849 overlap.try_add_link_structural_one(local, nbr)?;
850 overlap.resolve_remote_point(local, nbr, local)?;
851 }
852 }
853 }
854 Ok(())
855}
856
857fn build_local_sieve<M, V, St, CtSt>(
858 mesh_data: &MeshData<M, V, St, CtSt>,
859 local_set: &BTreeSet<PointId>,
860) -> Result<MeshSieve, MeshSieveError>
861where
862 M: OrientedSieve<Point = PointId, Payload = (), Orient = i32>,
863 St: Storage<V> + Clone,
864 CtSt: Storage<CellType> + Clone,
865{
866 let mut local = MeshSieve::default();
867 for &point in local_set {
868 local.add_point(point);
869 }
870 let points: Vec<PointId> = mesh_data.sieve.points().collect();
871 for src in &points {
872 if !local_set.contains(src) {
873 continue;
874 }
875 for (dst, orient) in mesh_data.sieve.cone_o(*src) {
876 if local_set.contains(&dst) {
877 local.add_arrow_o(*src, dst, (), orient)?;
878 }
879 }
880 }
881 Ok(local)
882}
883
884fn build_local_section_owned<V, St>(
885 section: &Section<V, St>,
886 local_points: &BTreeSet<PointId>,
887 owned_points: &BTreeSet<PointId>,
888) -> Result<Section<V, St>, MeshSieveError>
889where
890 V: Clone + Default,
891 St: Storage<V> + Clone,
892{
893 let mut atlas = Atlas::default();
894 for p in local_points {
895 if let Some((_off, len)) = section.atlas().get(*p) {
896 atlas.try_insert(*p, len)?;
897 }
898 }
899 let mut local_section = Section::<V, St>::new(atlas);
900 for p in owned_points {
901 if local_section.atlas().get(*p).is_none() {
902 continue;
903 }
904 let data = section.try_restrict(*p)?;
905 local_section.try_set(*p, data)?;
906 }
907 Ok(local_section)
908}
909
910fn build_local_section_full<V, St>(
911 section: &Section<V, St>,
912 local_points: &BTreeSet<PointId>,
913) -> Result<Section<V, St>, MeshSieveError>
914where
915 V: Clone + Default,
916 St: Storage<V> + Clone,
917{
918 build_local_section_owned(section, local_points, local_points)
919}
920
921fn build_local_tagged_section(
922 section: &TaggedSection,
923 local_points: &BTreeSet<PointId>,
924 owned_points: &BTreeSet<PointId>,
925) -> Result<TaggedSection, MeshSieveError> {
926 Ok(match section {
927 TaggedSection::F64(sec) => {
928 TaggedSection::F64(build_local_section_owned(sec, local_points, owned_points)?)
929 }
930 TaggedSection::F32(sec) => {
931 TaggedSection::F32(build_local_section_owned(sec, local_points, owned_points)?)
932 }
933 TaggedSection::I32(sec) => {
934 TaggedSection::I32(build_local_section_owned(sec, local_points, owned_points)?)
935 }
936 TaggedSection::I64(sec) => {
937 TaggedSection::I64(build_local_section_owned(sec, local_points, owned_points)?)
938 }
939 TaggedSection::U32(sec) => {
940 TaggedSection::U32(build_local_section_owned(sec, local_points, owned_points)?)
941 }
942 TaggedSection::U64(sec) => {
943 TaggedSection::U64(build_local_section_owned(sec, local_points, owned_points)?)
944 }
945 })
946}
947
948fn complete_tagged_section_with_ownership<C>(
949 section: &mut TaggedSection,
950 overlap: &Overlap,
951 ownership: &PointOwnership,
952 comm: &C,
953 my_rank: usize,
954) -> Result<(), MeshSieveError>
955where
956 C: Communicator + Sync,
957{
958 match section {
959 TaggedSection::F64(sec) => complete_section_with_ownership::<f64, _, CopyDelta, C>(
960 sec, overlap, ownership, comm, my_rank,
961 ),
962 TaggedSection::F32(sec) => complete_section_with_ownership::<f32, _, CopyDelta, C>(
963 sec, overlap, ownership, comm, my_rank,
964 ),
965 TaggedSection::I32(sec) => complete_section_with_ownership::<i32, _, CopyDelta, C>(
966 sec, overlap, ownership, comm, my_rank,
967 ),
968 TaggedSection::I64(sec) => complete_section_with_ownership::<i64, _, CopyDelta, C>(
969 sec, overlap, ownership, comm, my_rank,
970 ),
971 TaggedSection::U32(sec) => complete_section_with_ownership::<u32, _, CopyDelta, C>(
972 sec, overlap, ownership, comm, my_rank,
973 ),
974 TaggedSection::U64(sec) => complete_section_with_ownership::<u64, _, CopyDelta, C>(
975 sec, overlap, ownership, comm, my_rank,
976 ),
977 }
978}
979
980fn complete_labels_with_ownership<C>(
981 labels: &mut LabelSet,
982 overlap: &Overlap,
983 ownership: &PointOwnership,
984 comm: &C,
985 my_rank: usize,
986) -> Result<(), MeshSieveError>
987where
988 C: Communicator + Sync,
989{
990 #[cfg(any(
991 debug_assertions,
992 feature = "strict-invariants",
993 feature = "check-invariants"
994 ))]
995 overlap.validate_invariants()?;
996
997 let mut nb: BTreeSet<usize> = overlap.neighbor_ranks().collect();
998 nb.remove(&my_rank);
999 if nb.is_empty() {
1000 return Ok(());
1001 }
1002
1003 labels.clear_points(ownership.ghost_points());
1004
1005 let mut send_payloads: HashMap<usize, Vec<u8>> = HashMap::new();
1006 for (name, point, value) in labels.iter() {
1007 let owner = ownership.owner_or_err(point)?;
1008 if owner != my_rank {
1009 continue;
1010 }
1011 let name_len: u16 = name
1012 .len()
1013 .try_into()
1014 .map_err(|_| MeshSieveError::CommError {
1015 neighbor: my_rank,
1016 source: format!("label name too long: {name}").into(),
1017 })?;
1018 for (_dst, rem) in overlap.cone(crate::overlap::overlap::local(point)) {
1019 if rem.rank == my_rank {
1020 continue;
1021 }
1022 let remote_pt = rem
1023 .remote_point
1024 .ok_or(MeshSieveError::OverlapLinkMissing(point, rem.rank))?;
1025 let payload = send_payloads.entry(rem.rank).or_default();
1026 payload.extend_from_slice(&remote_pt.get().to_le_bytes());
1027 payload.extend_from_slice(&value.to_le_bytes());
1028 payload.extend_from_slice(&name_len.to_le_bytes());
1029 payload.extend_from_slice(name.as_bytes());
1030 }
1031 }
1032
1033 let neighbors: HashSet<usize> = nb.iter().copied().collect();
1034 let tag = CommTag::new(0xD1A5);
1035 let counts = crate::algs::completion::size_exchange::exchange_sizes_symmetric::<_, u8>(
1036 &send_payloads,
1037 comm,
1038 tag,
1039 &neighbors,
1040 )?;
1041
1042 let mut recvs = Vec::new();
1043 for &nbr in &nb {
1044 let n = counts.get(&nbr).copied().unwrap_or(0) as usize;
1045 let mut buf = vec![0u8; n];
1046 let h = comm.irecv_result(nbr, tag.offset(1).as_u16(), &mut buf)?;
1047 recvs.push((nbr, h, buf));
1048 }
1049
1050 let mut sends = Vec::new();
1051 for &nbr in &nb {
1052 let out = send_payloads.get(&nbr).map_or(&[][..], |v| &v[..]);
1053 sends.push(comm.isend_result(nbr, tag.offset(1).as_u16(), out)?);
1054 }
1055
1056 let mut maybe_err: Option<MeshSieveError> = None;
1057 for (nbr, h, mut buf) in recvs {
1058 match h.wait() {
1059 Some(raw) if raw.len() == buf.len() => {
1060 buf.copy_from_slice(&raw);
1061 if let Err(err) = decode_label_payload(&buf, nbr).map(|entries| {
1062 for (point, name, value) in entries {
1063 labels.set_label(point, &name, value);
1064 }
1065 }) && maybe_err.is_none()
1066 {
1067 maybe_err = Some(err);
1068 }
1069 }
1070 Some(raw) if maybe_err.is_none() => {
1071 maybe_err = Some(MeshSieveError::CommError {
1072 neighbor: nbr,
1073 source: format!(
1074 "label payload size mismatch: expected {}B, got {}B",
1075 buf.len(),
1076 raw.len()
1077 )
1078 .into(),
1079 });
1080 }
1081 None if maybe_err.is_none() => {
1082 maybe_err = Some(MeshSieveError::CommError {
1083 neighbor: nbr,
1084 source: format!("failed to receive label payload from rank {nbr}").into(),
1085 });
1086 }
1087 _ => {}
1088 }
1089 }
1090
1091 for send in sends {
1092 let _ = send.wait();
1093 }
1094
1095 if let Some(err) = maybe_err {
1096 Err(err)
1097 } else {
1098 Ok(())
1099 }
1100}
1101
1102fn decode_label_payload(
1103 buf: &[u8],
1104 neighbor: usize,
1105) -> Result<Vec<(PointId, String, i32)>, MeshSieveError> {
1106 let mut out = Vec::new();
1107 let mut idx = 0usize;
1108 while idx < buf.len() {
1109 let remaining = buf.len() - idx;
1110 if remaining < (8 + 4 + 2) {
1111 return Err(MeshSieveError::CommError {
1112 neighbor,
1113 source: "label payload truncated header".into(),
1114 });
1115 }
1116 let mut raw_id = [0u8; 8];
1117 raw_id.copy_from_slice(&buf[idx..idx + 8]);
1118 idx += 8;
1119 let mut raw_val = [0u8; 4];
1120 raw_val.copy_from_slice(&buf[idx..idx + 4]);
1121 idx += 4;
1122 let mut raw_len = [0u8; 2];
1123 raw_len.copy_from_slice(&buf[idx..idx + 2]);
1124 idx += 2;
1125 let name_len = u16::from_le_bytes(raw_len) as usize;
1126 if idx + name_len > buf.len() {
1127 return Err(MeshSieveError::CommError {
1128 neighbor,
1129 source: "label payload truncated name".into(),
1130 });
1131 }
1132 let name_bytes = &buf[idx..idx + name_len];
1133 idx += name_len;
1134 let name = std::str::from_utf8(name_bytes).map_err(|e| MeshSieveError::CommError {
1135 neighbor,
1136 source: format!("label name invalid utf8: {e}").into(),
1137 })?;
1138 let point = PointId::new(u64::from_le_bytes(raw_id))?;
1139 let value = i32::from_le_bytes(raw_val);
1140 out.push((point, name.to_string(), value));
1141 }
1142 Ok(out)
1143}
1144
1145fn resolve_overlap_via_exchange<C>(
1146 overlap: &mut Overlap,
1147 comm: &C,
1148 my_rank: usize,
1149 periodic_remote_map: Option<&BTreeMap<(PointId, usize), PointId>>,
1150 use_comm_completion: bool,
1151) -> Result<(), MeshSieveError>
1152where
1153 C: Communicator + Sync,
1154{
1155 #[cfg(any(
1156 debug_assertions,
1157 feature = "strict-invariants",
1158 feature = "check-invariants"
1159 ))]
1160 overlap.validate_invariants()?;
1161
1162 let mut nb: BTreeSet<usize> = overlap.neighbor_ranks().collect();
1163 nb.remove(&my_rank);
1164 if nb.is_empty() {
1165 return Ok(());
1166 }
1167
1168 let neighbors: Vec<usize> = nb.into_iter().collect();
1169 if !use_comm_completion || comm.is_no_comm() || comm.size() <= 1 {
1170 for &nbr in &neighbors {
1171 let points: Vec<PointId> = overlap.links_to(nbr).map(|(p, _)| p).collect();
1172 for p in points {
1173 let remote = periodic_remote_map
1174 .and_then(|map| map.get(&(p, nbr)).copied())
1175 .unwrap_or(p);
1176 overlap.resolve_remote_point(p, nbr, remote)?;
1177 }
1178 }
1179 return Ok(());
1180 }
1181
1182 let mut send_points: HashMap<usize, Vec<WirePointRepr>> = HashMap::new();
1183 let mut local_points: HashMap<usize, Vec<PointId>> = HashMap::new();
1184 for &nbr in &neighbors {
1185 let mut pts: Vec<PointId> = overlap.links_to(nbr).map(|(p, _)| p).collect();
1186 pts.sort_unstable();
1187 pts.dedup();
1188 let send: Vec<WirePointRepr> = pts.iter().map(|p| WirePointRepr::of(p.get())).collect();
1189 send_points.insert(nbr, send);
1190 local_points.insert(nbr, pts);
1191 }
1192
1193 let all_neighbors: HashSet<usize> = neighbors.iter().copied().collect();
1194 let tag = CommTag::new(0xD00D);
1195 let counts = crate::algs::completion::size_exchange::exchange_sizes_symmetric(
1196 &send_points,
1197 comm,
1198 tag,
1199 &all_neighbors,
1200 )?;
1201
1202 let mut recvs = Vec::new();
1203 for &nbr in &neighbors {
1204 let n = counts.get(&nbr).copied().unwrap_or(0) as usize;
1205 let mut buf = vec![WirePointRepr::zeroed(); n];
1206 let h = comm.irecv_result(nbr, tag.offset(1).as_u16(), cast_slice_mut(&mut buf))?;
1207 recvs.push((nbr, h, buf));
1208 }
1209
1210 let mut sends = Vec::new();
1211 for &nbr in &neighbors {
1212 let out = send_points.get(&nbr).map_or(&[][..], |v| &v[..]);
1213 sends.push(comm.isend_result(nbr, tag.offset(1).as_u16(), cast_slice(out))?);
1214 }
1215
1216 let mut recv_sets: HashMap<usize, HashSet<u64>> = HashMap::new();
1217 let mut maybe_err: Option<MeshSieveError> = None;
1218 for (nbr, h, mut buf) in recvs {
1219 match h.wait() {
1220 Some(raw) if raw.len() == buf.len() * std::mem::size_of::<WirePointRepr>() => {
1221 cast_slice_mut(&mut buf).copy_from_slice(&raw);
1222 let set: HashSet<u64> = buf.iter().map(|p| p.get()).collect();
1223 recv_sets.insert(nbr, set);
1224 }
1225 Some(raw) if maybe_err.is_none() => {
1226 let exp = buf.len() * std::mem::size_of::<WirePointRepr>();
1227 maybe_err = Some(MeshSieveError::CommError {
1228 neighbor: nbr,
1229 source: format!("payload size mismatch: expected {exp}B, got {}B", raw.len())
1230 .into(),
1231 });
1232 }
1233 None if maybe_err.is_none() => {
1234 maybe_err = Some(MeshSieveError::CommError {
1235 neighbor: nbr,
1236 source: "recv returned None".into(),
1237 });
1238 }
1239 _ => {}
1240 }
1241 }
1242
1243 for h in sends {
1244 let _ = h.wait();
1245 }
1246
1247 if let Some(err) = maybe_err {
1248 return Err(err);
1249 }
1250
1251 let mut to_remove: Vec<(PointId, usize)> = Vec::new();
1253 for (&nbr, points) in &local_points {
1254 let recv_set = recv_sets.get(&nbr).ok_or(MeshSieveError::MissingOverlap {
1255 source: format!("missing neighbor list from rank {nbr}").into(),
1256 })?;
1257 for &p in points {
1258 let remote = periodic_remote_map
1259 .and_then(|map| map.get(&(p, nbr)).copied())
1260 .unwrap_or(p);
1261 if !recv_set.contains(&remote.get()) {
1262 to_remove.push((p, nbr));
1263 continue;
1264 }
1265 overlap.resolve_remote_point(p, nbr, remote)?;
1266 }
1267 }
1268
1269 if !to_remove.is_empty() {
1270 for (p, nbr) in to_remove {
1271 let _ = overlap.remove_link(p, nbr);
1272 }
1273 overlap.prune_empty_parts();
1274 }
1275
1276 Ok(())
1277}
1278
1279fn should_use_comm_completion<C: Communicator + Sync + 'static>(comm: &C) -> bool {
1280 !comm.is_no_comm()
1281 && comm.size() > 1
1282 && std::any::TypeId::of::<C>()
1283 != std::any::TypeId::of::<crate::algs::communicator::RayonComm>()
1284}
1285
1286#[inline]
1287fn owner_of(parts: &[usize], p: PointId) -> Result<usize, MeshSieveError> {
1288 let idx = p
1289 .get()
1290 .checked_sub(1)
1291 .ok_or(MeshSieveError::PartitionIndexOutOfBounds(p.get() as usize))? as usize;
1292 parts
1293 .get(idx)
1294 .copied()
1295 .ok_or(MeshSieveError::PartitionIndexOutOfBounds(idx))
1296}