/* Simple forward Euler method for velocity integration in time */
 void euler(double &x, double &y, double timestep, const FluidQuantity &u, const FluidQuantity &v) const {
     double uVel = u.lerp(x, y)/_hx;
     double vVel = v.lerp(x, y)/_hx;
     
     x -= uVel*timestep;
     y -= vVel*timestep;
 }
 double maxTimestep() {
     double maxVelocity = 0.0;
     for (int y = 0; y < _h; y++) {
         for (int x = 0; x < _w; x++) {
             double u = _u->lerp(x + 0.5, y + 0.5);
             double v = _v->lerp(x + 0.5, y + 0.5);
             
             double velocity = sqrt(u*u + v*v);
             maxVelocity = max(maxVelocity, velocity);
         }
     }
     
     double maxTimestep = 4.0*_hx/maxVelocity;
     
     return min(maxTimestep, 1.0);
 }
 /* Returns the maximum allowed timestep. Note that the actual timestep
  * taken should usually be much below this to ensure accurate
  * simulation - just never above.
  */
 double maxTimestep() {
     double maxVelocity = 0.0;
     for (int y = 0; y < _h; y++) {
         for (int x = 0; x < _w; x++) {
             /* Average velocity at grid cell center */
             double u = _u->lerp(x + 0.5, y + 0.5);
             double v = _v->lerp(x + 0.5, y + 0.5);
             
             double velocity = sqrt(u*u + v*v);
             maxVelocity = max(maxVelocity, velocity);
         }
     }
     
     /* Fluid should not flow more than two grid cells per iteration */
     double maxTimestep = 2.0*_hx/maxVelocity;
     
     /* Clamp to sensible maximum value in case of very small velocities */
     return min(maxTimestep, 1.0);
 }
    /* Third order Runge-Kutta for velocity integration in time */
    void rungeKutta3(double &x, double &y, double timestep, const FluidQuantity &u, const FluidQuantity &v) const {
        double firstU = u.lerp(x, y)/_hx;
        double firstV = v.lerp(x, y)/_hx;

        double midX = x - 0.5*timestep*firstU;
        double midY = y - 0.5*timestep*firstV;

        double midU = u.lerp(midX, midY)/_hx;
        double midV = v.lerp(midX, midY)/_hx;

        double lastX = x - 0.75*timestep*midU;
        double lastY = y - 0.75*timestep*midV;

        double lastU = u.lerp(lastX, lastY);
        double lastV = v.lerp(lastX, lastY);
        
        x -= timestep*((2.0/9.0)*firstU + (3.0/9.0)*midU + (4.0/9.0)*lastU);
        y -= timestep*((2.0/9.0)*firstV + (3.0/9.0)*midV + (4.0/9.0)*lastV);
    }