package org.openpixi.pixi.physics.util; import org.openpixi.pixi.physics.grid.Cell; import org.openpixi.pixi.physics.grid.Grid; import org.openpixi.pixi.physics.particles.Particle; import java.util.List; /** * Compares the results of two simulations. * In case of failure throws ComparisonFailedException. */ public class ResultsComparator { private static final Double TOLERANCE = 1e-10; private static final int NO_STEP_TRACKING = -1; private int stepNo = NO_STEP_TRACKING; public ResultsComparator() { } /** * In case of failure outputs also the step number in which the failure occurred. */ public ResultsComparator(int stepNo) { this.stepNo = stepNo; } public void compare( List<Particle> expectedParticles, List<Particle> actualParticles, Grid expectedGrid, Grid actualGrid) { compareParticleLists(expectedParticles, actualParticles, TOLERANCE); compareGrids(expectedGrid, actualGrid, TOLERANCE); } private void compareParticleLists( List<Particle> expectedParticles, List<Particle> actualParticles, double tolerance) { if (expectedParticles.size() < actualParticles.size()) { fail("There are more actual particles than expected!"); } if (expectedParticles.size() > actualParticles.size()) { fail("There are less actual particles than expected!"); } for (Particle p: expectedParticles) { if (!findParticle(p, actualParticles, tolerance)) { fail("Could not find particle " + p + " in the list of actual particles!"); } } } private boolean findParticle( Particle p, List<Particle> particles, Double tolerance) { boolean retval = false; for (Particle p2: particles) { if (compareParticles(p, p2, tolerance)) { return true; } } return retval; } /** * Compares just the position. */ private boolean compareParticles(Particle p1, Particle p2, Double tolerance) { if (Math.abs(p1.getX() - p2.getX()) > tolerance) { return false; } if (Math.abs(p1.getY() - p2.getY()) > tolerance) { return false; } return true; } private void compareGrids(Grid expectedGrid, Grid actualGrid, double tolerance) { if (expectedGrid.getNumCellsX() < actualGrid.getNumCellsX()) { fail("Actual grid is larger in X direction!"); } if (expectedGrid.getNumCellsX() > actualGrid.getNumCellsX()) { fail("Actual grid is smaller in X direction!"); } if (expectedGrid.getNumCellsY() < actualGrid.getNumCellsY()) { fail("Actual grid is larger in Y direction!"); } if (expectedGrid.getNumCellsY() > actualGrid.getNumCellsY()) { fail("Actual grid is smaller in Y direction!"); } /* * We only compare the cells up till interpolation radius * (ie. we do not compare the last row and column of the cells). * The last row and column would be different in periodic boundaries because * in the non distributed simulation these rows and cells point inside of the grid, * whereas in distributed simulation they are not exchanged since they are not needed * for any interpolation. */ int differences = 0; for (int x = -Grid.INTERPOLATION_RADIUS; x < expectedGrid.getNumCellsX() + Grid.INTERPOLATION_RADIUS; ++x) { for (int y = -Grid.INTERPOLATION_RADIUS; y < expectedGrid.getNumCellsY() + Grid.INTERPOLATION_RADIUS; ++y) { Cell expectedCell = expectedGrid.getCell(x,y); Cell actualCell = actualGrid.getCell(x,y); if (!compareCells(expectedCell, actualCell, tolerance)) { ++differences; System.out.println(" -> differences in cell: " + x + "," + y); } } } if (differences > 0) { fail(String.format("%d cells were different!", differences)); } } private boolean compareCells(Cell cellA, Cell cellB, double tolerance) { boolean ok = true; double difference = Math.abs(cellA.getEx() - cellB.getEx()); if (difference > tolerance) { printSingleMemberDifference("ex", difference); ok = false; } difference = Math.abs(cellA.getEy() - cellB.getEy()); if (difference > tolerance) { printSingleMemberDifference("ey", difference); ok = false; } difference = Math.abs(cellA.getBz() - cellB.getBz()); if (difference > tolerance) { printSingleMemberDifference("bz", difference); ok = false; } difference = Math.abs(cellA.getBzo() - cellB.getBzo()); if (difference > tolerance) { printSingleMemberDifference("bzo", difference); ok = false; } difference = Math.abs(cellA.getJx() - cellB.getJx()); if (difference > tolerance) { printSingleMemberDifference("jx", difference); ok = false; } Math.abs(cellA.getJy() - cellB.getJy()); if (difference > tolerance) { printSingleMemberDifference("jy", difference); ok = false; } return ok; } private void printSingleMemberDifference(String member, double difference) { System.out.print(String.format("%s (%.6f) ", member, difference)); } private void fail(String msg) { StringBuilder finalMsg = new StringBuilder(msg); if (stepNo != NO_STEP_TRACKING) { finalMsg.append(" STEP NUMBER: " + stepNo); } finalMsg.append(", COMPARISON FAILED !!! "); throw new ComparisonFailedException(finalMsg.toString()); } /** * Used in debugging for fields comparison. */ private void printFields(Grid grid) { for (int y = -Grid.EXTRA_CELLS_BEFORE_GRID; y < grid.getNumCellsY() + Grid.EXTRA_CELLS_AFTER_GRID; ++y) { for (int x = -Grid.EXTRA_CELLS_BEFORE_GRID; x < grid.getNumCellsX() + Grid.EXTRA_CELLS_AFTER_GRID; ++x) { System.out.print(String.format("[%9.6f,%9.6f] ", grid.getCell(x, y).getEx(), grid.getCell(x, y).getEy())); } System.out.println(); } } /** * Used in debugging for currents comparison. */ private void printCurrents(Grid grid) { for (int y = -Grid.EXTRA_CELLS_BEFORE_GRID; y < grid.getNumCellsY() + Grid.EXTRA_CELLS_AFTER_GRID; ++y) { for (int x = -Grid.EXTRA_CELLS_BEFORE_GRID; x < grid.getNumCellsX() + Grid.EXTRA_CELLS_AFTER_GRID; ++x) { System.out.print(String.format("[%.6f,%.6f] ", grid.getCell(x, y).getJx(), grid.getCell(x, y).getJy())); } System.out.println(); } } }