package org.openpixi.pixi.distributed.partitioning; import org.openpixi.pixi.physics.util.IntBox; import java.util.ArrayList; import java.util.List; /** * Basic simulation area partitioner. * Works only under following conditions. * 1) numCellsX, numCellsY and numPartitions are all powers of 2. * 2) numPartitions <= numCellsX * numCellsY * * The algorithm works as follows. * We have list of areas (boxes) which we need to split (initially 1 area/box). * - In each iteration we take all the boxes and split them into half at the larger side. * - We repeat the previous step until we have the specified number of boxes. */ public class SimplePartitioner implements Partitioner { private IntBox[] partitions; private int numCellsX; private int numCellsY; public IntBox[] partition(int numCellsX, int numCellsY, int numPartitions) { this.numCellsX = numCellsX; this.numCellsY = numCellsY; assert numCellsX > 0; assert numCellsY > 0; if (!isPower2(numCellsX) || !isPower2(numCellsY) || !isPower2(numPartitions)) { throw new RuntimeException("Number of cells in x and y direction " + "as well as the number of partitions must be power of 2!"); } if (numPartitions > numCellsX * numCellsY) { throw new RuntimeException( "Number of partitions must be less or equal to the number of cells!"); } List<IntBox> partitions = new ArrayList<IntBox>(); partitions.add(new IntBox(0, numCellsX - 1, 0, numCellsY - 1)); while (partitions.size() < numPartitions) { partitions = splitBoxes(partitions); } this.partitions = partitions.toArray(new IntBox[0]); return this.partitions; } private List<IntBox> splitBoxes(List<IntBox> partitions) { List<IntBox> newPartitions = new ArrayList<IntBox>(); for (IntBox b: partitions) { if (b.xsize() > b.ysize()) { // Split along x axis int xmid = (b.xmin() + b.xsize() / 2); newPartitions.add(new IntBox(b.xmin(), xmid - 1, b.ymin(), b.ymax())); newPartitions.add(new IntBox(xmid, b.xmax(), b.ymin(), b.ymax())); } else { // Split along y axis int ymid = (b.ymin() + b.ysize() / 2); newPartitions.add(new IntBox(b.xmin(), b.xmax(), b.ymin(), ymid - 1)); newPartitions.add(new IntBox(b.xmin(), b.xmax(), ymid, b.ymax())); } } return newPartitions; } private boolean isPower2(int number) { if ((number & (number - 1)) == 0) { return true; } else { return false; } } @Override public String toString() { StringBuilder retval = new StringBuilder(); IntBox first = findFirst(); IntBox nextY = first; while (nextY != null) { IntBox nextX = nextY; while (nextX != null) { int index = getIndex(nextX); retval.append(index + " "); nextX = findNextX(nextX); } retval.append("\n"); nextY = findNextY(nextY); } return retval.toString().trim(); } private int getIndex(IntBox partition) { for (int i = 0; i < partitions.length; ++i) { if (partitions[i] == partition) { return i; } } throw new RuntimeException("Partition was not found!"); } private IntBox findNextX(IntBox current) { for (IntBox part: partitions) { if (part.xmin() == current.xmax() + 1 && part.ymin() == current.ymin()) { return part; } } return null; } private IntBox findNextY(IntBox current) { for (IntBox part: partitions) { if (part.ymin() == current.ymax() + 1 && part.xmin() == current.xmin()) { return part; } } return null; } private IntBox findFirst() { for (IntBox part: partitions) { if (part.xmin() == 0 && part.ymin() == 0) { return part; } } throw new RuntimeException("Could not find the first partition!"); } }