package org.openpixi.pixi.physics.fields; import edu.emory.mathcs.jtransforms.fft.*; import org.openpixi.pixi.physics.grid.Grid; public class PoissonSolverFFTPeriodic implements PoissonSolver { /**Solves the electrostatic Poisson equation with FFT assuming periodic boundaries. * * <p>This method should be called every time when new particles * are loaded into the simulation area (i.e. a new charge * distribution is introduced) It calculates the electrostatic * potential caused by this distribution, calculates the electric * fields by applying the negative nabla operator and saves them * in the field variables of the Grid class. Note that periodic * boundaries are assumed by the transformation itself AND by the * derivative of the potential!</p> * @param g Grid on which the calculation should be performed */ public void solve(Grid g) { double eps0 = 1.0/(4*Math.PI); //double eps0 = 1; //size of the array to be transformed int columns = g.getNumCellsX(); int rows = g.getNumCellsY(); double cellArea = g.getCellWidth() * g.getCellHeight(); //JTransform saves the imaginary part as a second row entry //therefore there must be twice as many rows double[][] trho = new double[columns][2*rows]; double[][] phi = new double[columns][2*rows]; DoubleFFT_2D fft = new DoubleFFT_2D(columns, rows); //prepare input for fft for(int i = 0; i < columns; i++) { for(int j = 0; j < rows; j++) { trho[i][2*j] = g.getRho(i,j)/cellArea; trho[i][2*j+1] = 0; } } //perform Fourier transformation fft.complexForward(trho); for(int i = 1; i < columns; i++) { for(int j = 0; j < rows; j++) { double d = (4 - 2 * Math.cos((2 * Math.PI * i) / g.getNumCellsX()) - 2 * Math.cos((2 * Math.PI * j) / g.getNumCellsY())); phi[i][2*j] = (cellArea * trho[i][2*j]) / (d*eps0); phi[i][2*j+1] = (cellArea * trho[i][2*j+1]) / (d*eps0); } } //i=0 but j!=0 for(int j = 1; j < rows; j++) { double d = (2 - 2 * Math.cos((2 * Math.PI * j) / g.getNumCellsY())); phi[0][2*j] = (cellArea * trho[0][2*j]) / (d * eps0); phi[0][2*j+1] = (cellArea * trho[0][2*j+1]) / (d * eps0); } phi[0][0] = 0; phi[0][1] = 0; //perform inverse Fourier transform fft.complexInverse(phi, true); //Solve Poisson equation in Fourier space //We omit the term with i,j=0 where d would become 0. This term only contributes a constant term //to the potential and can therefore be chosen arbitrarily. for(int i = 0; i < columns-1; i++) { for(int j = 0; j < rows-1; j++) { //the electric field in x direction is equal to the negative derivative of the //potential in x direction, analogous for y direction //using forward difference, since phi is located in the corner of the grid //and the electric field in the edges of the grid g.setEx(i, j, -(phi[i+1][2*j] - phi[i][2*j]) / (g.getCellWidth())); g.setEy(i, j, -(phi[i][2*(j+1)] - phi[i][2*j]) / (g.getCellHeight())); } } //upper boundary for(int i = 0; i < columns-1; i++) { g.setEx(i, rows-1, -(phi[i+1][2*(rows-1)] - phi[i][2*(rows-1)]) / (g.getCellWidth())); g.setEy(i, rows-1, -(phi[i][0] - phi[i][2*(rows-1)]) / (g.getCellHeight())); } //right boundary for(int j = 0; j < rows-1; j++) { g.setEx(columns-1, j, -(phi[0][2*j] - phi[columns-1][2*j]) / (g.getCellWidth())); g.setEy(columns-1, j, -(phi[columns-1][2*(j+1)] - phi[columns-1][2*j]) / (g.getCellHeight())); } //upper right corner g.setEx(columns-1, rows-1, -(phi[0][2*(rows-1)] - phi[columns-1][2*(rows-1)]) / (g.getCellWidth())); g.setEy(columns-1, rows-1, -(phi[columns-1][0] - phi[columns-1][2*(rows-1)]) / (g.getCellHeight())); //prepare output for(int i = 0; i < columns; i++) { for(int j = 0; j < rows; j++) { g.setPhi(i, j, phi[i][2*j]); } } } }