def test2d_1_non_periodic(self):
        """
        2d array test, 1 ghost, non-periodic boundary conditions
        """

        # create the dist array, the sizes are local to each processor
        da = mpinum.gdaZeros( (2,3), numpy.float32, numGhosts=1 )

        # processor rank and number of processes
        rk = da.rk
        nprocs = da.sz

        # set the data
        da[:] = rk

        # get the neighbor MPI rank (None if there is no neighbor)
        otherRk = rk - 1
        if otherRk < 0:
            otherRk = None

        # collective operation. all procs must call "get"
        southData = da.getData( otherRk, winID=(1,0) )

        # check 
        if otherRk is not None and otherRk >= 0:
            self.assertEqual(southData.min(), rk - 1)
            self.assertEqual(southData.max(), rk - 1)

        # clean up
        da.free()
    def test1d_1(self):
        """
        1d, float64
        """

        dtyp = numpy.float64

        # create the ghosted dist array
        n = 10
        da = mpinum.gdaZeros( (n,), dtyp, numGhosts=1 )

        # set data to process dependent value, 
        # da.rk is the mpi proc ID
        # da.sz is the size of the MPI communicator
        da[:] = 100*da.rk + numpy.array([i for i in range(n)], dtyp)

        # access remote data to the left
        leftRk = (da.rk - 1) % da.sz

        print('proc %d tries to access data from %d' % (da.rk, leftRk))
        leftData = da.getData(pe=leftRk, winID=(1,))

        print('leftData for rank %d = %s' % (da.rk, str(leftData)))
        # check
        if leftRk < da.rk:
            self.assertEqual(leftData[0], da[-1] - 100)
        else:
            self.assertEqual(leftData[0], da[-1] + 100*(da.sz-1))

        # free
        da.free()
Exemple #3
0
    def apply(self, localArray):
        """
        Apply Laplacian stencil to data
        @param localArray local array
        @return new array on local proc
        """

        # input dist array
        inp = gdaZeros(localArray.shape, localArray.dtype, numGhosts=1)
        # output array
        out = numpy.zeros(localArray.shape, localArray.dtype)

        # no displacement term
        weight = self.stencil[self.zeros]
        out[...] += weight * localArray

        for disp in self.srcLocalDomains:

            weight = self.stencil[disp]

            # no communication required here
            srcDom = self.srcLocalDomains[disp]
            dstDom = self.dstLocalDomains[disp]

            out[dstDom] += weight * localArray[srcDom]

            #
            # now the part that requires communication
            #

            # set the ghost values
            srcSlab = self.srcSlab[disp]
            # copy
            inp[srcSlab] = localArray[srcSlab]

            # send over to local process
            dstSlab = self.dstSlab[disp]
            winId = self.winIds[disp]
            rk = self.neighRk[disp]

            # remote fetch
            out[dstSlab] += weight * inp.getData(rk, winId)

        # some implementations require this
        inp.free()

        return out
    def test2d_1_periodic(self):
        """
        2d array test, 1 ghost, periodic boundary conditions
        """

        # create the dist array, the sizes are local to each processor
        da = mpinum.gdaZeros( (2,3), numpy.float32, numGhosts=1 )

        # processor rank and number of processes
        rk = da.rk
        nprocs = da.sz

        # set the data
        da[:] = rk

        # access neighbor data, collective operation
        southData = da.getData( (rk-1) % nprocs, winID=(1,0) )

        # check 
        self.assertEqual(southData.min(), (rk - 1) % nprocs)
        self.assertEqual(southData.max(), (rk - 1) % nprocs)

        # clean up
        da.free()
Exemple #5
0
# list of slices
slab = dc.getSlab(rk)

# starting/ending indices for local domain
iBeg, iEnd = slab[0].start, slab[0].stop
jBeg, jEnd = slab[1].start, slab[1].stop

# local variables
xx = numpy.outer(xs[iBeg:iEnd], numpy.ones((ny / npy, ), numpy.float64))
yy = numpy.outer(numpy.ones((nx / npx, ), numpy.float64), ys[jBeg:jEnd])

