use std::{collections::HashMap, rc::Rc};
use itertools::{Itertools, izip};
use mpi::traits::{CommunicatorCollectives, Equivalence};
use super::{ChunkSizes, GhostCommunicatorBuilder, IndexLayout};
pub struct Global2LocalDataMapper<'a, C: CommunicatorCollectives> {
index_layout: Rc<IndexLayout<'a, C>>,
ghost_communicator: super::GhostCommunicator<usize>,
ghost_to_position: HashMap<usize, usize>,
required_dofs: Vec<usize>,
chunk_size: usize,
}
impl<'a, C: CommunicatorCollectives> Global2LocalDataMapper<'a, C> {
pub fn new(
index_layout: Rc<IndexLayout<'a, C>>,
required_dofs: &[usize],
chunk_size: usize,
) -> Self {
let comm = index_layout.comm();
let rank = comm.rank() as usize;
let mut max_chunk_size = 0_usize;
comm.all_reduce_into(
&chunk_size,
&mut max_chunk_size,
mpi::collective::SystemOperation::max(),
);
let valid = chunk_size > 0
&& chunk_size == max_chunk_size
&& required_dofs
.iter()
.all(|&dof| index_layout.rank_from_index(dof).is_some());
let mut all_valid = false;
comm.all_reduce_into(
&valid,
&mut all_valid,
mpi::collective::SystemOperation::logical_and(),
);
assert!(
all_valid,
"required dof is outside the global layout or chunk sizes are invalid"
);
let mut ghost_dofs = Vec::<usize>::new();
for &dof in required_dofs {
if index_layout.rank_from_index(dof).expect("validated dof") != rank {
ghost_dofs.push(dof);
}
}
let ghost_dofs = ghost_dofs.iter().unique().copied().collect_vec();
let ghost_communicator = GhostCommunicatorBuilder::new()
.add_ghosts(
ghost_dofs
.iter()
.map(|&dof| (dof, index_layout.rank_from_index(dof).unwrap())),
)
.chunk_sizes(ChunkSizes::Uniform(chunk_size))
.build(comm);
let ghost_to_position = HashMap::<usize, usize>::from_iter(
ghost_communicator
.receive_indices()
.iter()
.enumerate()
.map(|(i, &d)| (d, i)),
);
Self {
index_layout,
ghost_communicator,
ghost_to_position,
required_dofs: required_dofs.to_vec(),
chunk_size,
}
}
pub fn map_data<T: Equivalence + Copy + Default + std::fmt::Debug>(
&self,
data: &[T],
) -> Vec<T> {
let chunk_size = self.chunk_size;
let rank = self.index_layout.comm().rank() as usize;
let data_valid = data.len() == self.index_layout.number_of_local_indices() * chunk_size;
let send_buffer = self.ghost_communicator.send_buffer_from_fn(|index, chunk| {
if data_valid {
let local_start_index = self
.index_layout
.global2local(rank, index)
.expect("ghost sender must own its requested index")
* chunk_size;
chunk.copy_from_slice(&data[local_start_index..local_start_index + chunk_size]);
}
});
let receive_buffer = send_buffer.forward();
assert!(
data_valid,
"mapper input data has length {}, expected {}",
data.len(),
self.index_layout.number_of_local_indices() * chunk_size
);
let mut output_data = vec![T::default(); self.required_dofs.len() * chunk_size];
for (&dof, output_chunk) in izip!(
self.required_dofs.iter(),
output_data.chunks_mut(chunk_size)
) {
if self
.index_layout
.rank_from_index(dof)
.expect("required dof is out of range")
== rank
{
let local_dof = self.index_layout.global2local(rank, dof).unwrap();
output_chunk
.copy_from_slice(&data[local_dof * chunk_size..(local_dof + 1) * chunk_size]);
} else {
output_chunk.copy_from_slice(receive_buffer.chunk(self.ghost_to_position[&dof]));
}
}
output_data
}
pub fn index_layout(&self) -> Rc<IndexLayout<'a, C>> {
self.index_layout.clone()
}
pub fn chunk_size(&self) -> usize {
self.chunk_size
}
pub fn ghost_communicator(&self) -> &super::GhostCommunicator<usize> {
&self.ghost_communicator
}
}