Difference between revisions of "Numerical Integration"

From VDrift
Jump to: navigation, search
m (Fix image links)
 
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
Note:  the old version of this page is here: [[Old Numerical Integration]]
 +
 
Numerical integration constitutes a broad family of algorithms for calculating the numerical value of a definite integral.  This the backbone of physics simulations because it allows calculation of velocity and position from forces (and therefore acceleration) applied to a rigid body.
 
Numerical integration constitutes a broad family of algorithms for calculating the numerical value of a definite integral.  This the backbone of physics simulations because it allows calculation of velocity and position from forces (and therefore acceleration) applied to a rigid body.
  
 
==Criteria==
 
==Criteria==
In realtime simulations, the most important integrator criteria are performance, stability, and accuracy.  Performance refers to how long it takes to perform the integration for a given timestep.  Stability refers to how well the integrator copes with stiff constraints such as high spring constants before errors becomes unacceptably large.  Accuracy refers to how well the integrator matches the expected result, and includes whether or not the integrator damps out (loses energy) over time.
+
In realtime simulations, the most important integrator criteria are performance, stability, and accuracy.  Performance refers to how long it takes to perform the integration for a given timestep.  Stability refers to how well the integrator copes with stiff constraints such as high spring constants before errors becomes unacceptably large.  Accuracy refers to how well the integrator matches the expected result.
 +
 
 +
==Integrator Order==
 +
The order of a numerical integrator is a convenient way to approximate the accuracy of an integrator. All integrators presented here have an order of at least one, which means that they eventually converge to the correct solution if the timestep is small enough.  First order methods converge to the exact solution in a linear relation to the timestep. Higher order methods converge to the correct solution in a power relation to the timestep.
  
 
==Euler Integration==
 
==Euler Integration==
The Euler method is a first order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most basic kind of explicit method for numerical integration for ordinary differential equations. The performance is excellent, but both stability and accuracy are poor. Because it is so simple, it unfortunately gets used very often. Vamos uses Euler integration.
+
The Euler method is a first order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most basic explicit method for numerical integration for ordinary differential equations.
 +
  real a = system.GetAcceleration(state);
 +
  state.x += state.v * dt;
 +
  state.v += a * dt;
  
   a = acceleration(state, t+dt)
+
==SUVAT==
   x += v*dt
+
The "suvat" method takes its name from the variables in the equations of motion.  This is a first order method that is very similar to the Euler method. The only difference is the addition of an acceleration term to the equation for the change in position.
   v += a*dt
+
   real a = system.GetAcceleration(state);
 +
   state.x += state.v * dt + a*dt*dt*0.5;
 +
   state.v += a * dt;
  
 
==Newton-Stormer-Verlet (NSV) / Symplectic Euler / Euler–Cromer algorithm==
 
==Newton-Stormer-Verlet (NSV) / Symplectic Euler / Euler–Cromer algorithm==
the Euler–Cromer algorithm or symplectic Euler method or Newton-Stormer-Verlet (NSV) method is a modification of the Euler method for solving Hamilton's equations, a system of ordinary differential equations that arises in classical mechanics. It is a symplectic integrator, which is a class of geometric integrators that is especially good at simulations of dynamics and hence it yields much better results than the standard Euler method.  The performance is excellent, and stability is fair, and accuracy is excellentUnbelievably, the algorithm is very simple and almost identical to the Euler method.
+
The Euler–Cromer algorithm or symplectic Euler method or Newton-Stormer-Verlet (NSV) method is a modification of the Euler method for solving Hamilton's equations, a system of ordinary differential equations that arises in classical mechanics. It is a symplectic integrator, which is a class of geometric integrators that is especially good at simulations of systems of undamped oscillators. Due to this property, it preserves energy better than the standard Euler method and so is often used in simulations of orbital mechanicsIt is a first order integrator.
 
+
   real a = system.GetAcceleration(state);
   a = acceleration(state, t+dt)
+
   state.v += a * dt;
   v += a*dt
+
   state.x += state.v * dt;
   x += v*dt
 
 
 
==Velocity Verlet==
 
Verlet integration is a numerical integration method originally designed for calculating the trajectories of particles in molecular dynamics simulations.  The velocity verlet variant directly calculates velocity.  The performance is fair, and both stability and accuracy are excellent.
 
  
Unfortunately, calculating the velocity depends on knowing the acceleration for the current iteration, which poses a problem when the acceleration depends on the velocity (such as with a damper). Using the velocity from the last iteration to calculate the acceleration gets around this but may have implications on the accuracy of the method.
+
==Basic Verlet/Velocity Verlet==
 
