Higher-Order Difference Equations
In our previous class, we were able to fix the instability of our spring system by switching from the never-correct Forward Euler time step to the Euler-Cromer time step, which made our simulation stable, but did not achieve an accurate result over time when compared to the exact answer.The solutions we've explored so far are primarily explicit time integrations, in that we're only using values from previous points in time to solve for points at future points in time. When we consider, later, implicit time integration, where values at the current time are solved self-referentially, things will get a lot more complex. So, we'd like to continue exploring techniques that are fundamentally explicit in nature.
The approach that all of the higher-order explicit methods will take will be to use lower-order approximations to compute temporary values, which will be used to compute better derivatives that will then be used to integrate forward. The first, and most common higher-order method is called "The Midpoint Method".
The Midpoint Method
Let's approximate the velocity at half-way through our time step, rather than all the way to the new time, just using the forward finite-difference approximation we used with Forward Euler:// Time Step function.
void TimeStep( float i_dt )
{
// Compute acceleration from current position.
float A = ( -Stiffness / BobMass ) * State[StatePositionX];
// Update position half-way.
State[StatePositionX] += ( i_dt/2.0 ) * State[StateVelocityX];
// Update velocity based on acceleration.
State[StateVelocityX] += i_dt * A;
// Update position half-way.
State[StatePositionX] += ( i_dt/2.0 ) * State[StateVelocityX];
// Update current time.
State[StateCurrentTime] += i_dt;
}
And here that is:
So really we've actually made things worse with this simple higher-order approximation. An alternative midpoint-method approach is called Velocity Verlet, and it looks like this:
// Time Step function.
void TimeStep( float i_dt )
{
// Compute acceleration from current position.
float A = ( -Stiffness / BobMass ) * State[StatePositionX];
// Update velocity half-way.
State[StateVelocityX] += ( i_dt/2.0 ) * A;
// Update position.
State[StatePositionX] += i_dt * State[StateVelocityX];
// Re-compute acceleration.
A = ( -Stiffness / BobMass ) * State[StatePositionX];
// Update velocity half-way.
State[StateVelocityX] += ( i_dt/2.0 ) * A;
// Update current time.
State[StateCurrentTime] += i_dt;
}
And here it is running:
Not bad! Now we're cooking with gas! Still not perfect, though. The next class will explore the fourth-order Runge Kutta approach, and begin to consider implicit methods.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.