mpi_fork_fnsp/topology/mod.rs
1//! Organizing processes as groups and communicators
2//!
3//! Processes are organized in communicators. All parallel processes initially partaking in
4//! the computation are organized in a context called the 'world communicator' which is available
5//! as a property of the `Universe`. From the world communicator, other communicators can be
6//! created. Processes can be addressed via their `Rank` within a specific communicator. This
7//! information is encapsulated in a `Process`.
8//!
9//! # Unfinished features
10//!
11//! - **6.3**: Group management
12//! - **6.3.2**: Constructors, `MPI_Group_range_incl()`, `MPI_Group_range_excl()`
13//! - **6.4**: Communicator management
14//! - **6.4.2**: Constructors, `MPI_Comm_dup_with_info()`, `MPI_Comm_idup()`,
15//! `MPI_Comm_split_type()`
16//! - **6.4.4**: Info, `MPI_Comm_set_info()`, `MPI_Comm_get_info()`
17//! - **6.6**: Inter-communication
18//! - **6.7**: Caching
19//! - **6.8**: Naming objects
20//! - **7**: Process topologies
21//! - **Parts of sections**: 8, 10, 12
22use std::ffi::{CStr, CString};
23use std::mem::MaybeUninit;
24use std::os::raw::{c_char, c_int};
25use std::process;
26
27use conv::ConvUtil;
28
29#[cfg(not(msmpi))]
30use crate::Tag;
31use crate::{Count, IntArray};
32
33use crate::datatype::traits::{Buffer, BufferMut, Collection, Datatype};
34use crate::ffi;
35use crate::ffi::{MPI_Comm, MPI_Group};
36use crate::raw::traits::AsRaw;
37use crate::with_uninitialized;
38
39mod cartesian;
40
41/// Topology traits
42pub mod traits {
43 pub use super::{AsCommunicator, Communicator, Group};
44}
45
46// Re-export cartesian functions and types from topology modules.
47pub use self::cartesian::*;
48
49/// Something that has a communicator associated with it
50pub trait AsCommunicator {
51 /// The type of the associated communicator
52 type Out: Communicator;
53 /// Returns the associated communicator.
54 fn as_communicator(&self) -> &Self::Out;
55}
56
57/// Identifies a certain process within a communicator.
58pub type Rank = c_int;
59
60/// A built-in communicator, e.g. `MPI_COMM_WORLD`
61///
62/// # Standard section(s)
63///
64/// 6.4
65#[derive(Copy, Clone, Debug)]
66pub struct SystemCommunicator(MPI_Comm);
67
68impl SystemCommunicator {
69 /// The 'world communicator'
70 ///
71 /// Contains all processes initially partaking in the computation.
72 ///
73 /// # Examples
74 /// See `examples/simple.rs`
75 pub fn world() -> SystemCommunicator {
76 unsafe { SystemCommunicator::from_raw_unchecked(ffi::RSMPI_COMM_WORLD) }
77 }
78
79 /// If the raw value is the null handle returns `None`
80 #[allow(dead_code)]
81 fn from_raw(raw: MPI_Comm) -> Option<SystemCommunicator> {
82 if raw == unsafe { ffi::RSMPI_COMM_NULL } {
83 None
84 } else {
85 Some(SystemCommunicator(raw))
86 }
87 }
88
89 /// Wraps the raw value without checking for null handle
90 unsafe fn from_raw_unchecked(raw: MPI_Comm) -> SystemCommunicator {
91 debug_assert_ne!(raw, ffi::RSMPI_COMM_NULL);
92 SystemCommunicator(raw)
93 }
94}
95
96unsafe impl AsRaw for SystemCommunicator {
97 type Raw = MPI_Comm;
98 fn as_raw(&self) -> Self::Raw {
99 self.0
100 }
101}
102
103impl Communicator for SystemCommunicator {}
104
105impl AsCommunicator for SystemCommunicator {
106 type Out = SystemCommunicator;
107 fn as_communicator(&self) -> &Self::Out {
108 self
109 }
110}
111
112/// An enum describing the topology of a communicator
113#[allow(clippy::module_name_repetitions)]
114#[derive(Copy, Clone, PartialEq, Debug)]
115pub enum Topology {
116 /// Graph topology type
117 Graph,
118 /// Cartesian topology type
119 Cartesian,
120 /// DistributedGraph topology type
121 DistributedGraph,
122 /// Undefined topology type
123 Undefined,
124}
125
126/// An enum indirecting between different concrete communicator topology types
127#[allow(clippy::module_name_repetitions)]
128pub enum IntoTopology {
129 /// Graph topology type
130 Graph(GraphCommunicator),
131 /// Cartesian topology type
132 Cartesian(CartesianCommunicator),
133 /// DistributedGraph topology type
134 DistributedGraph(DistributedGraphCommunicator),
135 /// Undefined topology type
136 Undefined(UserCommunicator),
137}
138
139/// A user-defined communicator
140///
141/// # Standard section(s)
142///
143/// 6.4
144pub struct UserCommunicator(MPI_Comm);
145
146impl UserCommunicator {
147 /// If the raw value is the null handle returns `None`
148 ///
149 /// # Safety
150 /// - `raw` must be a live `MPI_Comm` object.
151 /// - `raw` must not be used after calling `from_raw`.
152 pub unsafe fn from_raw(raw: MPI_Comm) -> Option<UserCommunicator> {
153 if raw == ffi::RSMPI_COMM_NULL {
154 None
155 } else {
156 Some(UserCommunicator(raw))
157 }
158 }
159
160 /// Wraps the raw value without checking for null handle
161 ///
162 /// # Safety
163 /// - `raw` must be a live `MPI_Comm` object.
164 /// - `raw` must not be used after calling `from_raw_unchecked`.
165 /// - `raw` must not be `MPI_COMM_NULL`.
166 unsafe fn from_raw_unchecked(raw: MPI_Comm) -> UserCommunicator {
167 debug_assert_ne!(raw, ffi::RSMPI_COMM_NULL);
168 UserCommunicator(raw)
169 }
170
171 /// Gets the topology of the communicator.
172 ///
173 /// # Standard section(s)
174 /// 7.5.5
175 ///
176 /// # Panics
177 /// Unexpected Topology type
178 pub fn topology(&self) -> Topology {
179 unsafe {
180 let (_, topology) =
181 with_uninitialized(|topology| ffi::MPI_Topo_test(self.as_raw(), topology));
182
183 if topology == ffi::RSMPI_GRAPH {
184 Topology::Graph
185 } else if topology == ffi::RSMPI_CART {
186 Topology::Cartesian
187 } else if topology == ffi::RSMPI_DIST_GRAPH {
188 Topology::DistributedGraph
189 } else if topology == ffi::RSMPI_UNDEFINED {
190 Topology::Undefined
191 } else {
192 panic!("Unexpected Topology type!")
193 }
194 }
195 }
196
197 /// Converts the communicator into its precise communicator type.
198 ///
199 /// # Standard section(s)
200 /// 7.5.5
201 ///
202 /// # Panics
203 /// Unexpected Topology type
204 pub fn into_topology(self) -> IntoTopology {
205 match self.topology() {
206 Topology::Cartesian => IntoTopology::Cartesian(CartesianCommunicator(self)),
207 Topology::Undefined => IntoTopology::Undefined(self),
208 Topology::Graph | Topology::DistributedGraph => unimplemented!(),
209 }
210 }
211}
212
213impl AsCommunicator for UserCommunicator {
214 type Out = UserCommunicator;
215 fn as_communicator(&self) -> &Self::Out {
216 self
217 }
218}
219
220unsafe impl AsRaw for UserCommunicator {
221 type Raw = MPI_Comm;
222 fn as_raw(&self) -> Self::Raw {
223 self.0
224 }
225}
226
227impl Communicator for UserCommunicator {}
228
229impl Drop for UserCommunicator {
230 fn drop(&mut self) {
231 unsafe {
232 ffi::MPI_Comm_free(&mut self.0);
233 }
234 assert_eq!(self.0, unsafe { ffi::RSMPI_COMM_NULL });
235 }
236}
237
238impl From<CartesianCommunicator> for UserCommunicator {
239 fn from(cart_comm: CartesianCommunicator) -> Self {
240 cart_comm.0
241 }
242}
243
244/// Unimplemented
245#[allow(missing_copy_implementations)]
246pub struct GraphCommunicator;
247
248/// Unimplemented
249#[allow(missing_copy_implementations)]
250pub struct DistributedGraphCommunicator;
251
252/// A color used in a communicator split
253#[derive(Copy, Clone, Debug)]
254pub struct Color(c_int);
255
256impl Color {
257 /// Special color of undefined value
258 pub fn undefined() -> Color {
259 Color(unsafe { ffi::RSMPI_UNDEFINED })
260 }
261
262 /// A color of a certain value
263 ///
264 /// Valid values are non-negative.
265 ///
266 /// # Panics
267 /// Value of color must be non-negative.
268 pub fn with_value(value: c_int) -> Color {
269 if value < 0 {
270 panic!("Value of color must be non-negative.")
271 }
272 Color(value)
273 }
274
275 /// The raw value understood by the MPI C API
276 fn as_raw(self) -> c_int {
277 self.0
278 }
279}
280
281/// A key used when determining the rank order of processes after a communicator split.
282pub type Key = c_int;
283
284/// Communicators are contexts for communication
285pub trait Communicator: AsRaw<Raw = MPI_Comm> {
286 /// Number of processes in this communicator
287 ///
288 /// # Examples
289 /// See `examples/simple.rs`
290 ///
291 /// # Standard section(s)
292 ///
293 /// 6.4.1
294 fn size(&self) -> Rank {
295 unsafe { with_uninitialized(|size| ffi::MPI_Comm_size(self.as_raw(), size)).1 }
296 }
297
298 /// The `Rank` that identifies the calling process within this communicator
299 ///
300 /// # Examples
301 /// See `examples/simple.rs`
302 ///
303 /// # Standard section(s)
304 ///
305 /// 6.4.1
306 fn rank(&self) -> Rank {
307 unsafe { with_uninitialized(|rank| ffi::MPI_Comm_rank(self.as_raw(), rank)).1 }
308 }
309
310 /// Bundles a reference to this communicator with a specific `Rank` into a `Process`.
311 ///
312 /// # Examples
313 /// See `examples/broadcast.rs` `examples/gather.rs` `examples/send_receive.rs`
314 fn process_at_rank(&self, r: Rank) -> Process<Self>
315 where
316 Self: Sized,
317 {
318 assert!(0 <= r && r < self.size());
319 Process::by_rank_unchecked(self, r)
320 }
321
322 /// Returns an `AnyProcess` identifier that can be used, e.g. as a `Source` in point to point
323 /// communication.
324 fn any_process(&self) -> AnyProcess<Self>
325 where
326 Self: Sized,
327 {
328 AnyProcess(self)
329 }
330
331 /// A `Process` for the calling process
332 fn this_process(&self) -> Process<Self>
333 where
334 Self: Sized,
335 {
336 let rank = self.rank();
337 Process::by_rank_unchecked(self, rank)
338 }
339
340 /// Compare two communicators.
341 ///
342 /// See enum `CommunicatorRelation`.
343 ///
344 /// # Standard section(s)
345 ///
346 /// 6.4.1
347 fn compare<C: ?Sized>(&self, other: &C) -> CommunicatorRelation
348 where
349 C: Communicator,
350 {
351 unsafe {
352 with_uninitialized(|cmp| ffi::MPI_Comm_compare(self.as_raw(), other.as_raw(), cmp))
353 .1
354 .into()
355 }
356 }
357
358 /// Duplicate a communicator.
359 ///
360 /// # Examples
361 ///
362 /// See `examples/duplicate.rs`
363 ///
364 /// # Standard section(s)
365 ///
366 /// 6.4.2
367 fn duplicate(&self) -> UserCommunicator {
368 unsafe {
369 UserCommunicator::from_raw_unchecked(
370 with_uninitialized(|newcomm| ffi::MPI_Comm_dup(self.as_raw(), newcomm)).1,
371 )
372 }
373 }
374
375 /// Split a communicator by color.
376 ///
377 /// Creates as many new communicators as distinct values of `color` are given. All processes
378 /// with the same value of `color` join the same communicator. A process that passes the
379 /// special undefined color will not join a new communicator and `None` is returned.
380 ///
381 /// # Examples
382 ///
383 /// See `examples/split.rs`
384 ///
385 /// # Standard section(s)
386 ///
387 /// 6.4.2
388 fn split_by_color(&self, color: Color) -> Option<UserCommunicator> {
389 self.split_by_color_with_key(color, Key::default())
390 }
391
392 /// Split a communicator by color.
393 ///
394 /// Like `split()` but orders processes according to the value of `key` in the new
395 /// communicators.
396 ///
397 /// # Standard section(s)
398 ///
399 /// 6.4.2
400 fn split_by_color_with_key(&self, color: Color, key: Key) -> Option<UserCommunicator> {
401 unsafe {
402 UserCommunicator::from_raw(
403 with_uninitialized(|newcomm| {
404 ffi::MPI_Comm_split(self.as_raw(), color.as_raw(), key, newcomm)
405 })
406 .1,
407 )
408 }
409 }
410
411 /// Split the communicator into subcommunicators, each of which can create a shared memory
412 /// region.
413 ///
414 /// Within each subgroup, the processes are ranked in the order defined by the value of the
415 /// argument key, with ties broken according to their rank in the old group.
416 ///
417 /// # Standard section(s)
418 ///
419 /// 6.4.2 (See: `MPI_Comm_split_type`)
420 fn split_shared(&self, key: c_int) -> UserCommunicator {
421 unsafe {
422 UserCommunicator::from_raw(
423 with_uninitialized(|newcomm| {
424 ffi::MPI_Comm_split_type(
425 self.as_raw(),
426 ffi::RSMPI_COMM_TYPE_SHARED,
427 key,
428 ffi::RSMPI_INFO_NULL,
429 newcomm,
430 )
431 })
432 .1,
433 ).expect("rsmpi internal error: MPI implementation incorrectly returned MPI_COMM_NULL from MPI_Comm_split_type(..., MPI_COMM_TYPE_SHARED, ...)")
434 }
435 }
436
437 /// Split a communicator collectively by subgroup.
438 ///
439 /// Proceses pass in a group that is a subgroup of the group associated with the old
440 /// communicator. Different processes may pass in different groups, but if two groups are
441 /// different, they have to be disjunct. One new communicator is created for each distinct
442 /// group. The new communicator is returned if a process is a member of the group he passed in,
443 /// otherwise `None`.
444 ///
445 /// This call is a collective operation on the old communicator so all processes have to
446 /// partake.
447 ///
448 /// # Examples
449 ///
450 /// See `examples/split.rs`
451 ///
452 /// # Standard section(s)
453 ///
454 /// 6.4.2
455 fn split_by_subgroup_collective<G: ?Sized>(&self, group: &G) -> Option<UserCommunicator>
456 where
457 G: Group,
458 {
459 unsafe {
460 UserCommunicator::from_raw(
461 with_uninitialized(|newcomm| {
462 ffi::MPI_Comm_create(self.as_raw(), group.as_raw(), newcomm)
463 })
464 .1,
465 )
466 }
467 }
468
469 /// Split a communicator by subgroup.
470 ///
471 /// Like `split_by_subgroup_collective()` but not a collective operation.
472 ///
473 /// # Examples
474 ///
475 /// See `examples/split.rs`
476 ///
477 /// # Standard section(s)
478 ///
479 /// 6.4.2
480 #[cfg(not(msmpi))]
481 fn split_by_subgroup<G: ?Sized>(&self, group: &G) -> Option<UserCommunicator>
482 where
483 G: Group,
484 {
485 self.split_by_subgroup_with_tag(group, Tag::default())
486 }
487
488 /// Split a communicator by subgroup
489 ///
490 /// Like `split_by_subgroup()` but can avoid collision of concurrent calls
491 /// (i.e. multithreaded) by passing in distinct tags.
492 ///
493 /// # Standard section(s)
494 ///
495 /// 6.4.2
496 #[cfg(not(msmpi))]
497 fn split_by_subgroup_with_tag<G: ?Sized>(&self, group: &G, tag: Tag) -> Option<UserCommunicator>
498 where
499 G: Group,
500 {
501 unsafe {
502 UserCommunicator::from_raw(
503 with_uninitialized(|newcomm| {
504 ffi::MPI_Comm_create_group(self.as_raw(), group.as_raw(), tag, newcomm)
505 })
506 .1,
507 )
508 }
509 }
510
511 /// The group associated with this communicator
512 ///
513 /// # Standard section(s)
514 ///
515 /// 6.3.2
516 fn group(&self) -> UserGroup {
517 unsafe {
518 UserGroup(with_uninitialized(|group| ffi::MPI_Comm_group(self.as_raw(), group)).1)
519 }
520 }
521
522 /// Abort program execution
523 ///
524 /// # Standard section(s)
525 ///
526 /// 8.7
527 fn abort(&self, errorcode: c_int) -> ! {
528 unsafe {
529 ffi::MPI_Abort(self.as_raw(), errorcode);
530 }
531 process::abort();
532 }
533
534 /// Set the communicator name
535 ///
536 /// # Standard section(s)
537 ///
538 /// 6.8, see the `MPI_Comm_set_name` function
539 fn set_name(&self, name: &str) {
540 let c_name = CString::new(name).expect("Failed to convert the Rust string to a C string");
541 unsafe {
542 ffi::MPI_Comm_set_name(self.as_raw(), c_name.as_ptr());
543 }
544 }
545
546 /// Get the communicator name
547 ///
548 /// # Standard section(s)
549 ///
550 /// 6.8, see the `MPI_Comm_get_name` function
551 fn get_name(&self) -> String {
552 type BufType = [c_char; ffi::MPI_MAX_OBJECT_NAME as usize];
553
554 unsafe {
555 let mut buf = MaybeUninit::<BufType>::uninit();
556
557 let (_, _resultlen) = with_uninitialized(|resultlen| {
558 ffi::MPI_Comm_get_name(self.as_raw(), &mut (*buf.as_mut_ptr())[0], resultlen)
559 });
560
561 let buf_cstr = CStr::from_ptr(buf.assume_init().as_ptr());
562 buf_cstr.to_string_lossy().into_owned()
563 }
564 }
565
566 /// Creates a communicator with ranks laid out in a multi-dimensional space, allowing for easy
567 /// neighbor-to-neighbor communication, while providing MPI with information to allow it to
568 /// better optimize the physical locality of ranks that are logically close.
569 ///
570 /// * `dims` - array of spatial extents for the cartesian space
571 /// * `periods` - Must match length of `dims`. For `i` in 0 to `dims.len()`, `periods[i]` indicates if
572 /// axis `i` is periodic. i.e. if `true`, the element at `dims[i] - 1` in axis `i` is a neighbor of
573 /// element 0 in axis `i`
574 /// * `reorder` - If true, MPI may re-order ranks in the new communicator.
575 ///
576 /// # Standard section(s)
577 /// 7.5.1 [`MPI_Cart_create`]
578 fn create_cartesian_communicator(
579 &self,
580 dims: &[Count],
581 periods: &[bool],
582 reorder: bool,
583 ) -> Option<CartesianCommunicator> {
584 assert_eq!(
585 dims.len(),
586 periods.len(),
587 "dims and periods must be parallel, equal-sized arrays"
588 );
589
590 let periods: IntArray = periods.iter().map(|x| *x as i32).collect();
591
592 unsafe {
593 let mut comm_cart = ffi::RSMPI_COMM_NULL;
594 ffi::MPI_Cart_create(
595 self.as_raw(),
596 dims.count(),
597 dims.as_ptr(),
598 periods.as_ptr(),
599 reorder as Count,
600 &mut comm_cart,
601 );
602 CartesianCommunicator::from_raw(comm_cart)
603 }
604 }
605
606 /// Gets the target rank of this rank as-if
607 /// [`create_cartesian_communicator`](#method.create_cartesian_communicator) had been called
608 /// with `dims`, `periods`, and `reorder = true`.
609 ///
610 /// Returns `None` if the local process would not particate in the new `CartesianCommunciator`.
611 ///
612 /// * `dims` - array of spatial extents for the cartesian space
613 /// * `periods` - Must match length of `dims`. For `i` in 0 to `dims.len()`, `periods[i]` indicates if
614 /// axis `i` is periodic. i.e. if `true`, the element at `dims[i] - 1` in axis `i` is a neighbor of
615 /// element 0 in axis `i`
616 ///
617 /// # Standard section
618 /// 7.5.8 [`MPI_Cart_map`]
619 fn cartesian_map(&self, dims: &[Count], periods: &[bool]) -> Option<Rank> {
620 assert_eq!(
621 dims.len(),
622 periods.len(),
623 "dims and periods must be parallel, equal-sized arrays"
624 );
625
626 let periods: IntArray = periods.iter().map(|x| *x as i32).collect();
627
628 unsafe {
629 let mut new_rank = ffi::MPI_UNDEFINED;
630 ffi::MPI_Cart_map(
631 self.as_raw(),
632 dims.count(),
633 dims.as_ptr(),
634 periods.as_ptr(),
635 &mut new_rank,
636 );
637 if new_rank == ffi::MPI_UNDEFINED {
638 None
639 } else {
640 Some(new_rank)
641 }
642 }
643 }
644
645 /// Gets the implementation-defined buffer size required to pack 'incount' elements of type
646 /// 'datatype'.
647 ///
648 /// # Standard section(s)
649 ///
650 /// 4.2, see [`MPI_Pack_size`]
651 fn pack_size<Dt>(&self, incount: Count, datatype: &Dt) -> Count
652 where
653 Dt: Datatype,
654 {
655 unsafe {
656 with_uninitialized(|size| {
657 ffi::MPI_Pack_size(incount, datatype.as_raw(), self.as_raw(), size)
658 })
659 .1
660 }
661 }
662
663 /// Packs inbuf into a byte array with an implementation-defined format. Often paired with
664 /// `unpack` to convert back into a specific datatype.
665 ///
666 /// # Standard Sections
667 ///
668 /// 4.2, see [`MPI_Pack`]
669 fn pack<Buf>(&self, inbuf: &Buf) -> Vec<u8>
670 where
671 Buf: ?Sized + Buffer,
672 {
673 let inbuf_dt = inbuf.as_datatype();
674
675 let mut outbuf = vec![
676 0;
677 self.pack_size(inbuf.count(), &inbuf_dt)
678 .value_as::<usize>()
679 .expect("MPI_Pack_size returned a negative buffer size!")
680 ];
681
682 let position = self.pack_into(inbuf, &mut outbuf[..], 0);
683
684 outbuf.resize(
685 position
686 .value_as()
687 .expect("MPI_Pack returned a negative position!"),
688 0,
689 );
690
691 outbuf
692 }
693
694 /// Packs inbuf into a byte array with an implementation-defined format. Often paired with
695 /// `unpack` to convert back into a specific datatype.
696 ///
697 /// # Standard Sections
698 ///
699 /// 4.2, see [`MPI_Pack`]
700 fn pack_into<Buf>(&self, inbuf: &Buf, outbuf: &mut [u8], position: Count) -> Count
701 where
702 Buf: ?Sized + Buffer,
703 {
704 let inbuf_dt = inbuf.as_datatype();
705
706 let mut position: Count = position;
707 unsafe {
708 ffi::MPI_Pack(
709 inbuf.pointer(),
710 inbuf.count(),
711 inbuf_dt.as_raw(),
712 outbuf.as_mut_ptr().cast(),
713 outbuf.count(),
714 &mut position,
715 self.as_raw(),
716 );
717 }
718 position
719 }
720
721 /// Unpacks an implementation-specific byte array from `pack` or `pack_into` into a buffer of a
722 /// specific datatype.
723 ///
724 /// # Standard Sections
725 ///
726 /// 4.2, see [`MPI_Unpack`]
727 ///
728 /// # Safety
729 /// Unpacking must be succesfull
730 unsafe fn unpack_into<Buf>(&self, inbuf: &[u8], outbuf: &mut Buf, position: Count) -> Count
731 where
732 Buf: ?Sized + BufferMut,
733 {
734 let outbuf_dt = outbuf.as_datatype();
735
736 let mut position: Count = position;
737 ffi::MPI_Unpack(
738 inbuf.as_ptr().cast(),
739 inbuf.count(),
740 &mut position,
741 outbuf.pointer_mut(),
742 outbuf.count(),
743 outbuf_dt.as_raw(),
744 self.as_raw(),
745 );
746 position
747 }
748}
749
750/// The relation between two communicators.
751///
752/// # Standard section(s)
753///
754/// 6.4.1
755#[derive(Copy, Clone, PartialEq, Eq, Debug)]
756pub enum CommunicatorRelation {
757 /// Identical groups and same contexts
758 Identical,
759 /// Groups match in constituents and rank order, contexts differ
760 Congruent,
761 /// Group constituents match but rank order differs
762 Similar,
763 /// Otherwise
764 Unequal,
765}
766
767impl From<c_int> for CommunicatorRelation {
768 fn from(i: c_int) -> CommunicatorRelation {
769 if i == unsafe { ffi::RSMPI_IDENT } {
770 return CommunicatorRelation::Identical;
771 } else if i == unsafe { ffi::RSMPI_CONGRUENT } {
772 return CommunicatorRelation::Congruent;
773 } else if i == unsafe { ffi::RSMPI_SIMILAR } {
774 return CommunicatorRelation::Similar;
775 } else if i == unsafe { ffi::RSMPI_UNEQUAL } {
776 return CommunicatorRelation::Unequal;
777 }
778 panic!("Unknown communicator relation: {}", i)
779 }
780}
781
782/// Identifies a process by its `Rank` within a certain communicator.
783#[derive(Copy, Clone)]
784pub struct Process<'a, C>
785where
786 C: 'a + Communicator,
787{
788 comm: &'a C,
789 rank: Rank,
790}
791
792impl<'a, C> Process<'a, C>
793where
794 C: 'a + Communicator,
795{
796 #[allow(dead_code)]
797 fn by_rank(c: &'a C, r: Rank) -> Option<Self> {
798 if r == unsafe { ffi::RSMPI_PROC_NULL } {
799 None
800 } else {
801 Some(Process { comm: c, rank: r })
802 }
803 }
804
805 fn by_rank_unchecked(c: &'a C, r: Rank) -> Self {
806 Process { comm: c, rank: r }
807 }
808
809 /// The process rank
810 pub fn rank(&self) -> Rank {
811 self.rank
812 }
813}
814
815impl<'a, C> AsCommunicator for Process<'a, C>
816where
817 C: 'a + Communicator,
818{
819 type Out = C;
820 fn as_communicator(&self) -> &Self::Out {
821 self.comm
822 }
823}
824
825/// Identifies an arbitrary process that is a member of a certain communicator, e.g. for use as a
826/// `Source` in point to point communication.
827pub struct AnyProcess<'a, C>(&'a C)
828where
829 C: 'a + Communicator;
830
831impl<'a, C> AsCommunicator for AnyProcess<'a, C>
832where
833 C: 'a + Communicator,
834{
835 type Out = C;
836 fn as_communicator(&self) -> &Self::Out {
837 self.0
838 }
839}
840
841/// A built-in group, e.g. `MPI_GROUP_EMPTY`
842///
843/// # Standard section(s)
844///
845/// 6.2.1
846#[derive(Copy, Clone)]
847pub struct SystemGroup(MPI_Group);
848
849impl SystemGroup {
850 /// An empty group
851 pub fn empty() -> SystemGroup {
852 SystemGroup(unsafe { ffi::RSMPI_GROUP_EMPTY })
853 }
854}
855
856unsafe impl AsRaw for SystemGroup {
857 type Raw = MPI_Group;
858 fn as_raw(&self) -> Self::Raw {
859 self.0
860 }
861}
862
863impl Group for SystemGroup {}
864
865/// A user-defined group of processes
866///
867/// # Standard section(s)
868///
869/// 6.2.1
870pub struct UserGroup(MPI_Group);
871
872impl Drop for UserGroup {
873 fn drop(&mut self) {
874 unsafe {
875 ffi::MPI_Group_free(&mut self.0);
876 }
877 assert_eq!(self.0, unsafe { ffi::RSMPI_GROUP_NULL });
878 }
879}
880
881unsafe impl AsRaw for UserGroup {
882 type Raw = MPI_Group;
883 fn as_raw(&self) -> Self::Raw {
884 self.0
885 }
886}
887
888impl Group for UserGroup {}
889
890/// Groups are collections of parallel processes
891pub trait Group: AsRaw<Raw = MPI_Group> {
892 /// Group union
893 ///
894 /// Constructs a new group that contains all members of the first group followed by all members
895 /// of the second group that are not also members of the first group.
896 ///
897 /// # Standard section(s)
898 ///
899 /// 6.3.2
900 fn union<G>(&self, other: &G) -> UserGroup
901 where
902 G: Group,
903 {
904 unsafe {
905 UserGroup(
906 with_uninitialized(|newgroup| {
907 ffi::MPI_Group_union(self.as_raw(), other.as_raw(), newgroup)
908 })
909 .1,
910 )
911 }
912 }
913
914 /// Group intersection
915 ///
916 /// Constructs a new group that contains all processes that are members of both the first and
917 /// second group in the order they have in the first group.
918 ///
919 /// # Standard section(s)
920 ///
921 /// 6.3.2
922 fn intersection<G>(&self, other: &G) -> UserGroup
923 where
924 G: Group,
925 {
926 unsafe {
927 UserGroup(
928 with_uninitialized(|newgroup| {
929 ffi::MPI_Group_intersection(self.as_raw(), other.as_raw(), newgroup)
930 })
931 .1,
932 )
933 }
934 }
935
936 /// Group difference
937 ///
938 /// Constructs a new group that contains all members of the first group that are not also
939 /// members of the second group in the order they have in the first group.
940 ///
941 /// # Standard section(s)
942 ///
943 /// 6.3.2
944 fn difference<G>(&self, other: &G) -> UserGroup
945 where
946 G: Group,
947 {
948 unsafe {
949 UserGroup(
950 with_uninitialized(|newgroup| {
951 ffi::MPI_Group_difference(self.as_raw(), other.as_raw(), newgroup)
952 })
953 .1,
954 )
955 }
956 }
957
958 /// Subgroup including specified ranks
959 ///
960 /// Constructs a new group where the process with rank `ranks[i]` in the old group has rank `i`
961 /// in the new group.
962 ///
963 /// # Standard section(s)
964 ///
965 /// 6.3.2
966 fn include(&self, ranks: &[Rank]) -> UserGroup {
967 unsafe {
968 UserGroup(
969 with_uninitialized(|newgroup| {
970 ffi::MPI_Group_incl(self.as_raw(), ranks.count(), ranks.as_ptr(), newgroup)
971 })
972 .1,
973 )
974 }
975 }
976
977 /// Subgroup including specified ranks
978 ///
979 /// Constructs a new group containing those processes from the old group that are not mentioned
980 /// in `ranks`.
981 ///
982 /// # Standard section(s)
983 ///
984 /// 6.3.2
985 fn exclude(&self, ranks: &[Rank]) -> UserGroup {
986 unsafe {
987 UserGroup(
988 with_uninitialized(|newgroup| {
989 ffi::MPI_Group_excl(self.as_raw(), ranks.count(), ranks.as_ptr(), newgroup)
990 })
991 .1,
992 )
993 }
994 }
995
996 /// Number of processes in the group.
997 ///
998 /// # Standard section(s)
999 ///
1000 /// 6.3.1
1001 fn size(&self) -> Rank {
1002 unsafe { with_uninitialized(|size| ffi::MPI_Group_size(self.as_raw(), size)).1 }
1003 }
1004
1005 /// Rank of this process within the group.
1006 ///
1007 /// # Standard section(s)
1008 ///
1009 /// 6.3.1
1010 fn rank(&self) -> Option<Rank> {
1011 unsafe {
1012 let (_, rank) = with_uninitialized(|rank| ffi::MPI_Group_rank(self.as_raw(), rank));
1013 if rank == ffi::RSMPI_UNDEFINED {
1014 None
1015 } else {
1016 Some(rank)
1017 }
1018 }
1019 }
1020
1021 /// Find the rank in group `other' of the process that has rank `rank` in this group.
1022 ///
1023 /// If the process is not a member of the other group, returns `None`.
1024 ///
1025 /// # Standard section(s)
1026 ///
1027 /// 6.3.1
1028 fn translate_rank<G>(&self, rank: Rank, other: &G) -> Option<Rank>
1029 where
1030 G: Group,
1031 {
1032 unsafe {
1033 let (_, translated) = with_uninitialized(|translated| {
1034 ffi::MPI_Group_translate_ranks(self.as_raw(), 1, &rank, other.as_raw(), translated)
1035 });
1036 if translated == ffi::RSMPI_UNDEFINED {
1037 None
1038 } else {
1039 Some(translated)
1040 }
1041 }
1042 }
1043
1044 /// Find the ranks in group `other' of the processes that have ranks `ranks` in this group.
1045 ///
1046 /// If a process is not a member of the other group, returns `None`.
1047 ///
1048 /// # Standard section(s)
1049 ///
1050 /// 6.3.1
1051 fn translate_ranks<G>(&self, ranks: &[Rank], other: &G) -> Vec<Option<Rank>>
1052 where
1053 G: Group,
1054 {
1055 ranks
1056 .iter()
1057 .map(|&r| self.translate_rank(r, other))
1058 .collect()
1059 }
1060
1061 /// Compare two groups.
1062 ///
1063 /// # Standard section(s)
1064 ///
1065 /// 6.3.1
1066 fn compare<G>(&self, other: &G) -> GroupRelation
1067 where
1068 G: Group,
1069 {
1070 unsafe {
1071 with_uninitialized(|relation| {
1072 ffi::MPI_Group_compare(self.as_raw(), other.as_raw(), relation)
1073 })
1074 .1
1075 .into()
1076 }
1077 }
1078}
1079
1080/// The relation between two groups.
1081///
1082/// # Standard section(s)
1083///
1084/// 6.3.1
1085#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1086pub enum GroupRelation {
1087 /// Identical group members in identical order
1088 Identical,
1089 /// Identical group members in different order
1090 Similar,
1091 /// Otherwise
1092 Unequal,
1093}
1094
1095impl From<c_int> for GroupRelation {
1096 fn from(i: c_int) -> GroupRelation {
1097 if i == unsafe { ffi::RSMPI_IDENT } {
1098 return GroupRelation::Identical;
1099 } else if i == unsafe { ffi::RSMPI_SIMILAR } {
1100 return GroupRelation::Similar;
1101 } else if i == unsafe { ffi::RSMPI_UNEQUAL } {
1102 return GroupRelation::Unequal;
1103 }
1104 panic!("Unknown group relation: {}", i)
1105 }
1106}