+
Verlet integration is a numerical integration method originally designed for calculating the trajectories of particles in molecular dynamics simulations.  The velocity verlet variant directly calculates velocity. It is a second order integrator. Unfortunately, this method is unsuitable for simulations where the acceleration is dependent on velocities, such as with a damper. The modified verlet variant below uses some tricks to try to get around this limitation, but this method will only provide first-order accuracy.
   if (not oldaccel)
+
   if (!have_oldaccel)
       oldaccel = acceleration(state, t+dt)
+
       oldaccel = system.GetAcceleration(state);
 
    
 
    
   x += v*dt + 0.5*oldaccel*dt*dt
+
   state.x += state.v*dt + 0.5*oldaccel*dt*dt;
   a = acceleration(state, t+dt)
+
   state.v += 0.5*oldaccel*dt;
   v += 0.5*(a + oldaccel)*dt
+
  real a = system.GetAcceleration(state);
 +
   state.v += 0.5*a*dt;
 
    
 
    
   oldaccel = a
+
   oldaccel = a;
 +
  have_oldaccel = true;
  
==Runge Kutta 4==
+
==Improved Euler/Trapezoidal/Bilinear/Predictor-Corrector/Heun==
The Runge–Kutta methods are an important family of implicit and explicit iterative methods for the approximation of solutions of ordinary differential equations.  The Runge Kutta 4 (or RK4) is a well-known 4th-order explicit Runge Kutta algorithm.  The code snippet shown below is high level, and the actual implementation is a bit more complicated.  Performance is poor, since the acceleration must be evaluated for all objects 4 times per iteration, stability is excellent, and accuracy is fair.
+
The so-call "Improved Euler" method, also known as the trapezoidal or bilinear or predictor/corrector or Heun Formula method, is a second order integrator.
   Derivative a = evaluate(state, t)
+
   STATE predictor(state);
   Derivative b = evaluate(state, t, dt*0.5f, a)
+
   predictor.x += state.v * dt;
   Derivative c = evaluate(state, t, dt*0.5f, b)
+
   predictor.v += system.GetAcceleration(state) * dt;
  Derivative d = evaluate(state, t, dt, c)
 
 
    
 
    
   const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx)
+
   STATE corrector(state);
   const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv)
+
  corrector.x += predictor.v * dt;
 +
   corrector.v += system.GetAcceleration(predictor) * dt;
 
    
 
    
   state.x = state.x + dxdt*dt
+
   state.x = (predictor.x + corrector.x)*0.5;
   state.v = state.v + dvdt*dt
+
   state.v = (predictor.v + corrector.v)*0.5;
  
==Detailed Comparison: oscillating spring-mass==
+
==Runge Kutta 4==
For comparing these algorithms I used a simple spring-mass oscillator, because it can be difficult to integrate when the spring is very stiff, but it can be analytically solved easily so I have something to compare the integrators toIn addition, its force calculation depends only on position, which allows the Velocity Verlet algorithm to work as it is commonly used. For this simulation the instantaneous acceleration input into all integrators is calculated as:
+
The Runge–Kutta methods are an important family of implicit and explicit iterative methods for the approximation of solutions of ordinary differential equations.  The Runge Kutta 4 (or RK4) is a well-known fourth-order explicit Runge Kutta algorithmThe code snippet shown below is high level, and the actual implementation is a bit more complicated. Note that the evaluate() function calls the system's acceleration function.
   a = -k*x/m;
+
  DERIVATIVE a = evaluate(state, 0, DERIVATIVE(), system);
where k is the spring constant, x is the position, and m is the mass.
+
   DERIVATIVE b = evaluate(state, dt*0.5, a, system);
 +
  DERIVATIVE c = evaluate(state, dt*0.5, b, system);
 +
  DERIVATIVE d = evaluate(state, dt, c, system);
  
The analytic solution is calculated as:
+
   const float dxdt = 1.0/6.0 * (a.dx + 2.0*(b.dx + c.dx) + d.dx);
   A * cos (sqrt(k/m)*t)
+
  const float dvdt = 1.0/6.0 * (a.dv + 2.0*(b.dv + c.dv) + d.dv);
