Source code for pyccel.stdlib.parallel.mpi

# ------------------------------------------------------------------------- #
# This file is part of Pyccel which is released under MIT License. See the  #
# LICENSE file or go to https://github.com/pyccel/pyccel/blob/devel/LICENSE #
# for full license details.                                                 #
# ------------------------------------------------------------------------- #
"""
Module containing the `Cart` class which maps MPI's Cart concepts to a single
place, providing a convenient interface with encapsulated MPI functionality.
The functions of the `Cart` class are imported from the `mpi` module in
`pyccel.stdlib.parallel` and are translated to the appropriate MPI calls in
the target language. This may need removing with #251.
"""

# TODO - improve communicate method
#      - debug reduce method: it must return the global error

# from pyccel.stdlib.parallel.mpi import (
#    MPI_DOUBLE,
#    MPI_SUM,
#    mpi_allreduce,
#    mpi_cart_coords,
#    mpi_cart_create,
#    mpi_cart_shift,
#    mpi_cart_sub,
#    mpi_comm_free,
#    mpi_comm_rank,
#    mpi_comm_size,
#    mpi_comm_world,
#    mpi_dims_create,
#    mpi_sendrecv,
#    mpi_status_size,
#    mpi_type_commit,
#    mpi_type_contiguous,
#    mpi_type_free,
#    mpi_type_vector,
# )

# $ header class Cart(public)
# $ header method __init__(Cart, int [:], int [:], bool [:], bool)
# $ header method __del__(Cart)
# $ header method communicate(Cart, double [:,:])
# $ header method reduce(Cart, double)


