use std::mem;
use conv::ConvUtil;
use super::{sealed, AsCommunicator, Communicator, IntoTopology, Rank};
use crate::{
datatype::traits::*, ffi, ffi::MPI_Comm, raw::traits::*, topology::SimpleCommunicator,
with_uninitialized, with_uninitialized2, Count, IntArray,
};
pub struct CartesianLayout {
pub dims: Vec<Count>,
pub periods: Vec<bool>,
pub coords: Vec<Count>,
}
pub struct CartesianCommunicator(pub(crate) SimpleCommunicator);
impl CartesianCommunicator {
pub unsafe fn try_from_raw(raw: MPI_Comm) -> Option<CartesianCommunicator> {
SimpleCommunicator::try_from_raw(raw).and_then(|comm| match comm.into_topology() {
IntoTopology::Cartesian(c) => Some(c),
incorrect => {
mem::forget(incorrect);
None
}
})
}
pub fn num_dimensions(&self) -> Count {
unsafe { with_uninitialized(|count| ffi::MPI_Cartdim_get(self.as_raw(), count)).1 }
}
pub unsafe fn get_layout_into_unchecked(
&self,
dims: &mut [Count],
periods: &mut [bool],
coords: &mut [Count],
) {
let mut periods_int: IntArray = smallvec::smallvec![0; periods.len()];
ffi::MPI_Cart_get(
self.as_raw(),
self.num_dimensions(),
dims.as_mut_ptr(),
periods_int.as_mut_ptr(),
coords.as_mut_ptr(),
);
for (p, pi) in periods.iter_mut().zip(periods_int.iter()) {
*p = match pi {
0 => false,
1 => true,
_ => panic!(
"Received an invalid boolean value ({}) from the MPI implementation",
pi
),
}
}
}
pub fn get_layout_into(&self, dims: &mut [Count], periods: &mut [bool], coords: &mut [Count]) {
assert_eq!(
dims.count(),
periods.count(),
"dims, periods, and coords must be the same length"
);
assert_eq!(
dims.count(),
coords.count(),
"dims, periods, and coords must be the same length"
);
assert_eq!(
self.num_dimensions(),
dims.count(),
"dims, periods, and coords must be equal in length to num_dimensions()"
);
unsafe { self.get_layout_into_unchecked(dims, periods, coords) }
}
pub fn get_layout(&self) -> CartesianLayout {
let num_dims = self
.num_dimensions()
.value_as()
.expect("Received unexpected value from MPI_Cartdim_get");
let mut layout = CartesianLayout {
dims: vec![0; num_dims],
periods: vec![false; num_dims],
coords: vec![0; num_dims],
};
self.get_layout_into(
&mut layout.dims[..],
&mut layout.periods[..],
&mut layout.coords[..],
);
layout
}
pub unsafe fn coordinates_to_rank_unchecked(&self, coords: &[Count]) -> Rank {
with_uninitialized(|rank| ffi::MPI_Cart_rank(self.as_raw(), coords.as_ptr(), rank)).1
}
pub fn coordinates_to_rank(&self, coords: &[Count]) -> Rank {
let num_dims: usize = self
.num_dimensions()
.value_as()
.expect("Received unexpected value from MPI_Cartdim_get");
assert_eq!(
num_dims,
coords.len(),
"The coordinates slice must be the same length as the number of dimension in the \
CartesianCommunicator"
);
let layout = self.get_layout();
for (i, coord) in coords.iter().enumerate() {
if !layout.periods[i] {
assert!(
*coord > 0,
"The non-periodic coordinate (coords[{}] = {}) must be greater than 0.",
i,
*coord
);
assert!(
*coord <= layout.dims[i],
"The non-period coordinate (coords[{}] = {}) must be within the bounds of the \
CartesianCoordinator (dims[{}] = {})",
i,
*coord,
i,
layout.dims[i]
);
}
}
unsafe { self.coordinates_to_rank_unchecked(coords) }
}
pub unsafe fn rank_to_coordinates_into_unchecked(&self, rank: Rank, coords: &mut [Count]) {
ffi::MPI_Cart_coords(self.as_raw(), rank, coords.count(), coords.as_mut_ptr());
}
pub fn rank_to_coordinates_into(&self, rank: Rank, coords: &mut [Count]) {
assert!(
rank >= 0 && rank < self.size(),
"rank ({}) must be in the range [0,{})",
rank,
self.size()
);
unsafe { self.rank_to_coordinates_into_unchecked(rank, coords) }
}
pub fn rank_to_coordinates(&self, rank: Rank) -> Vec<Count> {
let mut coords = vec![
0;
self.num_dimensions()
.value_as()
.expect("Received unexpected value from MPI_Cartdim_get")
];
self.rank_to_coordinates_into(rank, &mut coords[..]);
coords
}
pub unsafe fn shift_unchecked(
&self,
dimension: Count,
displacement: Count,
) -> (Option<Rank>, Option<Rank>) {
let (_, rank_source, rank_destination) =
with_uninitialized2(|rank_source, rank_destination| {
ffi::MPI_Cart_shift(
self.as_raw(),
dimension,
displacement,
rank_source,
rank_destination,
)
});
let rank_source = if rank_source != ffi::RSMPI_PROC_NULL {
Some(rank_source)
} else {
None
};
let rank_destination = if rank_destination != ffi::RSMPI_PROC_NULL {
Some(rank_destination)
} else {
None
};
(rank_source, rank_destination)
}
pub fn shift(&self, dimension: Count, displacement: Count) -> (Option<Rank>, Option<Rank>) {
assert!(
dimension >= 0,
"dimension ({}) cannot be negative",
dimension
);
assert!(
dimension < self.num_dimensions(),
"dimension ({}) is not valid for this communicator (num_dimensions = {})",
dimension,
self.num_dimensions(),
);
unsafe { self.shift_unchecked(dimension, displacement) }
}
pub unsafe fn subgroup_unchecked(&self, retain: &[bool]) -> CartesianCommunicator {
let retain_int: IntArray = retain.iter().map(|b| *b as _).collect();
CartesianCommunicator::from_raw(
with_uninitialized(|new_comm| {
ffi::MPI_Cart_sub(self.as_raw(), retain_int.as_ptr(), new_comm)
})
.1,
)
}
pub fn subgroup(&self, retain: &[bool]) -> CartesianCommunicator {
assert_eq!(
self.num_dimensions(),
retain.count(),
"The length of the retained dimensions array must be equal to the number of dimensions \
in the CartesianCommunicator");
unsafe { self.subgroup_unchecked(retain) }
}
}
impl Communicator for CartesianCommunicator {
fn target_size(&self) -> Rank {
self.size()
}
}
impl sealed::AsHandle for CartesianCommunicator {
fn as_handle(&self) -> &sealed::CommunicatorHandle {
self.0.as_handle()
}
}
impl AsCommunicator for CartesianCommunicator {
type Out = CartesianCommunicator;
fn as_communicator(&self) -> &Self::Out {
self
}
}
unsafe impl AsRaw for CartesianCommunicator {
type Raw = MPI_Comm;
fn as_raw(&self) -> Self::Raw {
self.0.as_raw()
}
}
impl FromRaw for CartesianCommunicator {
unsafe fn from_raw(raw: <Self as AsRaw>::Raw) -> Self {
debug_assert_ne!(raw, ffi::RSMPI_COMM_NULL);
CartesianCommunicator(SimpleCommunicator::from_raw(raw))
}
}