package org.openpixi.pixi.distributed; import org.openpixi.pixi.distributed.movement.boundary.BorderRegions; import org.openpixi.pixi.physics.GeneralBoundaryType; import org.openpixi.pixi.physics.movement.boundary.BoundaryRegions; import org.openpixi.pixi.physics.util.IntBox; import org.openpixi.pixi.physics.util.Point; /** * Maps boundary and border regions to neighbors. * We distinguish border neighbors (those to whom we send data) * and boundary neighbors (those from which we receive data). * These neighbors are the same at the edges but different at the corners. * * Finds also the direction of the neighbors. * The neighbor directions are necessary for * correct particle position translation when sending the particle to neighbor * and for correct mapping of border cells at one node to ghost cells at other node. * * While the periodic regions have the directions of their neighbors predefined in the fields * boundaryNeighborsDirections and borderNeighborsDirections, for the hardwall regions it is * easier to determine the direction based on the region neighbor. * * For the hardwall regions we can not have the directions predefined as the directions at the * corner depend on the layout. * * On the other hand, we can not find the neighbor direction through the neighbor in periodic * boundaries. For example, at the top edge of the global simulation area we want the neighbor * direction to point upward and not downward! * * How do we find the neighbors? * ============================= * * First we determine for both types of neighbors (boundary and border) helper points * which help us find the neighbors. * * For each boundary region we specify a point which belongs to the region. * Afterwards we search the list of neighbors for the neighbor which contains the point. * If such a neighbor is not found, it means that local simulation boundary * is also global simulation boundary. * * Note that the situation differs between hardwall and periodic boundary types. * While under periodic boundaries we always have a neighbor on each side, * under hardwall boundaries this is not true. * * For border regions we also have the helper points which identify the potential neighbors. * For each edge region one helper point is enough. * However, the corner border regions have three helper points * since they can have up to three neighbors, * depending on boundary type and corner location. */ public class NeighborMap { /** Dummy neighbor ID signalizing that there is no neighbor. */ public static final int NO_NEIGHBOR = -1; private IntBox[] partitions; private int thisWorkerID; private IntBox globalSimArea; private GeneralBoundaryType boundaryType; /** Maps boundary regions to neighbors. */ private int[] boundaryNeighbors = new int[BoundaryRegions.NUM_OF_REGIONS]; private Point[] boundaryPoints = new Point[BoundaryRegions.NUM_OF_REGIONS]; /** Only used for periodic boundaries. */ private Point[] boundaryNeighborsDirections = new Point[BoundaryRegions.NUM_OF_REGIONS]; /** Maps border regions to neighbors. */ private int[][] borderNeighbors = new int[BorderRegions.NUM_OF_REGIONS][]; private Point[][] borderPoints = new Point[BorderRegions.NUM_OF_REGIONS][]; /** Only used for periodic boundaries. */ private Point[][] borderNeighborsDirections = new Point[BorderRegions.NUM_OF_REGIONS][]; public int getBoundaryNeighbor(int region) { return boundaryNeighbors[region]; } public int[] getBorderNeighbors(int region) { return borderNeighbors[region]; } public Point getBoundaryNeighborsDirections(int region) { if (boundaryType == GeneralBoundaryType.Periodic) { return boundaryNeighborsDirections[region]; } else { return getNeighborDirection(getBoundaryNeighbor(region)); } } public Point[] getBorderNeighborsDirections(int region) { if (boundaryType == GeneralBoundaryType.Periodic) { return borderNeighborsDirections[region]; } else { int[] neighbors = getBorderNeighbors(region); Point[] directions = new Point[neighbors.length]; for (int i = 0; i < neighbors.length; ++i) { directions[i] = getNeighborDirection(neighbors[i]); } return directions; } } public NeighborMap(int thisWorkerID, IntBox[] partitions, IntBox globalSimArea, GeneralBoundaryType boundaryType) { this.partitions = partitions; this.thisWorkerID = thisWorkerID; this.globalSimArea = globalSimArea; this.boundaryType = boundaryType; initBoundaryNeighbors(); initBoundaryPoints(partitions[thisWorkerID]); initBoundaryNeighborsDirections(partitions[thisWorkerID]); initBorderNeighbors(); initBorderPoints(partitions[thisWorkerID]); initBorderNeighborsDirections(partitions[thisWorkerID]); if (boundaryType == GeneralBoundaryType.Hardwall) { setHardwallBoundaryNeighbors(); setHardwallBorderNeighbors(); } else if (boundaryType == GeneralBoundaryType.Periodic) { setPeriodicBoundaryNeighbors(); setPeriodicBorderNeighbors(); } else { throw new RuntimeException("Unsupported boundary type!"); } } private void initBoundaryNeighbors() { for (int region = 0; region < BoundaryRegions.NUM_OF_REGIONS; ++region) { boundaryNeighbors[region] = NO_NEIGHBOR; } } private void initBorderNeighbors() { for (int region = 0; region < BorderRegions.NUM_OF_REGIONS; ++region) { borderNeighbors[region] = new int[] {}; } } private void initBoundaryPoints(IntBox partition) { int xmid = partition.xmin() + partition.xsize() / 2; int ymid = partition.ymin() + partition.ysize() / 2; boundaryPoints[BoundaryRegions.X_MIN + BoundaryRegions.Y_MIN] = new Point(partition.xmin() - 1, partition.ymin() - 1); boundaryPoints[BoundaryRegions.X_MIN + BoundaryRegions.Y_CENTER] = new Point(partition.xmin() - 1, ymid); boundaryPoints[BoundaryRegions.X_MIN + BoundaryRegions.Y_MAX] = new Point(partition.xmin() - 1, partition.ymax() + 1); boundaryPoints[BoundaryRegions.X_CENTER + BoundaryRegions.Y_MIN] = new Point(xmid, partition.ymin() - 1); // The point for the center region remains null. boundaryPoints[BoundaryRegions.X_CENTER + BoundaryRegions.Y_MAX] = new Point(xmid, partition.ymax() + 1); boundaryPoints[BoundaryRegions.X_MAX + BoundaryRegions.Y_MIN] = new Point(partition.xmax() + 1, partition.ymin() - 1); boundaryPoints[BoundaryRegions.X_MAX + BoundaryRegions.Y_CENTER] = new Point(partition.xmax() + 1, ymid); boundaryPoints[BoundaryRegions.X_MAX + BoundaryRegions.Y_MAX] = new Point(partition.xmax() + 1, partition.ymax() + 1); } private void initBoundaryNeighborsDirections(IntBox partition) { boundaryNeighborsDirections[BoundaryRegions.X_MIN + BoundaryRegions.Y_MIN] = new Point(-1, -1); boundaryNeighborsDirections[BoundaryRegions.X_MIN + BoundaryRegions.Y_CENTER] = new Point(-1, 0); boundaryNeighborsDirections[BoundaryRegions.X_MIN + BoundaryRegions.Y_MAX] = new Point(-1, +1); boundaryNeighborsDirections[BoundaryRegions.X_CENTER + BoundaryRegions.Y_MIN] = new Point(0, -1); boundaryNeighborsDirections[BoundaryRegions.X_CENTER + BoundaryRegions.Y_MAX] = new Point(0, +1); boundaryNeighborsDirections[BoundaryRegions.X_MAX + BoundaryRegions.Y_MIN] = new Point(+1, -1); boundaryNeighborsDirections[BoundaryRegions.X_MAX + BoundaryRegions.Y_CENTER] = new Point(+1, 0); boundaryNeighborsDirections[BoundaryRegions.X_MAX + BoundaryRegions.Y_MAX] = new Point(+1, +1); } private void initBorderPoints(IntBox partition) { /* * Corner points - can have up to 3 neighbors => 3 points to identify the neighbors. * The first point always identifies the corner neighbor. */ borderPoints[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(partition.xmin() - 1, partition.ymin() - 1), new Point(partition.xmin(), partition.ymin() - 1), new Point(partition.xmin() - 1, partition.ymin()) }; borderPoints[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(partition.xmin() - 1, partition.ymax() + 1), new Point(partition.xmin(), partition.ymax() + 1), new Point(partition.xmin() - 1, partition.ymax()) }; borderPoints[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(partition.xmax() + 1, partition.ymin() - 1), new Point(partition.xmax(), partition.ymin() - 1), new Point(partition.xmax() + 1, partition.ymin()) }; borderPoints[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(partition.xmax() + 1, partition.ymax() + 1), new Point(partition.xmax(), partition.ymax() + 1), new Point(partition.xmax() + 1, partition.ymax()) }; /* * Outside hardwall corner points (only needed at the edges of the global simulation). * It is important to note that for region X_BORDER_MIN + Y_BOUNDARY_MIN (region 1) * the point which identifies the neighbor is from region X_BOUNDARY_MIN + Y_BORDER_MIN * (region 2). In another words, the data from region 1 needs to be sent to region 2. */ borderPoints[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BOUNDARY_MIN] = new Point[] { new Point(partition.xmin() - 1, partition.ymin()) }; borderPoints[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BOUNDARY_MIN] = new Point[] { new Point(partition.xmax() + 1, partition.ymin()) }; borderPoints[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BOUNDARY_MAX] = new Point[] { new Point(partition.xmin() - 1, partition.ymax()) }; borderPoints[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BOUNDARY_MAX] = new Point[] { new Point(partition.xmax() + 1, partition.ymax()) }; borderPoints[BorderRegions.X_BOUNDARY_MIN + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(partition.xmin(), partition.ymin() - 1) }; borderPoints[BorderRegions.X_BOUNDARY_MIN + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(partition.xmin(), partition.ymax() + 1) }; borderPoints[BorderRegions.X_BOUNDARY_MAX + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(partition.xmax(), partition.ymin() - 1) }; borderPoints[BorderRegions.X_BOUNDARY_MAX + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(partition.xmax(), partition.ymax() + 1) }; /* * Edge points. */ int xmid = partition.xmin() + partition.xsize() / 2; int ymid = partition.ymin() + partition.ysize() / 2; borderPoints[BorderRegions.X_BORDER_MIN + BorderRegions.Y_CENTER] = new Point[] { new Point(partition.xmin() - 1, ymid) }; borderPoints[BorderRegions.X_BORDER_MAX + BorderRegions.Y_CENTER] = new Point[] { new Point(partition.xmax() + 1, ymid) }; borderPoints[BorderRegions.X_CENTER + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(xmid, partition.ymin() - 1) }; borderPoints[BorderRegions.X_CENTER + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(xmid, partition.ymax() + 1) }; } private void initBorderNeighborsDirections(IntBox partition) { // Corner neighbors directions borderNeighborsDirections[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(-1, -1), new Point(0, -1), new Point(-1, 0) }; borderNeighborsDirections[BorderRegions.X_BORDER_MIN + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(-1, +1), new Point(0, +1), new Point(-1, 0) }; borderNeighborsDirections[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(+1, -1), new Point(0, -1), new Point(+1, 0) }; borderNeighborsDirections[BorderRegions.X_BORDER_MAX + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(+1, +1), new Point(0, +1), new Point(+1, 0) }; // Edge neighbors borderNeighborsDirections[BorderRegions.X_BORDER_MIN + BorderRegions.Y_CENTER] = new Point[] { new Point(-1, 0) }; borderNeighborsDirections[BorderRegions.X_BORDER_MAX + BorderRegions.Y_CENTER] = new Point[] { new Point(+1, 0) }; borderNeighborsDirections[BorderRegions.X_CENTER + BorderRegions.Y_BORDER_MIN] = new Point[] { new Point(0, -1) }; borderNeighborsDirections[BorderRegions.X_CENTER + BorderRegions.Y_BORDER_MAX] = new Point[] { new Point(0, +1) }; } private void setHardwallBoundaryNeighbors() { // Map edge neighbors for (int region: BoundaryRegions.EDGE_REGIONS) { Point p = boundaryPoints[region]; int workerID = findNeighborByPoint(p); boundaryNeighbors[region] = workerID; } /* * Map corner regions to neighbors. * If the true corner neighbor does not exist the region should be mapped to * left, right, bottom or top neighbor (if one of them exists; * in the corner of the global simulation area there is no neighbor). */ for (int region: BoundaryRegions.CORNER_REGIONS) { Point p = boundaryPoints[region]; int workerID = findNeighborByPoint(p); if (workerID == NO_NEIGHBOR) { workerID = findNeighborAroundPoint(p); } boundaryNeighbors[region] = workerID; } } private void setHardwallBorderNeighbors() { // Map edge neighbors for (int region: BorderRegions.EDGE_REGIONS) { Point[] points = borderPoints[region]; assert points.length == 1; int workerID = findNeighborByPoint(points[0]); borderNeighbors[region] = new int[] {workerID}; if (workerID == NO_NEIGHBOR) { borderNeighborsDirections[region] = new Point[] {}; } } // Map corner neighbors for (int region: BorderRegions.CORNER_REGIONS) { Point[] points = borderPoints[region]; assert points.length == 3; int[] allNeighbors = new int[points.length]; for (int i = 0; i < points.length; ++i) { int workerID = findNeighborByPoint(points[i]); allNeighbors[i] = workerID; } borderNeighbors[region] = allNeighbors; // If there is no true corner neighbor we also need to map the border regions // lying outside of the simulation area. // If there is a true corner we map the outside corner regions to NO_NEIGHBOR. for (int i = 1; i < points.length; ++i) { int outsideRegion = findOutsideBorderRegionByPoint(points[i]); borderNeighbors[outsideRegion] = new int[] {NO_NEIGHBOR}; } if (allNeighbors[0] == NO_NEIGHBOR) { for (int i = 1; i < points.length; ++i) { if (allNeighbors[i] != NO_NEIGHBOR) { int outsideRegion = findOutsideBorderRegionByPoint(points[i]); borderNeighbors[outsideRegion][0] = allNeighbors[i]; } } } } } private void setPeriodicBoundaryNeighbors() { for (int region: BoundaryRegions.EDGE_REGIONS) { setSinglePeriodicBoundaryNeighbor(region); } for (int region: BoundaryRegions.CORNER_REGIONS) { setSinglePeriodicBoundaryNeighbor(region); } } private void setSinglePeriodicBoundaryNeighbor(int region) { Point p = boundaryPoints[region]; applyPeriodicBoundary(p); int workerID = findNeighborByPoint(p); assert workerID != NO_NEIGHBOR; boundaryNeighbors[region] = workerID; } private void setPeriodicBorderNeighbors() { for (int region: BorderRegions.EDGE_REGIONS) { setSinglePeriodicBorderNeighbor(region); } for (int region: BorderRegions.CORNER_REGIONS) { setSinglePeriodicBorderNeighbor(region); } // Outside corner regions are not used under periodic boundaries for (int region: BorderRegions.OUTSIDE_CORNER_REGIONS) { borderNeighbors[region] = new int[] {NO_NEIGHBOR}; } } private void setSinglePeriodicBorderNeighbor(int region) { Point[] points = borderPoints[region]; int[] allNeighbors = new int[points.length]; for (int i = 0; i < points.length; ++i) { applyPeriodicBoundary(points[i]); int workerID = findNeighborByPoint(points[i]); assert workerID != NO_NEIGHBOR; allNeighbors[i] = workerID; } borderNeighbors[region] = allNeighbors; } private int findOutsideBorderRegionByPoint(Point point) { for (int region: BorderRegions.OUTSIDE_CORNER_REGIONS) { for (Point p: borderPoints[region]) { if (p.equals(point)) { return region; } } } throw new RuntimeException("Could not find outside corner border region for point: " + point); } private int findNeighborAroundPoint(Point p) { for (Point sp: getSurroundingPoints(p)) { int workerID = findNeighborByPoint(sp); if (workerID != NO_NEIGHBOR) { return workerID; } } return NO_NEIGHBOR; } private Point[] getSurroundingPoints(Point p) { return new Point[] { new Point(p.x - 1, p.y), new Point(p.x + 1, p.y), new Point(p.x, p.y - 1), new Point(p.x, p.y + 1) }; } private int findNeighborByPoint(Point p) { for (int i = 0; i < partitions.length; ++i) { if (partitions[i].contains(p.x, p.y)) { return i; } } return NO_NEIGHBOR; } private Point getNeighborDirection(int neighbor) { if (neighbor == NO_NEIGHBOR) { return null; } IntBox myPart = partitions[thisWorkerID]; IntBox neighborPart = partitions[neighbor]; int xdirec = 0; int ydirec = 0; if (myPart.xmax() < neighborPart.xmin()) { xdirec = +1; } else if (neighborPart.xmax() < myPart.xmin()) { xdirec = -1; } if (myPart.ymax() < neighborPart.ymin()) { ydirec = +1; } else if (neighborPart.ymax() < myPart.ymin()) { ydirec = -1; } return new Point(xdirec, ydirec); } private void applyPeriodicBoundary(Point p) { if (p.x < globalSimArea.xmin()) { p.x += globalSimArea.xsize(); } else if (p.x > globalSimArea.xmax()) { p.x -= globalSimArea.xsize(); } if (p.y < globalSimArea.ymin()) { p.y += globalSimArea.ysize(); } else if (p.y > globalSimArea.ymax()) { p.y -= globalSimArea.ysize(); } } }