where A is the amplitude (and the initial position) and t is the time in seconds.
 
  
The constants were set to:
+
   state.x = state.x + dxdt*dt;
   A = 0.5
+
   state.v = state.v + dvdt*dt;
   m = 250.0
 
  dt = 0.1
 
  
[[Image:M250a1k200dt01t20.pdf-cropped.png]]
+
==Performance Comparison==
 +
In most simulations, calculation of the system's acceleration function is computationally very expensive.  Therefore, we can rank the integrators by how many times they evaluate the system's acceleration function:
 +
{| cellspacing="0" border="1"
 +
!Method
 +
!Evaluations of the Acceleration Function
 +
|-
 +
!Euler
 +
|1
 +
|-
 +
!SUVAT
 +
|1
 +
|-
 +
!NSV
 +
|1
 +
|-
 +
!Modified Verlet
 +
|1
 +
|-
 +
!Improved Euler
 +
|2
 +
|-
 +
!RK4
 +
|4
 +
|}
  
This is the first simulation, with k set to 200 and run for 20 seconds. The Euler integrator is already unstable, with quickly increasing error as time goes on. All of the other methods are similar for this simulation.
+
Because of the extra evaluations of the acceleration function, the Improved Euler method is about twice as slow as the Euler method, and the RK4 method is about four times as slow as the Euler method. Another way to look at this is that we could run the Euler method with a 0.001 second timestep with the same performance as running the RK4 method with a 0.004 second timestep.
  
[[Image:M250a1k10000dt01t20.pdf.png]]
+
==Detailed comparison==
 +
For comparing these algorithms I used a damped spring-mass system because it can be difficult to integrate when the spring is very stiff or the damping is high, but it can be analytically solved so I have something to compare the integrators to. The damping force is proportional to the velocity state, while the spring force is proportional to the position state. Zeta is the damping ratio. The analytic solution can be obtained with the following calculations:
 +
  real zeta = c / (2.0 * sqrt(k*m));
 +
  real w = sqrt(k/m);
 +
 
 +
  if (zeta < 1.0) //underdamped
 +
  {
 +
      real wd = w*sqrt(1.0-zeta*zeta);
 +
     
 +
      real A = ic.x;
 +
      real B = (1.0/wd)*(zeta*w*ic.x+ic.v);
 +
      real out = (A*cos(wd*time)+B*sin(wd*time))*exp(-zeta*w*time);
 +
      return out;
 +
  }
 +
  else if (zeta > 1.0) //overdamped
 +
  {
 +
      real beta = c/m;
 +
      real rminus = 0.5*(-beta-sqrt(beta*beta-4.0*w*w));
 +
      real rplus = 0.5*(-beta+sqrt(beta*beta-4.0*w*w));
 +
      real A = ic.x - (rminus*ic.x - ic.v)/(rminus-rplus);
 +
      real B = (rminus*ic.x-ic.v)/(rminus-rplus);
 +
      real out = A*exp(rminus*time)+B*exp(rplus*time);
 +
      return out;
 +
  }
 +
  else //critically damped
 +
  {
 +
      real A = ic.x;
 +
      real B = ic.v + w*ic.x;
 +
     
 +
      return (A + B*time)*exp(-w*time);
 +
  }
  
The k constant has been increased to 10,000. At this value all of the non-Euler methods are initially similar, but....
+
The spring constant (k) was fixed at 600,000. Mass was set to 168 kg. Initial conditions were X = 1 m and V = 0 m/s. The damping coefficient was calculated depending on the damping ratio (zeta). These values were chosen to be similar to what may be encountered in VDrift.
  
[[Image:M250a1k10000dt01t200.pdf.png]]
+
Timesteps were varied between 0.001 seconds and 0.020 seconds. To account for performance differences, the improved Euler method was handicapped with a timestep twice as large as the indicated timestep, and the RK4 method was handicapped with a timestep four times as large as the indicated timestep. The mean absolute error is plotted for each integrator at each timestep.
  
This is the same k constant of 10,000 after ~200 seconds. The RK4 integrator is losing energy, while the NSV and Velocity Verlet methods have amplitudes similar to the exact answer.
+
===Critically Damped System===
 +
First, a damping ratio of 1 is simulated. Systems such as car suspensions are often critically damped.
  
[[Image:M250a1k100000dt01t20.pdf.png]]
+
====Stability====
 +
[[File:zeta1.png]]
  