# local field
zz = numpy.sin(numpy.pi * xx) * numpy.cos(2 * numpy.pi * yy)

# create and set distributed array
zda = mpinum.gdaZeros(zz.shape, zz.dtype, numGhosts=1)
zda[:] = zz

# compute the star Laplacian in the interior, this does not require
# any communication

laplaceZ = 4 * zda[:]

# local neighbour contributions, no communication
laplaceZ[1:, :] -= zda[0:-1, :]
laplaceZ[0:-1, :] -= zda[1:, :]
laplaceZ[:, 1:] -= zda[:, 0:-1]
laplaceZ[:, 0:-1] -= zda[:, 1:]

# now compute and fill in the halo
Exemple #6
0
slab = dc.getSlab(rk)
iBeg, iEnd = slab[0].start, slab[0].stop
jBeg, jEnd = slab[1].start, slab[1].stop

# local domain sizes
nx, ny = iEnd - iBeg, jEnd - jBeg

# the decomp must be regular
if not dc.getDecomp():
    if rk == 0:
        print('no decomp could be found, adjust the number of procs')
        MPI.Finalize()
        sys.exit(1)

# create and set the input distributed array
inputData = mpinum.gdaZeros((nx, ny), numpy.float32, numGhosts=1)
setValues(nxG, nyG, iBeg, iEnd, jBeg, jEnd, inputData)

# store the number of times a cell has an invalid neighbor so
# we can correct the weights
numInvalidNeighbors = numpy.zeros((nx, ny), numpy.int32)

domain = Partition(2)

# the ghosted array only exposes west, east, south and north
# windows. Need to also export the corners
for disp in (-1, -1), (-1, 1), (1, -1), (1, 1):
    d0 = (disp[0], 0)
    d1 = (0, disp[1])
    n0 = (-disp[0], 0)
    n1 = (0, -disp[1])
