package org.openpixi.pixi.physics.grid; 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; /** * 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()]; // Create inner cells for (int x = 0; x < getNumCellsX(); x++) { for (int y = 0; y < getNumCellsY(); y++) { cells[index(x)][index(y)] = new Cell(); } } createBoundaryCells(); } 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 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(); } } }