The k constant has been increased to 100,000.  The NSV integrator is unstable at this level.  The RK4 integrator is almost uniformly zero. The only integrator that is still close to the exact value is the Velocity Verlet integrator.
+
Reading left to right along the timestep increments, the RK4 integrator's results become unacceptable first at a timestep of 0.009 seconds (remember that this actually means a 0.036 second timestep due to the performance handicap of the RK4 method).  The NSV is next, followed by the modified verlet and improved euler methods. The suvat method is second to last to become unacceptable and the euler method is acceptable even with a 0.020 second timestep.
  
[[Image:M250a1k100000dt01t200.pdf.png]]
+
====Accuracy====
 +
[[File:zeta1closeup.png]]
  
This is the same k constant of 100,000 after ~200 seconds. The Velocity Verlet integrator is doing pretty well here, mostly preserving energy.  The RK4 integrator is zero.
+
Looking at a closeup of the results, it becomes apparent where the strengths of higher order integrators lie. At 0.002, the RK4 (actual timestep 0.008) and improved euler (actual timestep 0.004) methods beat out all of the other methods. Of the first order methods at 0.002, from most to least accurate they are: modified verlet, suvat, euler, and nsv.
  
[[Image:M250a1k1000000dt01t20.pdf.png]]
+
From these results we can conclude that when the system is not stiff (that is, the forces involved are small relative to the timestep), higher order integrators are most accurate, even after penalizing their lower performance.
  
The k constant is now 1,000,000.  At this level both the RK4 and Velocity Verlet integrators quickly become unstable.
+
===Overdamped System Stability===
 +
Here a damping ratio of 2 is simulated. Systems with a lot of friction, such as tire simulations, are often overdamped systems.
  
==Detailed Comparison: spring-mass-damper==
+
[[File:zeta2.png]]
For realtime dynamics simulations, damping forces are usually applied. The damping force is proportional to the velocity state, while the spring force is proportional to the position state.  Acceleration is calculated as:
 
  a = (-k*x - c*v)/m
 
where:
 
  c = 2*sqrt(k*m)
 
  
The analytic solution for the position is:
+
In order from least to most stable: rk4, improved euler, nsv, modified verlet, suvat, euler.
  (A + B*t)*exp(-w*t)
 
where:
 
  w = sqrt(k/m);
 
  B = vo + w*xo;
 
  
The constants were set to:
+
===Underdamped System Stability===
  m = 250.0
+
Here a damping ratio of 0.1 is simulated. Oscillating systems such as planetary orbits are underdamped.
  A = 1.0
 
  xo = 1.0
 
  vo = 0.0
 
  dt = 0.1
 
  
Because the Velocity Verlet algorithm shown above isn't technically correct since due to the damper the acceleration depends on the velocity, a modified Velocity Verlet algorithm was added to the comparison which is purported to give better results for these sorts of cases:
+
[[File:zeta0.1.png]]
if (not oldaccel)
 
    oldaccel = acceleration(state, t+dt)
 
 
x += v*dt + 0.5*oldaccel*dt*dt
 
v += 0.5*oldaccel*dt
 
a = acceleration(state, t+dt)
 
v += 0.5*a*dt
 
 
oldaccel = a
 
  
[[Image:Damped-m250a1k10000dt01t10.png]]
+
In order from least to most stable: euler, suvat, improved euler, rk4, nsv, modified verletWe can see why systems of orbits and molecular dynamics are often simulated with NSV or velocity verlet methods: they maintain stability very well here.
 
 
Cutting right to the chase, k=10,000 is where the unmodified Velocity Verlet algorithm starts to fall apart.  The other integrators are similar, although note that the RK4 solution is right on top of the analytic solution.
 
 
 
[[Image:Damped-m250a1k15000dt01t10.png]]
 
 
 
Increasing k to 15,000 results in the NSV starting to show major inaccuraciesSurprisingly, the Euler algorithm is still stable and doesn't show the same undesirable behavior as the NSV algorithm.
 
 
 
[[Image:Damped-m250a1k18000dt01t10.png‎]]
 
 
 
At k = 18,000, the modified Verlet algorithm becomes unstable and we're left with only the Euler and RK4 algorithms.  The Euler algorithm starts to have major error at k = 30,000, and the RK4 does the same at k = 80,000.
 
  
 
==Summary==
 
==Summary==
 
