rdbench 0.1.0

Reaction-diffusion simulation based on Gray-Scott model.
Documentation
use super::{CenterBlockFiller, Domain, FieldType, GrayScott, Parameters};
use crate::args::Args;
use crate::error::{Error, Result};
use mpi::environment::Universe;
use mpi::ffi;
use mpi::topology::CartesianCommunicator;
use mpi::traits::Communicator;

pub struct GrayScottFactory;

impl GrayScottFactory {
    pub fn create(universe: &Universe, args: &Args) -> Result<GrayScott> {
        let params = Parameters::new(
            args.param_du,
            args.param_dv,
            args.param_f,
            args.param_k,
            args.param_dt,
        );
        let cart_comm =
            Self::create_cart_comm(&universe.world(), [args.nr_tiles_y, args.nr_tiles_x])?;
        let domain = Domain::from_cart_comm(&cart_comm, [args.sz_tile_y, args.sz_tile_x]);
        let mut model = GrayScott::new(cart_comm, domain, params);
        model.fill(&CenterBlockFiller::new(0.7, 6, 6), FieldType::V);
        Ok(model)
    }

    fn create_cart_comm(
        comm: &impl Communicator,
        dims: [usize; 2],
    ) -> Result<CartesianCommunicator> {
        if dims.iter().all(|&d| d != 0) && dims[0] * dims[1] != comm.size() as usize {
            return Err(Error::invalid_domain(
                comm.size(),
                &dims.map(|d| d as i32).to_vec(),
                format!(
                    "Number of processes ({}) does not match the number of tiles ({}x{})",
                    comm.size(),
                    dims[1],
                    dims[0],
                )
                .as_str(),
            ));
        }
        let dims = Self::create_dims(comm.size(), &dims.map(|d| d as i32))?;
        comm.create_cartesian_communicator(&dims[..], &[true; 2], true)
            .ok_or(Error::invalid_domain(
                comm.size(),
                &dims,
                "Failed to create Cartesian communicator",
            ))
    }

    fn create_dims(np: i32, dims: &[i32]) -> Result<Vec<i32>> {
        if np <= 0 {
            return Err(Error::InvalidParameters(
                "Number of processes must be positive".to_string(),
            ));
        }
        if dims.is_empty() {
            return Err(Error::InvalidParameters(
                "Dimensions must not be empty".to_string(),
            ));
        }
        if dims.iter().any(|&d| d < 0) {
            return Err(Error::InvalidParameters(
                "Dimensions must be positive".to_string(),
            ));
        }

        let mut dims = dims.to_vec();
        let ndims = dims.len() as i32;

        let result = unsafe { ffi::MPI_Dims_create(np, ndims, dims.as_mut_ptr()) };

        if result != ffi::MPI_SUCCESS as i32 {
            return Err(Error::mpi_error(result, "Failed to create dimensions"));
        }

        Ok(dims)
    }
}