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}