/*Siddhartha Kasivajhula * Georgia Institute of Technology * c/o Predrag Cvitanovic * Advisor: Rytis Paskauskas * Fall 2005 */ //import cs1.Keyboard; /* Revisions: 1. Substituted Math.round() for simple int.*/ /*Fixes: (12/01/2005) The time now resets after each traversal. This should correct precision error. To implement this change, added t=0; in reset(totalTime) in class AFM In getMore(), changed afm.setX(0, SAMPS-1) to afm.setX(0, SAMPS-1)-Surface.L Finally, reset t0, x0, y0, px0, py0 (reference values) at the beginning of each traversal. */ //! @brief This class represents the entire system. This is the brain behind the cool graphics. /// This class brings the AFM and the surface together, looks for collisions, causes AFM to recalculate its trajectory /// in the event of a collision ("bounce"). Newton's bisection algorithm is used to find the precise point of intersection /// of surface and AFM. public class AFMSimulator { private Surface surface; //!< The surface private AFM afm; //!< The AFM private final double SURF_TOLERANCE = 1.e-11; //!< minimum precision for bisection static final int STROBE_OFFSET = 0; //!< formerly used to iterate a few times before using any data. not used anymore. Scheduled for removal. private double totalTime; //!< time for 1 traversal of the surface private double roots[][]; //!< points of intersection on this traversal private int num_roots = 0; //!< number of points of intersection on this traversal (simply roots.length won't work because array is not purged after each traversal) private boolean dragging = false; //!< this is a flag. Signals all concerned that there is some dragging going on. private final int DRAGGING_THRESHOLD = 100; //!< if there are many points of intersection on any single traversal, the 'dragging' flag goes up /** * Constructor. Sets up the Simulator engine. */ public AFMSimulator() { surface = new Surface(); afm = new AFM(); roots = new double[1000][10]; }//end constructor public void resetSim() { if (afm.getY(0) < surface.gimmeY(afm.getX(0))) afm.setY(0, surface.gimmeY(afm.getX(0))); totalTime = Surface.L / afm.getV(); iterate(); }// end resetSim() public void getMore() // this method is called by the graphics applet. It generates (1000) new values { // for the AFM, i.e. another single traversal of the surface. afm.setX(0, afm.getX(AFMConstants.SAMPLES - 1)-Surface.L); // x = x-L afm.setY(0, afm.getY(AFMConstants.SAMPLES - 1)); // y = y afm.setPX(0, afm.getPX(AFMConstants.SAMPLES - 1)); // px = px afm.setPY(0, afm.getPY(AFMConstants.SAMPLES - 1)); // py = py afm.setT(0, afm.getT(AFMConstants.SAMPLES - 1)); afm.setTime(0, afm.getTime(AFMConstants.SAMPLES - 1)); iterate(); } public void iterate() { double err, to, tn, yo, yn, t; afm.reset(totalTime); // passes total time to AFM double t0 = afm.getT(0); double x0 = afm.getX(0); double y0 = afm.getY(0); double px0 = afm.getPX(0); double py0 = afm.getPY(0); num_roots = 0; dragging = false; for (int i = 0; i < AFMConstants.SAMPLES; i++) { int hmm = 0; if (afm.getY(i) < surface.gimmeY(afm.getX(i))) { // implementing bisection (numerical newton) method to find surface and trajectory intersection. if (i > 0) to = afm.getT(i - 1); else to = afm.getT(i) - afm.getTimeStep(); //Warning: not on surface tn = afm.getT(i); yo = afm.gimmeY(to, t0, x0, y0, px0, py0) - surface.gimmeY(afm.gimmeX(to, t0, x0, y0, px0, py0)); yn = afm.gimmeY(tn, t0, x0, y0, px0, py0) - surface.gimmeY(afm.gimmeX(tn, t0, x0, y0, px0, py0)); do { if (to == tn) { t = to; break; } t = -1 * (tn * yo - to * yn) / (yn - yo); err = afm.gimmeY(t, t0, x0, y0, px0, py0) - surface.gimmeY(afm.gimmeX(t, t0, x0, y0, px0, py0)); // y(t) - surfaceY(x(t)) maybe some parameters too??? if (err * yn > 0) { yn = err; tn = t; } else { yo = err; to = t; } /* System.out.println("bisection error = " + err + "\n");*/ hmm++; if (hmm > 20) { System.out.println("An infinite amount of work"); break; } } while (Math.abs(err) > SURF_TOLERANCE); // set the arrays from this point (t). afm.setY(i, surface.gimmeY(afm.gimmeX(t, t0, x0, y0, px0, py0))); // bring AFM to surface at intersection point afm.setX(i, afm.gimmeX(t, t0, x0, y0, px0, py0)); // store precise x position in current array index afm.setT(i, t); // store precise time of intersection in current index afm.reset(i, surface.getNX(afm.getX(i)), surface.getNY(afm.getX(i)), afm.gimmePX(t, t0, x0, y0, px0, py0), afm.gimmePY(t, t0, x0, y0, px0, py0)); /*iterate AFM equations after point of intersection*/ /* This now becomes the "last"(previous) point of intersection. After reflection */ // AFM is moved to point of intersection, i.e. the // current index now contains the "true" point of // intersection, and the AFM is on the surface at that point /* Even the time at this index is changed to the time of "true" intersection, so that the motion equations /* in AFM.java have the correct values for "t[i]-t[i0]" */ /* the AFM motion equations are iterated from this point with the precise values for /* "incoming" x, y, px, py */ roots[num_roots][5] = t0; // indices 5-9 are old intersection point roots[num_roots][6] = x0; roots[num_roots][7] = px0; roots[num_roots][8] = y0; roots[num_roots][9] = py0; t0 = afm.getT(i); x0 = afm.getX(i); y0 = afm.getY(i); px0 = afm.getPX(i); py0 = afm.getPY(i); // should be -?!?!?! Nope, reflection is handled by AFM reset(params..) method // and now record this as a point of intersection for Poincare' roots[num_roots][0] = t0; // indices 0-4 are new intersection point roots[num_roots][1] = x0; roots[num_roots][2] = px0; roots[num_roots][3] = y0; roots[num_roots][4] = py0; num_roots++; //System.out.println(num_roots); }//end if(below surface) }// end for if (num_roots > DRAGGING_THRESHOLD) dragging = true; }// end iterate() public boolean isDragging() { return dragging; } public void setParams(double t, double a, double x, double y, double px, double py, double equiPos, double V, double e) { /*receives parameter values from GUI and forwards them to afm*/ afm.setT(0, t); afm.setTime(0, t); afm.setRestit(a); // afm.setX(0, x); // set all afm.setY(0, y); // parameter afm.setPX(0, px); // values afm.setPY(0, py); // afm.setEquiPos(equiPos); afm.setV(V); afm.setE(e); } public double[][] getRoots() { return roots; // in life, one must occasionally return to one's roots. } public boolean hasRoots() { return (num_roots > 0); } public int numRoots() { return num_roots; } public AFM getAFM() // returns AFM object { return afm; } public Surface getSurface() //returns Surface object { return surface; } public boolean isOnSurface(double x, double y) { return Math.abs(surface.gimmeY(x) - y) <= 0.01; } public double getTotalTime() { return totalTime; } public static void main(String[] args) // for testing purposes { AFMSimulator afmSim = new AFMSimulator(); double a = AFMConstants.periodic_a; double x = AFMConstants.periodic_x; double y = AFMConstants.periodic_y; double px = AFMConstants.periodic_px; double py = AFMConstants.periodic_py; double y0 = AFMConstants.periodic_y0; double V = AFMConstants.periodic_V; double e = AFMConstants.periodic_e; afmSim.setParams(0, a, x, y, px, py, y0, V, e); afmSim.resetSim(); int iters = 5; //double a = 0.9; double data[][] = new double[5][4]; double good[][] = new double[100][4]; int goodCounter = 0; boolean isGood = true; /* Add some code here to test, if needed */ }//end main() }//end AFMSimulator