Exemple #7
0
    def __init__(self, velocity, lengths, numCells):

        self.rk = MPI.COMM_WORLD.Get_rank()
        self.sz = MPI.COMM_WORLD.Get_size()

        # decomposition
        self.dc = mpinum.CubeDecomp(self.sz, numCells)
        if not self.dc.getDecomp():
            print(
                '*** No uniform decomposition could be found for {0} processes'
                .format(self.sz))
            print('*** Please ajust the number of cells {0}'.format(numCells))
            sys.exit(1)

        # begin/end indices of local sub-domain
        self.localSlices = self.dc.getSlab(self.rk)
        self.iBeg = numpy.array([s.start for s in self.localSlices])
        self.iEnd = numpy.array([s.stop for s in self.localSlices])
        self.nsLocal = numpy.array(
            [s.stop - s.start for s in self.localSlices])
        print('[{0}] local number of cells: {1}'.format(self.rk, self.nsLocal))

        # global number of cells
        self.numCells = numCells

        self.ndims = 3
        self.deltas = numpy.zeros((self.ndims, ), numpy.float64)
        self.upDirection = numpy.zeros((self.ndims, ), numpy.float64)
        self.v = velocity
        self.lengths = lengths

        # number of local field values
        self.ntot = 1
        for j in range(self.ndims):
            self.upDirection[j] = -1
            if velocity[j] < 0.: self.upDirection[j] = +1
            self.deltas[j] = lengths[j] / numCells[j]
            self.ntot *= self.nsLocal[j]

        self.coeff = self.v * self.upDirection / self.deltas

        # initializing the field
        self.f = mpinum.gdaZeros(self.nsLocal, numpy.float64, numGhosts=1)
        self.fOld = mpinum.gdaZeros(self.nsLocal, numpy.float64, numGhosts=1)

        # initialize lower corner to one
        if self.rk == 0:
            self.f[0, 0, 0] = 1

        # get the neighboring ranks
        self.neighbSide = [[] for i in range(self.ndims)]
        direction = numpy.array([0] * self.ndims)
        self.neighbRk = numpy.array([0] * self.ndims)
        periodic = [True for i in range(self.ndims)]
        for i in range(self.ndims):
            direction[i] = self.upDirection[i]
            self.neighbRk[i] = self.dc.getNeighborProc(self.rk,
                                                       direction,
                                                       periodic=periodic)
            self.neighbSide[i] = tuple(-direction)
            direction[i] = 0
    def test2d_laplacian_periodic(self):
        """
        2d array, apply Laplacian, periodic along the two axes
        """
        from mpinum import CubeDecomp
        from mpinum import MultiArrayIter
        import operator
        from math import sin, pi

        # global sizes
        ndims = 2
        #ns = numpy.array([60] * ndims)
        ns = numpy.array([3*4] * ndims)

        # local rank and number of procs
        rk = MPI.COMM_WORLD.Get_rank()
        sz = MPI.COMM_WORLD.Get_size()

        # find a domain decomposition
        dc = CubeDecomp(sz, ns)

        # not all numbers of procs will give a uniform domain decomposition,
        # exit if none can be found
        if not dc.getDecomp():
            if rk == 0: 
                print('no decomp could be found, adjust the number of procs')
            return
        
        # get the local start/stop indices along each axis as a list of 
        # 1d slices
        localSlices = dc.getSlab(rk)
        iBeg = numpy.array([s.start for s in localSlices])
        iEnd = numpy.array([s.stop for s in localSlices])
        nsLocal = numpy.array([s.stop - s.start for s in localSlices])
        
        # create the dist arrays
        da = mpinum.gdaZeros(nsLocal, numpy.float32, numGhosts=1)
        laplacian = mpinum.gdaZeros(nsLocal, numpy.float32, numGhosts=1)

        # set the data
        for it in MultiArrayIter(nsLocal):
            localInds = it.getIndices()
            globalInds = iBeg + localInds
            # positions are cell centered, domain is [0, 1]^ndims
            position = (globalInds  + 0.5)/ numpy.array(ns, numpy.float32)
            # sin(2*pi*x) * sin(2*pi*y) ...
            da[tuple(localInds)] = reduce(operator.mul, 
                                   [numpy.sin(2*numpy.pi*position[i]) for i in range(ndims)])

        # apply the Laplacian finite difference operator.
        # Start by performing all the operations that do
        # not require any communication.
        laplacian[:] = 2 * ndims * da

        # now subtract the neighbor values which are local to this process
        for idim in range(ndims):
            # indices shifted in the + direction along axis idim
            slabP = [slice(None, None, None) for j in range(idim)] + \
                [slice(1, None, None)] + \
                [slice(None, None, None) for j in range(idim + 1, ndims)]
            # indices shifted in the - direction along axis idim
            slabM = [slice(None, None, None) for j in range(idim)] + \
                [slice(0, -1, None)] + \
                [slice(None, None, None) for j in range(idim + 1, ndims)]
            laplacian[slabP] -= da[slabM] # subtract left neighbor
            laplacian[slabM] -= da[slabP] # subtract right neighbor

        # fetch the data located on other procs
        periodic = [True for idim in range(ndims)]
        for idim in range(ndims):
            # define the positive and negative directions
            directionP = tuple([0 for j in range(idim)] + [1] + [0 for j in range(idim + 1, ndims)])
            directionM = tuple([0 for j in range(idim)] + [-1] + [0 for j in range(idim + 1, ndims)])
            procP = dc.getNeighborProc(rk, directionP, periodic=periodic)
            procM = dc.getNeighborProc(rk, directionM, periodic=periodic)

            # this is where communication takes place... Note that when
            # accessing the data on the low-end side on rank procM we
            # access the slide on the positive side on procM (directionP).
            # And inversely for the high-end side data...
            dataM = da.getData(procM, winID=directionP)
            dataP = da.getData(procP, winID=directionM)

            # finish off the operator
            laplacian[da.getEllipsis(winID=directionM)] -= dataM
            laplacian[da.getEllipsis(winID=directionP)] -= dataP

        # compute a checksum and send the result to rank 0
        checksum = laplacian.reduce(lambda x,y:abs(x) + abs(y), 0.0, rootPe=0)
        if rk == 0:
            print('checksum = ', checksum)
            # float32 calculation has higher error
            assert(abs(checksum - 32.0) < 1.e-4)
        
        # free the windows
        da.free()
        laplacian.free()