+
Let's rank each integrator in terms of accuracy, and then stability for critically-, over-, and under-damped systems. One is the best. Recall that because we already penalized improved euler and RK4 methods for performance, we can consider each method to have roughly the same performance.
The results from the spring oscillator and critically damped spring are shown below as rankings for each integrator, with 1 being the best and 4 the worst.  The rankings are based on when the method becomes unstable, with accuracy being used as a tie-breaker where necessary.
 
 
 
 
{| cellspacing="0" border="1"
 
{| cellspacing="0" border="1"
 
!Method
 
!Method
!Oscillating Spring
+
!Accuracy
!Critically Damped Spring
+
!Stability: critically-damped
 +
!Stability: over-damped
 +
!Stability: under-damped
 
|-
 
|-
 
!Euler
 
!Euler
 +
|5
 +
|1
 +
|tied 1/2
 +
|6
 +
|-
 +
!SUVAT
 
|4
 
|4
 
|2
 
|2
 +
|tied 1/2
 +
|5
 
|-
 
|-
 
!NSV
 
!NSV
 +
|6
 +
|4
 +
|tied 3/4
 +
|tied 1/2
 +
|-
 +
!Modified Verlet
 
|3
 
|3
 
|4
 
|4
 +
|tied 3/4
 +
|tied 1/2
 
|-
 
|-
!RK4
+
!Improved Euler
 
|2
 
|2
|1
+
|3
 +
|5
 +
|4
 
|-
 
|-
!Velocity Verlet
+
!RK4
 
|1
 
|1
 +
|5
 +
|6
 
|3
 
|3
 
|}
 
|}
  
It should be noted that for the oscillating spring case, the RK4 integrator resulted in poor accuracy (zero response) while all of the other algorithms except the Euler method were still giving plausible results. Although the RK4 algorithm stayed stable until the same high k values as the Velocity Verlet algorithm, the velocity verlet algorithm's better accuracy at lower k values makes it clearly better for this case.
+
Note that no integrator is clearly the worst or clearly the best; they all have strengths and weaknesses. If you are simulating a non-stiff system that will generate only small forces relative to your timestep, the RK4 method is the winner. If you're simulating a strictly stiff underdamped system, use the NSV or modified verlet methods. If you're simulating a strictly stiff overdamped or critically damped system, use the Euler or SUVAT methods. If you have a combination of needs, some compromise is called for.
 
 
The performance of the Euler, NSV, and Velocity Verlet integrators is similar, with the Euler and NSV methods resulting in slightly less arithmetic.  The RK4 has worse performance and is more complicated.  In fact, the RK4 results in 4 evaluations of the acceleration function per timestep. If this function is the performance bottleneck, then the other methods could be run with a smaller timestep (0.25*dt) with similar performance, and with a smaller timestep all methods (except the basic Euler) outperform the RK4 algorithm for both cases.
 
  
The RK4 algorithm is very stable, but it comes at a performance and accuracy cost.  The RK4 integrator loses energy and requires much more arithmetic.  Better stability and accuracy could be achieved at the same performance level by running the Velocity Verlet algorithm with a smaller timestep.
+
VDrift requires stability in the face of stiff overdamped systems, and currently uses the SUVAT method due to slightly improved accuracy over the euler method.
  
Based on the stability, accuracy, and performance of these integrators, I believe that the best algorithm for general realtime dynamics simulations is the modified Velocity Verlet algorithm.
+
[[Category:Development]]

Latest revision as of 07:19, 27 August 2012

Note: the old version of this page is here: Old Numerical Integration

Numerical integration constitutes a broad family of algorithms for calculating the numerical value of a definite integral. This the backbone of physics simulations because it allows calculation of velocity and position from forces (and therefore acceleration) applied to a rigid body.

Criteria

In realtime simulations, the most important integrator criteria are performance, stability, and accuracy. Performance refers to how long it takes to perform the integration for a given timestep. Stability refers to how well the integrator copes with stiff constraints such as high spring constants before errors becomes unacceptably large. Accuracy refers to how well the integrator matches the expected result.

Integrator Order

The order of a numerical integrator is a convenient way to approximate the accuracy of an integrator. All integrators presented here have an order of at least one, which means that they eventually converge to the correct solution if the timestep is small enough. First order methods converge to the exact solution in a linear relation to the timestep. Higher order methods converge to the correct solution in a power relation to the timestep.

Euler Integration

The Euler method is a first order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most basic explicit method for numerical integration for ordinary differential equations.

 real a = system.GetAcceleration(state);
 state.x += state.v * dt;
 state.v += a * dt;

