package org.openpixi.pixi.physics.grid; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; import org.openpixi.pixi.parallel.cellaccess.CellAction; import org.openpixi.pixi.parallel.cellaccess.CellIterator; import org.openpixi.pixi.physics.Settings; import org.openpixi.pixi.physics.fields.FieldSolver; public class Grid { /* * TODO remove the accessors for individual cell fields and call directly the accessors on the cell * TODO extract field solver to simulation * - makes grid simpler * - makes all the important initialization to happen at one place (in simulation) */ /** * The purpose of the extra cells is twofold. 1) They represent the * boundaries around the simulation area cells. The boundaries assure that * we always have a cell to interpolate to. For example, the hardwall * particle boundaries allow the particle to be outside of the simulation * area. That means that the particle can be in a cell [numCellsX, * numCellsY]. If the particle is in this cell, it will be interpolated to * the four surrounding cells from [numCellsX, numCellsY] to [numCellsX+1, * numCellsY+1]. Hence, the two extra cells after the grid's end. However, * at the beginning we only need one extra cell as the particle in cell * [-1,-1] interpolates to cells from [-1,-1] to [0,0]. * * 2) In the field solver we usually have to use left, right, top and bottom * neighboring cell to update the value of the current cell. Without the * extra cells we would have to treat each side separately in order not to * get IndexOutOfBounds exception. With the extra cells we can comfortably * iterate through the entire grid in a uniform way using the 0 values of * extra cells when calculating the fields at the sides. */ public static final int INTERPOLATION_RADIUS = 1; public static final int HARDWALL_SAFETY_CELLS = 1; //Check comment above, but due to the CIC to particle interpolator //we actually need two cells at the beginning. But for an other reason than at the end. public static final int EXTRA_CELLS_BEFORE_GRID = INTERPOLATION_RADIUS + HARDWALL_SAFETY_CELLS - 1 + 1; public static final int EXTRA_CELLS_AFTER_GRID = INTERPOLATION_RADIUS + HARDWALL_SAFETY_CELLS; /** * solver algorithm for the maxwell equations */ private FieldSolver fsolver; private GridBoundaryType boundaryType; private CellIterator cellIterator; private ResetChargeAction resetCharge = new ResetChargeAction(); private ResetCurrentAction resetCurrent = new ResetCurrentAction(); private StoreFieldsAction storeFields = new StoreFieldsAction(); private Cell[][] cells; public int[][] parallelBoundaries; public int[] parallelBoundariesArray; private int mark = 1; /** * number of cells in x direction */ private int numCellsX; /** * number of cells in x direction */ private int numCellsY; /** * width of each cell */ private double cellWidth; /** * height of each cell */ private double cellHeight; public FieldSolver getFsolver() { return fsolver; } public void setFsolver(FieldSolver fsolver) { this.fsolver = fsolver; } public double getJx(int x, int y) { return cells[index(x)][index(y)].getJx(); } public void addJx(int x, int y, double value) { cells[index(x)][index(y)].addJx(value); } public double getJy(int x, int y) { return cells[index(x)][index(y)].getJy(); } public void addJy(int x, int y, double value) { cells[index(x)][index(y)].addJy(value); } public double getRho(int x, int y) { return cells[index(x)][index(y)].getRho(); } public void setRho(int x, int y, double value) { cells[index(x)][index(y)].setRho(value); } public void addRho(int x, int y, double value) { cells[index(x)][index(y)].addRho(value); } public double getPhi(int x, int y) { return cells[index(x)][index(y)].getPhi(); } public void setPhi(int x, int y, double value) { cells[index(x)][index(y)].setPhi(value); } public double getEx(int x, int y) { return cells[index(x)][index(y)].getEx(); } public void setEx(int x, int y, double value) { cells[index(x)][index(y)].setEx(value); } public double getExo(int x, int y) { return cells[index(x)][index(y)].getExo(); } public void setExo(int x, int y, double value) { cells[index(x)][index(y)].setExo(value); } public void addEx(int x, int y, double value) { cells[index(x)][index(y)].setEx(cells[index(x)][index(y)].getEx() + value); } public double getEy(int x, int y) { return cells[index(x)][index(y)].getEy(); } public void setEy(int x, int y, double value) { cells[index(x)][index(y)].setEy(value); } public double getEyo(int x, int y) { return cells[index(x)][index(y)].getEyo(); } public void setEyo(int x, int y, double value) { cells[index(x)][index(y)].setEyo(value); } public void addEy(int x, int y, double value) { cells[index(x)][index(y)].setEy(cells[index(x)][index(y)].getEy() + value); } public double getBz(int x, int y) { return cells[index(x)][index(y)].getBz(); } public void setBz(int x, int y, double value) { cells[index(x)][index(y)].setBz(value); } public void addBz(int x, int y, double value) { cells[index(x)][index(y)].setBz(cells[index(x)][index(y)].getBz() + value); } public double getBzo(int x, int y) { return cells[index(x)][index(y)].getBzo(); } public void setBzo(int x, int y, double value) { cells[index(x)][index(y)].setBzo(value); } public int getNumCellsX() { return numCellsX; } public int getNumCellsY() { return numCellsY; } public double getCellWidth() { return cellWidth; } public double getCellHeight() { return cellHeight; } public Cell getCell(int x, int y) { return cells[index(x)][index(y)]; } public Cell[][] getCells() { return cells; } public Grid(Settings settings) { this.boundaryType = settings.getGridBoundary(); set(settings.getGridCellsX(), settings.getGridCellsY(), settings.getSimulationWidth(), settings.getSimulationHeight()); this.fsolver = settings.getGridSolver(); this.fsolver.initializeIterator(settings.getCellIterator(), numCellsX, numCellsY); this.cellIterator = settings.getCellIterator(); this.cellIterator.setExtraCellsMode(numCellsX, numCellsY); } /** * In the distributed version we want to create the grid from cells which * come from master; hence, this constructor. Creates grid from the given * cells. The input cells have to contain also the boundary cells. */ public Grid(Settings settings, Cell[][] cells) { this.numCellsX = cells.length - EXTRA_CELLS_BEFORE_GRID - EXTRA_CELLS_AFTER_GRID; this.numCellsY = cells[0].length - EXTRA_CELLS_BEFORE_GRID - EXTRA_CELLS_AFTER_GRID; this.cellWidth = settings.getSimulationWidth() / numCellsX; this.cellHeight = settings.getSimulationHeight() / numCellsY; this.cells = cells; /* * Grid and FieldSolver must have each its own cell iterator! * They use different modes of iteration. * While the grid iterates also over the extra cells the field solver does not. */ this.fsolver = settings.getGridSolver(); fsolver.initializeIterator(settings.getCellIterator(), numCellsX, numCellsY); this.cellIterator = settings.getCellIterator(); this.cellIterator.setExtraCellsMode(this.numCellsX, this.numCellsY); } /** * Change the size of the field. TODO make sure the method can not be called * in distributed version E.g. throw an exception if this is distributed * version */ public void changeSize(int numCellsX, int numCellsY, double simWidth, double simHeight) { set(numCellsX, numCellsY, simWidth, simHeight); fsolver.changeSize(numCellsX, numCellsY); cellIterator.setExtraCellsMode(this.numCellsX, this.numCellsY); } /** * This method is dangerous as it would not work in distributed version. * TODO make sure the method can not be called in distributed version E.g. * throw an exception if this is distributed version */ private void set(int numCellsX, int numCellsY, double simWidth, double simHeight) { this.numCellsX = numCellsX; this.numCellsY = numCellsY; this.cellWidth = simWidth / numCellsX; this.cellHeight = simHeight / numCellsY; createGridWithBoundaries(); } private void createGridWithBoundaries() { cells = new Cell[getNumCellsXTotal()][getNumCellsYTotal()]; parallelBoundaries = new int[getNumCellsXTotal()][getNumCellsYTotal()]; parallelBoundariesArray = new int[getNumCellsXTotal() * getNumCellsYTotal()]; // Create inner cells for (int x = 0; x < getNumCellsX(); x++) { for (int y = 0; y < getNumCellsY(); y++) { cells[index(x)][index(y)] = new Cell(); } } for (int x = 0; x < getNumCellsXTotal(); x++) { for (int y = 0; y < getNumCellsYTotal(); y++) { parallelBoundaries[x][y] = 0; } } createBoundaryCells(); createParallelBoundary(); } private void createBoundaryCells() { // left boundary (with corner cells) for (int x = 0; x < EXTRA_CELLS_BEFORE_GRID; x++) { for (int y = 0; y < getNumCellsYTotal(); y++) { createBoundaryCell(x, y); } } // right boundary (with corner cells) for (int x = EXTRA_CELLS_BEFORE_GRID + numCellsX; x < getNumCellsXTotal(); x++) { for (int y = 0; y < getNumCellsYTotal(); y++) { createBoundaryCell(x, y); } } // top boundary (without corner cells) for (int x = EXTRA_CELLS_BEFORE_GRID; x < EXTRA_CELLS_BEFORE_GRID + numCellsX; x++) { for (int y = 0; y < EXTRA_CELLS_BEFORE_GRID; y++) { createBoundaryCell(x, y); } } // bottom boundary (without corner cells) for (int x = EXTRA_CELLS_BEFORE_GRID; x < EXTRA_CELLS_BEFORE_GRID + numCellsX; x++) { for (int y = EXTRA_CELLS_BEFORE_GRID + numCellsY; y < getNumCellsYTotal(); y++) { createBoundaryCell(x, y); } } } /** * Based on the boundary type creates the boundary cell. */ private void createBoundaryCell(int x, int y) { if (boundaryType == GridBoundaryType.Hardwall) { cells[x][y] = new Cell(); } else if (boundaryType == GridBoundaryType.Periodic) { int xmin = EXTRA_CELLS_BEFORE_GRID; int xmax = numCellsX + EXTRA_CELLS_BEFORE_GRID - 1; int ymin = EXTRA_CELLS_BEFORE_GRID; int ymax = numCellsY + EXTRA_CELLS_BEFORE_GRID - 1; int refX = x; int refY = y; if (x < xmin) { refX += numCellsX; } else if (x > xmax) { refX -= numCellsX; } if (y < ymin) { refY += numCellsY; } else if (y > ymax) { refY -= numCellsY; } cells[x][y] = cells[refX][refY]; } } public void createParallelBoundary() { int mark = 1; /* * Find the cells that share the same reference and mark them. * If cells[i][j]==cells[a][b] then mark[i][j]==mark[a][b] */ for (int i = 0; i < getNumCellsXTotal(); i++) { for (int j = 0; j < getNumCellsYTotal(); j++) { mark++; for (int l = 0; l < getNumCellsXTotal(); l++) { for (int m = 0; m < getNumCellsYTotal(); m++) { if (cells[i][j] == cells[l][m] && parallelBoundaries[l][m] == 0) { parallelBoundaries[l][m] = mark; } } } } } /* * Convert the matrix to an array */ int k = 0; for (int i = 0; i < getNumCellsXTotal(); i++) { for (int j = 0; j < getNumCellsYTotal(); j++) { parallelBoundariesArray[k++] = parallelBoundaries[i][j]; } } /* * Create a HashMap where the key is the mark and the value is * a list containing indexes of all the cells that have that mark. */ HashMap<Integer, Vector<Integer>> marks = new HashMap<Integer, Vector<Integer>>(); Vector<Integer> indexes; for (int i = 0; i < getNumCellsXTotal() * getNumCellsYTotal(); i++) { mark = parallelBoundariesArray[i]; if (!marks.containsKey(mark)) { indexes = new Vector<Integer>(); indexes.add(i); marks.put(mark, indexes); } else { indexes = marks.get(mark); indexes.add(i); marks.put(mark, indexes); } } /* * Create circular lists so a cell will point to the next cell * that has the same mark */ int index1, index2; for (int i = 0; i < getNumCellsXTotal(); i++) { for (int j = 0; j < getNumCellsYTotal(); j++) { mark = parallelBoundaries[i][j]; //get a mark indexes = marks.get(mark); //get the indexes of all the cells that share the mark for (int l = 0; l < indexes.size(); l++) { index1 = indexes.elementAt(l); if (l == indexes.size() - 1) { //the last cell will point to the first one parallelBoundariesArray[index1] = indexes.elementAt(0); } else { //every other cell will point to the next cell in the list index2 = indexes.elementAt(l + 1); parallelBoundariesArray[index1] = index2; } } } } } public void updateGrid(double tstep) { storeFields(); getFsolver().step(this, tstep); } public void resetCurrent() { cellIterator.execute(this, resetCurrent); } public void resetCharge() { cellIterator.execute(this, resetCharge); } public void storeFields() { cellIterator.execute(this, storeFields); } /** * Maps the client index which can be negative to the real array index which * has to be non-negative. The client index can be negative if the client is * asking for a cell which is within the top or left boundary. (By client we * mean any code which is using this class) * */ private int index(int clientIdx) { return EXTRA_CELLS_BEFORE_GRID + clientIdx; } /** * Includes the extra cells. */ public int getNumCellsXTotal() { return numCellsX + EXTRA_CELLS_BEFORE_GRID + EXTRA_CELLS_AFTER_GRID; } /** * Includes the extra cells. */ public int getNumCellsYTotal() { return numCellsY + EXTRA_CELLS_BEFORE_GRID + EXTRA_CELLS_AFTER_GRID; } private class ResetCurrentAction implements CellAction { public void execute(Grid grid, int x, int y) { grid.getCell(x, y).resetCurrent(); } } private class ResetChargeAction implements CellAction { public void execute(Grid grid, int x, int y) { grid.getCell(x, y).resetCharge(); } } private class StoreFieldsAction implements CellAction { public void execute(Grid grid, int x, int y) { grid.getCell(x, y).storeFields(); } } }