package org.openpixi.pixi.physics.grid; import java.util.ArrayList; import java.util.Random; import junit.framework.TestCase; import org.openpixi.pixi.physics.Settings; import org.openpixi.pixi.physics.Simulation; import org.openpixi.pixi.physics.fields.SimpleSolver; import org.openpixi.pixi.physics.force.ConstantForce; import org.openpixi.pixi.physics.particles.Particle; import org.openpixi.pixi.physics.particles.ParticleFull; /** * Unit test for Solver. */ public class CloudInCellTest extends TestCase { boolean VERBOSE = false; Random random = new Random(); //double ACCURACY_LIMIT = 1.e-16; double ACCURACY_LIMIT = 1.e-14; void assertAlmostEquals(String text, double x, double y, double limit) { if ((Math.abs(x - y) / Math.abs(x + y) > limit) || (Double.isNaN(x) != Double.isNaN(y)) || (Double.isInfinite(x) != Double.isInfinite(y))) { assertTrue(text + " expected:<" + x + "> but was:<" + y + ">", false); } } /** * Create the test case * * @param testName * name of the test case */ public CloudInCellTest(String testName) { super(testName); } public void testFourBoundaryMovesSimple() { // Positive charge for (int charge = -1; charge <=1; charge++){ //bottom up testMoveSimple(4.8, 4.8, 4.8, 5.2, charge, "four boundary: x=const"); testMoveSimple(5.3, 5.2, 5.3, 4.8, charge, "four boundary: x=const"); //left to right testMoveSimple(4.8, 5.3, 5.2, 5.3, charge, "four boundary: left - right y=const"); testMoveSimple(4.8, 4.8, 5.2, 4.8, charge, "four boundary: left - right y=const"); //from top left to down right testMoveSimple(4.9, 5.4, 5.4, 4.6, charge, "four boundary: top left - down right "); //from down left to top right testMoveSimple(4.7, 4.6, 5.4, 5.3, charge, "four boundary: down left - top right "); //special moves testMoveSimple(4.6, 4.5, 5.4, 4.5, charge, "four boundary: middle of cell"); testMoveSimple(4.6, 5.0, 5.4, 5.0, charge, "four boundary: on teh edge" ); } } public void testFourBoundaryMoves() { // Positive charge for (int charge = -1; charge <=1; charge++){ //bottom up testMove(4.8, 4.8, 4.8, 5.2, charge, "four boundary: x=const"); testMove(5.3, 5.2, 5.3, 4.8, charge, "four boundary: x=const"); //left to right testMove(4.8, 5.3, 5.2, 5.3, charge, "four boundary: left - right y=const"); testMove(4.8, 4.8, 5.2, 4.8, charge, "four boundary: left - right y=const"); //from top left to down right testMove(4.9, 5.4, 5.4, 4.6, charge, "four boundary: top left - down right "); //from down left to top right testMove(4.7, 4.6, 5.4, 5.3, charge, "four boundary: down left - top right "); //special moves testMove(4.6, 4.5, 5.4, 4.5, charge, "four boundary: middle of cell"); testMove(4.6, 5.0, 5.4, 5.0, charge, "four boundary: on teh edge" ); } } public void testSevenBoundaryMoves() { testMove(3.3, 5.2, 3.6, 5.4, +1, "seven boundary, move right"); testMove(5.7, 5.2, 5.3, 5.4, +1, "seven boundary, move left"); testMove(5.3, 5.2, 5.2, 5.8, +1, "seven boundary, move up"); testMove(5.7, 7.6, 5.6, 7.4, +1, "seven boundary, move down"); } public void testTenBoundaryMoves() { testMove(5.7, 5.8, 5.3, 5.2, +1, "ten boundary"); } public void testRandomMoves() { for (int i = 0; i < 1000; i++) { double x1 = 2 + 6 * Math.random(); double y1 = 2 + 6 * Math.random(); double phi = 2 * Math.PI * Math.random(); double distance = 1 * Math.random(); double x2 = x1 + distance * Math.cos(phi); double y2 = y1 + distance * Math.sin(phi); testMove(x1, y1, x2, y2, +1, "random boundary " + i); } } private void testMoveSimple(double x1, double y1, double x2, double y2, double charge, String text) { Settings stt = GridTestCommon.getCommonSettings(); Grid grid = new Grid(stt); //We iterate over all the grid cells manually to avoid dependence of this //test on the cellIterator implementation. But using the grid method //grid.resetCharge() should also work! for (int x = -grid.EXTRA_CELLS_BEFORE_GRID; x < (grid.getNumCellsX()+grid.EXTRA_CELLS_AFTER_GRID); x++) { for (int y = -grid.EXTRA_CELLS_BEFORE_GRID; y < (grid.getNumCellsY()+grid.EXTRA_CELLS_AFTER_GRID); y++) { grid.getCell(x, y).resetCurrent(); } } Particle p = new ParticleFull(); p.setX(x1); p.setY(y1); p.setVx((x2 - x1) / stt.getTimeStep()); p.setVy((y2 - y1) / stt.getTimeStep()); p.setCharge(charge); InterpolatorAlgorithm interpolation = new CloudInCell(); interpolation.interpolateToGrid(p, grid, stt.getTimeStep()); double jx = GridTestCommon.getJxSum(grid); double jy = GridTestCommon.getJySum(grid); assertAlmostEquals(text + ", jx", charge * p.getVx(), jx, ACCURACY_LIMIT); assertAlmostEquals(text + ", jy", charge * p.getVy(), jy, ACCURACY_LIMIT); } private void testMove(double x1, double y1, double x2, double y2, double charge, String text) { Settings stt = GridTestCommon.getCommonSettings(); stt.setInterpolator(new CloudInCell()); stt.setGridSolver(new SimpleSolver()); // Add single particle Particle p = new ParticleFull(); p.setX(x1); p.setY(y1); p.setVx((x2 - x1) / stt.getTimeStep()); p.setVy((y2 - y1) / stt.getTimeStep()); p.setMass(1); p.setCharge(charge); stt.addParticle(p); Simulation s = new Simulation(stt); s.prepareAllParticles(); // The simulation always creates its own copy of particles // (in fact the setting class does so) // and we would like to obtain the reference to our initial particle p. p = s.particles.get(0); // Advance particle s.particlePush(); //Remember old values after boundary check double sx = p.getPrevX(); double sy = p.getPrevY(); // Calculate current s.getInterpolation().interpolateToGrid(s.particles, s.grid, s.tstep); double jx = GridTestCommon.getJxSum(s.grid); double jy = GridTestCommon.getJySum(s.grid); if (VERBOSE) System.out.println("Total current " + text + ": jx = " + jx + ", jy = " + jy + " (from " + sx + ", " + sy + " to " + p.getX() + ", " + p.getY() + ")"); GridTestCommon.checkSignJx(s.grid); GridTestCommon.checkSignJy(s.grid); // This is what ChargeConservingAreaWeightningTest test for (current during timestep) // assertAlmostEquals(text + ", jx", charge * (p.x - sx), jx, ACCURACY_LIMIT); // assertAlmostEquals(text + ", jy", charge * (p.y - sy), jy, ACCURACY_LIMIT); // This is what is appropriate for CIC: momentary current assertAlmostEquals(text + ", jx", charge * p.getVx(), jx, ACCURACY_LIMIT); assertAlmostEquals(text + ", jy", charge * p.getVy(), jy, ACCURACY_LIMIT); } public void testFourBoundtatryMovesForce() { // Positive charge //bottom up int charge = 1; testMoveForce(4.8, 4.8, 0.2, 0, -1, 1, charge, "four boundary: x=const"); } public void testRandomMovesForce() { for (int i = 0; i < 1000; i++) { double x1 = 2 + 6 * Math.random(); double y1 = 2 + 6 * Math.random(); double phi = 2 * Math.PI * Math.random(); double distance = 0.8 * Math.random(); double vx = distance * Math.cos(phi); double vy = distance * Math.sin(phi); testMoveForce(x1, y1, vx, vy, 0.5*Math.random(), 0.5*Math.random(), +1, "random boundary " + i); } } private void testMoveForce(double x1, double y1, double vx, double vy, double ex, double bz, double charge, String text) { Settings stt = GridTestCommon.getCommonSettings(); stt.setInterpolator(new CloudInCell()); stt.setGridSolver(new SimpleSolver()); // Add single particle Particle p = new ParticleFull(); p.setX(x1); p.setY(y1); p.setVx(vx); p.setVy(vy); p.setMass(1); p.setCharge(charge); stt.addParticle(p); ConstantForce force = new ConstantForce(); force.ex = ex; force.bz = bz; stt.addForce(force); Simulation s = new Simulation(stt); s.prepareAllParticles(); // Advance particle s.particlePush(); // The simulation always creates its own copy of particles // (in fact the setting class does so) // and we would like to obtain the reference to our initial particle p. p = s.particles.get(0); //Remember old values after boundary check double sx = p.getPrevX(); double sy = p.getPrevY(); // Calculate current s.getInterpolation().interpolateToGrid(s.particles, s.grid, s.tstep); double jx = GridTestCommon.getJxSum(s.grid); double jy = GridTestCommon.getJySum(s.grid); if (VERBOSE) System.out.println("Total current " + text + ": jx = " + jx + ", jy = " + jy + " (from " + sx + ", " + sy + " to " + p.getX() + ", " + p.getY() + ")"); GridTestCommon.checkSignJx(s.grid); GridTestCommon.checkSignJy(s.grid); // This is what ChargeConservingAreaWeightningTest test for (current during timestep) // assertAlmostEquals(text + ", jx", charge * (p.x - sx), jx, ACCURACY_LIMIT); // assertAlmostEquals(text + ", jy", charge * (p.y - sy), jy, ACCURACY_LIMIT); // This is what is appropriate for CIC: momentary current assertAlmostEquals(text + ", jx", charge * p.getVx(), jx, ACCURACY_LIMIT); assertAlmostEquals(text + ", jy", charge * p.getVy(), jy, ACCURACY_LIMIT); } }