[docs] class Cart: """ A class to map MPI's Cart concepts. A class to map MPI's Cart concepts. This may need removing with #251. Parameters ---------- npts : tuple[int, ...] Number of points in each dimension, excluding the ghost regions. pads : tuple[int, ...] Array padding, i.e. number of ghost points in each dimension. periods : tuple[bool, ...] Whether the grid is periodic (true) or not (false) in each dimension. reorder : bool MPI ranking may be reordered (true) or not (false). """ def __init__(self, npts, pads, periods, reorder): ntx = npts[0] nty = npts[1] # ... TODO : to be computed using 'len' self.ndims = 2 self.n_neighbour = 4 # ... # ... Constants north = 0 east = 1 south = 2 west = 3 # ... # ... self.neighbour = zeros(self.n_neighbour, int) self.coords = zeros(self.ndims, int) self.dims = zeros(self.ndims, int) self.starts = zeros(self.ndims, int) self.ends = zeros(self.ndims, int) self.comm1d = zeros(self.ndims, int) self.steps = [1, 1] self.pads = pads self.periods = periods self.reorder = reorder # ... # ... TODO: remove from here ierr = -1 size = -1 self.rank = -1 self.rank_in_topo = -1 self.comm_cart = -1 comm = mpi_comm_world mpi_comm_size(comm, size, ierr) mpi_comm_rank(comm, self.rank, ierr) # ... # ... # Know the number of processes along x and y mpi_dims_create(size, self.ndims, self.dims, ierr) # ... # ... # Create a 2d mpi cart mpi_cart_create( comm, self.ndims, self.dims, self.periods, self.reorder, self.comm_cart, ierr, ) # Know my coordinates in the topology mpi_comm_rank(self.comm_cart, self.rank_in_topo, ierr) mpi_cart_coords( self.comm_cart, self.rank_in_topo, self.ndims, self.coords, ierr ) # X-axis limits sx = (self.coords[0] * ntx) / self.dims[0] ex = ((self.coords[0] + 1) * ntx) / self.dims[0] - 1 # Y-axis limits sy = (self.coords[1] * nty) / self.dims[1] ey = ((self.coords[1] + 1) * nty) / self.dims[1] - 1 # ... # ... self.starts[0] = sx self.ends[0] = ex self.starts[1] = sy self.ends[1] = ey # ... # ... self.sx = sx self.ex = ex + 1 self.sy = sy self.ey = ey + 1 # ... # ... grid without ghost cells self.r_x = range(self.sx, self.ex, self.steps[0]) self.r_y = range(self.sy, self.ey, self.steps[1]) self.indices = tensor(self.r_x, self.r_y) # ... # ... self.sx_ext = sx - self.pads[0] self.ex_ext = ex + self.pads[0] + 1 self.sy_ext = sy - self.pads[1] self.ey_ext = ey + self.pads[1] + 1 # ... # ... extended grid with ghost cells self.r_ext_x = range(self.sx_ext, self.ex_ext, self.steps[0]) self.r_ext_y = range(self.sy_ext, self.ey_ext, self.steps[1]) self.extended_indices = tensor(self.r_ext_x, self.r_ext_y) # ... # ... Neighbours # Search of my West and East neighbours mpi_cart_shift( self.comm_cart, 0, self.pads[0], self.neighbour[west], self.neighbour[east], ierr, ) # Search of my South and North neighbours mpi_cart_shift( self.comm_cart, 1, self.pads[1], self.neighbour[south], self.neighbour[north], ierr, ) # ... # ... Create 1d communicator within the cart flags = [True, False] mpi_cart_sub(self.comm_cart, flags, self.comm1d[0], ierr) flags = [False, True] mpi_cart_sub(self.comm_cart, flags, self.comm1d[1], ierr) # ... # ... Derived Types # Creation of the type_line derived datatype to exchange points # with northern to southern neighbours self.type_line = -1 mpi_type_vector( ey - sy + 1, 1, ex - sx + 1 + 2 * self.pads[0], MPI_DOUBLE, self.type_line, ierr, ) mpi_type_commit(self.type_line, ierr) # Creation of the type_column derived datatype to exchange points # with western to eastern neighbours self.type_column = -1 mpi_type_contiguous(ex - sx + 1, MPI_DOUBLE, self.type_column, ierr) mpi_type_commit(self.type_column, ierr) # ... def __del__(self): ierr = -1 # Free the datatype mpi_type_free(self.type_line, ierr) mpi_type_free(self.type_column, ierr) # Destruction of the communicators mpi_comm_free(self.comm_cart, ierr)
[docs] def communicate(self, u): ierr = -1 tag = 1435 status = zeros(mpi_status_size, int) # ... Constants north = 0 east = 1 south = 2 west = 3 # ... sx = self.starts[0] ex = self.ends[0] sy = self.starts[1] ey = self.ends[1] # ... Communication # Send to neighbour north and receive from neighbour south mpi_sendrecv( u[sx, sy], 1, self.type_line, self.neighbour[north], tag, u[ex + 1, sy], 1, self.type_line, self.neighbour[south], tag, self.comm_cart, status, ierr, ) # Send to neighbour south and receive from neighbour north mpi_sendrecv( u[ex, sy], 1, self.type_line, self.neighbour[south], tag, u[sx - 1, sy], 1, self.type_line, self.neighbour[north], tag, self.comm_cart, status, ierr, ) # Send to neighbour west and receive from neighbour east mpi_sendrecv( u[sx, sy], 1, self.type_column, self.neighbour[west], tag, u[sx, ey + 1], 1, self.type_column, self.neighbour[east], tag, self.comm_cart, status, ierr, ) # Send to neighbour east and receive from neighbour west mpi_sendrecv( u[sx, ey], 1, self.type_column, self.neighbour[east], tag, u[sx, sy - 1], 1, self.type_column, self.neighbour[west], tag, self.comm_cart, status, ierr, )
# ...
[docs] def reduce(self, x): ierr = -1 global_x = 0.0 mpi_allreduce(x, global_x, 1, MPI_DOUBLE, MPI_SUM, self.comm_cart, ierr) print(global_x, x)