/// Make a list of link cells that need to be sent across the specified /// face. For each face, the list must include all cells, local and /// halo, in the first two planes of link cells. Halo cells must be /// included in the list of link cells to send since local atoms may /// have moved from local cells into halo cells on this time step. /// (Actual remote atoms should have been deleted, so the halo cells /// should contain only these few atoms that have just crossed.) /// Sending these atoms will allow them to be reassigned to the task /// that covers the spatial domain they have moved into. /// /// Note that link cell grid coordinates range from -1 to gridSize[iAxis]. /// \see initLinkCells for an explanation link cell grid coordinates. /// /// \param [in] boxes Link cell information. /// \param [in] iFace Index of the face data will be sent across. /// \param [in] nCells Number of cells to send. This is used for a /// consistency check. /// \return The list of cells to send. Caller is responsible to free /// the list. int* mkAtomCellList(LinkCell* boxes, enum HaloFaceOrder iFace, const int nCells) { int* list = comdMalloc(nCells*sizeof(int)); int xBegin = -1; int xEnd = boxes->gridSize[0]+1; int yBegin = -1; int yEnd = boxes->gridSize[1]+1; int zBegin = -1; int zEnd = boxes->gridSize[2]+1; if (iFace == HALO_X_MINUS) xEnd = xBegin+2; if (iFace == HALO_X_PLUS) xBegin = xEnd-2; if (iFace == HALO_Y_MINUS) yEnd = yBegin+2; if (iFace == HALO_Y_PLUS) yBegin = yEnd-2; if (iFace == HALO_Z_MINUS) zEnd = zBegin+2; if (iFace == HALO_Z_PLUS) zBegin = zEnd-2; int count = 0; for (int ix=xBegin; ix<xEnd; ++ix) for (int iy=yBegin; iy<yEnd; ++iy) for (int iz=zBegin; iz<zEnd; ++iz) list[count++] = getBoxFromTuple(boxes, ix, iy, iz); assert(count == nCells); return list; }
/// Get the index of the link cell that contains the specified /// coordinate. This can be either a halo or a local link cell. /// /// Because the rank ownership of an atom is strictly determined by the /// atom's position, we need to take care that all ranks will agree which /// rank owns an atom. The conditionals at the end of this function are /// special care to ensure that all ranks make compatible link cell /// assignments for atoms that are near a link cell boundaries. If no /// ranks claim an atom in a local cell it will be lost. If multiple /// ranks claim an atom it will be duplicated. int getBoxFromCoord(LinkCell* boxes, real_t rr[3]) { const real_t* localMin = boxes->localMin; // alias const real_t* localMax = boxes->localMax; // alias const int* gridSize = boxes->gridSize; // alias int ix = (int)(floor((rr[0] - localMin[0])*boxes->invBoxSize[0])); int iy = (int)(floor((rr[1] - localMin[1])*boxes->invBoxSize[1])); int iz = (int)(floor((rr[2] - localMin[2])*boxes->invBoxSize[2])); // For each axis, if we are inside the local domain, make sure we get // a local link cell. Otherwise, make sure we get a halo link cell. if(rr[0] < localMax[0]) { if (ix == gridSize[0]) { ix = gridSize[0] - 1; } } else { ix = gridSize[0]; // assign to halo cell } if(rr[1] < localMax[1]) { if (iy == gridSize[1]) { iy = gridSize[1] - 1; } } else { iy = gridSize[1]; } if(rr[2] < localMax[2]) { if (iz == gridSize[2]) { iz = gridSize[2] - 1; } } else { iz = gridSize[2]; } return getBoxFromTuple(boxes, ix, iy, iz); }
//for shared memory only, takes a box ID for a halo cell and returns the local cell that it corresponds to. int getLocalHaloTuple(LinkCell *boxes, int iBox) { int x,y,z; getTuple(boxes, iBox, &x, &y, &z); haloToLocalCell(&x, &y, &z, boxes->gridSize); return getBoxFromTuple(boxes, x, y, z); }
/// \details /// Populates the nbrBoxes array with the 27 boxes that are adjacent to /// iBox. The count is 27 instead of 26 because iBox is included in the /// list (as neighbor 13). Caller is responsible to alloc and free /// nbrBoxes. /// \return The number of nbr boxes (always 27 in this implementation). int getNeighborBoxes(LinkCell* boxes, int iBox, int* nbrBoxes) { int ix, iy, iz; getTuple(boxes, iBox, &ix, &iy, &iz); int count = 0; for (int i=ix-1; i<=ix+1; i++) for (int j=iy-1; j<=iy+1; j++) for (int k=iz-1; k<=iz+1; k++) nbrBoxes[count++] = getBoxFromTuple(boxes,i,j,k); return count; }
/// Make a list of link cells that need to receive data across the /// specified face. Note that this list must be compatible with the /// corresponding send list to ensure that the data goes to the correct /// atoms. /// /// \see initLinkCells for information about the conventions for grid /// coordinates of link cells. int* mkForceRecvCellList(LinkCell* boxes, int face, int nCells) { int* list = comdMalloc(nCells*sizeof(int)); int xBegin, xEnd, yBegin, yEnd, zBegin, zEnd; int nx = boxes->gridSize[0]; int ny = boxes->gridSize[1]; int nz = boxes->gridSize[2]; switch(face) { case HALO_X_MINUS: xBegin=-1; xEnd=0; yBegin=0; yEnd=ny; zBegin=0; zEnd=nz; break; case HALO_X_PLUS: xBegin=nx; xEnd=nx+1; yBegin=0; yEnd=ny; zBegin=0; zEnd=nz; break; case HALO_Y_MINUS: xBegin=-1; xEnd=nx+1; yBegin=-1; yEnd=0; zBegin=0; zEnd=nz; break; case HALO_Y_PLUS: xBegin=-1; xEnd=nx+1; yBegin=ny; yEnd=ny+1; zBegin=0; zEnd=nz; break; case HALO_Z_MINUS: xBegin=-1; xEnd=nx+1; yBegin=-1; yEnd=ny+1; zBegin=-1; zEnd=0; break; case HALO_Z_PLUS: xBegin=-1; xEnd=nx+1; yBegin=-1; yEnd=ny+1; zBegin=nz; zEnd=nz+1; break; default: assert(1==0); } int count = 0; for (int ix=xBegin; ix<xEnd; ++ix) for (int iy=yBegin; iy<yEnd; ++iy) for (int iz=zBegin; iz<zEnd; ++iz) list[count++] = getBoxFromTuple(boxes, ix, iy, iz); assert(count == nCells); return list; }