SUVAT

The "suvat" method takes its name from the variables in the equations of motion. This is a first order method that is very similar to the Euler method. The only difference is the addition of an acceleration term to the equation for the change in position.

 real a = system.GetAcceleration(state);
 state.x += state.v * dt + a*dt*dt*0.5;
 state.v += a * dt;

Newton-Stormer-Verlet (NSV) / Symplectic Euler / Euler–Cromer algorithm

The Euler–Cromer algorithm or symplectic Euler method or Newton-Stormer-Verlet (NSV) method is a modification of the Euler method for solving Hamilton's equations, a system of ordinary differential equations that arises in classical mechanics. It is a symplectic integrator, which is a class of geometric integrators that is especially good at simulations of systems of undamped oscillators. Due to this property, it preserves energy better than the standard Euler method and so is often used in simulations of orbital mechanics. It is a first order integrator.

 real a = system.GetAcceleration(state);
 state.v += a * dt;
 state.x += state.v * dt;

Basic Verlet/Velocity Verlet

Verlet integration is a numerical integration method originally designed for calculating the trajectories of particles in molecular dynamics simulations. The velocity verlet variant directly calculates velocity. It is a second order integrator. Unfortunately, this method is unsuitable for simulations where the acceleration is dependent on velocities, such as with a damper. The modified verlet variant below uses some tricks to try to get around this limitation, but this method will only provide first-order accuracy.

 if (!have_oldaccel)
     oldaccel = system.GetAcceleration(state);
 
 state.x += state.v*dt + 0.5*oldaccel*dt*dt;
 state.v += 0.5*oldaccel*dt;
 real a = system.GetAcceleration(state);
 state.v += 0.5*a*dt;
 
 oldaccel = a;
 have_oldaccel = true;

Improved Euler/Trapezoidal/Bilinear/Predictor-Corrector/Heun

The so-call "Improved Euler" method, also known as the trapezoidal or bilinear or predictor/corrector or Heun Formula method, is a second order integrator.

 STATE predictor(state);
 predictor.x += state.v * dt;
 predictor.v += system.GetAcceleration(state) * dt;
 
 STATE corrector(state);
 corrector.x += predictor.v * dt;
 corrector.v += system.GetAcceleration(predictor) * dt;
 
 state.x = (predictor.x + corrector.x)*0.5;
 state.v = (predictor.v + corrector.v)*0.5;

Runge Kutta 4

The Runge–Kutta methods are an important family of implicit and explicit iterative methods for the approximation of solutions of ordinary differential equations. The Runge Kutta 4 (or RK4) is a well-known fourth-order explicit Runge Kutta algorithm. The code snippet shown below is high level, and the actual implementation is a bit more complicated. Note that the evaluate() function calls the system's acceleration function.

 DERIVATIVE a = evaluate(state, 0, DERIVATIVE(), system);
 DERIVATIVE b = evaluate(state, dt*0.5, a, system);
 DERIVATIVE c = evaluate(state, dt*0.5, b, system);
 DERIVATIVE d = evaluate(state, dt, c, system);
 const float dxdt = 1.0/6.0 * (a.dx + 2.0*(b.dx + c.dx) + d.dx);
 const float dvdt = 1.0/6.0 * (a.dv + 2.0*(b.dv + c.dv) + d.dv);
 state.x = state.x + dxdt*dt;
 state.v = state.v + dvdt*dt;

Performance Comparison

In most simulations, calculation of the system's acceleration function is computationally very expensive. Therefore, we can rank the integrators by how many times they evaluate the system's acceleration function:

Method Evaluations of the Acceleration Function
Euler 1
SUVAT 1
NSV 1
Modified Verlet 1
Improved Euler 2
RK4 4

Because of the extra evaluations of the acceleration function, the Improved Euler method is about twice as slow as the Euler method, and the RK4 method is about four times as slow as the Euler method. Another way to look at this is that we could run the Euler method with a 0.001 second timestep with the same performance as running the RK4 method with a 0.004 second timestep.

Detailed comparison

For comparing these algorithms I used a damped spring-mass system because it can be difficult to integrate when the spring is very stiff or the damping is high, but it can be analytically solved so I have something to compare the integrators to. The damping force is proportional to the velocity state, while the spring force is proportional to the position state. Zeta is the damping ratio. The analytic solution can be obtained with the following calculations:

 real zeta = c / (2.0 * sqrt(k*m));
 real w = sqrt(k/m);
 
 if (zeta < 1.0) //underdamped
 {
     real wd = w*sqrt(1.0-zeta*zeta);
     
     real A = ic.x;
     real B = (1.0/wd)*(zeta*w*ic.x+ic.v);
     real out = (A*cos(wd*time)+B*sin(wd*time))*exp(-zeta*w*time);
     return out;
 }
 else if (zeta > 1.0) //overdamped
 {
     real beta = c/m;
     real rminus = 0.5*(-beta-sqrt(beta*beta-4.0*w*w));
     real rplus = 0.5*(-beta+sqrt(beta*beta-4.0*w*w));
     real A = ic.x - (rminus*ic.x - ic.v)/(rminus-rplus);
     real B = (rminus*ic.x-ic.v)/(rminus-rplus);
     real out = A*exp(rminus*time)+B*exp(rplus*time);
     return out;
 }
 else //critically damped
 {
     real A = ic.x;
     real B = ic.v + w*ic.x;
     
     return (A + B*time)*exp(-w*time);
 }

The spring constant (k) was fixed at 600,000. Mass was set to 168 kg. Initial conditions were X = 1 m and V = 0 m/s. The damping coefficient was calculated depending on the damping ratio (zeta). These values were chosen to be similar to what may be encountered in VDrift.

Timesteps were varied between 0.001 seconds and 0.020 seconds. To account for performance differences, the improved Euler method was handicapped with a timestep twice as large as the indicated timestep, and the RK4 method was handicapped with a timestep four times as large as the indicated timestep. The mean absolute error is plotted for each integrator at each timestep.

Critically Damped System

First, a damping ratio of 1 is simulated. Systems such as car suspensions are often critically damped.

Stability

Zeta1.png

Reading left to right along the timestep increments, the RK4 integrator's results become unacceptable first at a timestep of 0.009 seconds (remember that this actually means a 0.036 second timestep due to the performance handicap of the RK4 method). The NSV is next, followed by the modified verlet and improved euler methods. The suvat method is second to last to become unacceptable and the euler method is acceptable even with a 0.020 second timestep.

Accuracy

Zeta1closeup.png

Looking at a closeup of the results, it becomes apparent where the strengths of higher order integrators lie. At 0.002, the RK4 (actual timestep 0.008) and improved euler (actual timestep 0.004) methods beat out all of the other methods. Of the first order methods at 0.002, from most to least accurate they are: modified verlet, suvat, euler, and nsv.

From these results we can conclude that when the system is not stiff (that is, the forces involved are small relative to the timestep), higher order integrators are most accurate, even after penalizing their lower performance.

Overdamped System Stability

Here a damping ratio of 2 is simulated. Systems with a lot of friction, such as tire simulations, are often overdamped systems.

Zeta2.png

In order from least to most stable: rk4, improved euler, nsv, modified verlet, suvat, euler.

Underdamped System Stability

Here a damping ratio of 0.1 is simulated. Oscillating systems such as planetary orbits are underdamped.

Zeta0.1.png

In order from least to most stable: euler, suvat, improved euler, rk4, nsv, modified verlet. We can see why systems of orbits and molecular dynamics are often simulated with NSV or velocity verlet methods: they maintain stability very well here.

Summary

Let's rank each integrator in terms of accuracy, and then stability for critically-, over-, and under-damped systems. One is the best. Recall that because we already penalized improved euler and RK4 methods for performance, we can consider each method to have roughly the same performance.

Method Accuracy Stability: critically-damped Stability: over-damped Stability: under-damped
Euler 5 1 tied 1/2 6
SUVAT 4 2 tied 1/2 5
NSV 6 4 tied 3/4 tied 1/2
Modified Verlet 3 4 tied 3/4 tied 1/2
Improved Euler 2 3 5 4
RK4 1 5 6 3

Note that no integrator is clearly the worst or clearly the best; they all have strengths and weaknesses. If you are simulating a non-stiff system that will generate only small forces relative to your timestep, the RK4 method is the winner. If you're simulating a strictly stiff underdamped system, use the NSV or modified verlet methods. If you're simulating a strictly stiff overdamped or critically damped system, use the Euler or SUVAT methods. If you have a combination of needs, some compromise is called for.

VDrift requires stability in the face of stiff overdamped systems, and currently uses the SUVAT method due to slightly improved accuracy over the euler method.