NAMD-lite - Heun's method implementation

From: Nick Schafer (npschafer_at_wisc.edu)
Date: Tue Jul 10 2007 - 11:01:36 CDT

Dear NAMDers -

My name is Nick Schafer and I am an undergraduate at the University of
Wisconsin, Madison. I'm interested in numerical methods for the phase
space propagation of molecular dynamics. In order to get a feel for how
the Verlet integrator stacks up against other simple methods, I've
implemented both Forward Euler and Heun's method in NAMD-Lite. As far
as I can tell, my Heun's method implementation is working. However, I'm
not very sure that I'm handling the force evaluations correctly because
the Verlet integrator that I used as a model uses only one force
evaluation, which is at the current positions. Heun's method requires a
force evaluation at a "predicted" state of the system, which makes it
just slightly more complicated. Anyway, here is some pseudo code that I
was trying to follow during my implementation, followed by my code. If
someone who knows exactly how the force evaluations in NAMD-lite work
could take a look and tell me whether they think I am doing it right, it
would be greatly appreciated.

Pseudo code:
    % first, we calculate the current acceleration
    current_acceleration = function(current_velocity, etc.)
    % we use this to do a poor job of estimating future velocity
    future_velocity = current_velocity + new_acceleration * timestep;
    % use (poorly predicted) future velocity to calc future acceleration
    future_acceleration = function(future_velocity, etc.)
    % find the average acceleration
    acceleration = 0.5 * (current_acceleration + future_acceleration)
    % use the average acceleration
    new_velocity = current_velocity + acceleration * timestep;

Inside runge.c, equivalent to verlet.c :

int step_compute_runge(Step *s, StepSystem *sys, int32 numsteps)
{
  const double dt = s->param->timestep;
  const double half_dt = 0.5 * dt;
  double konst;
  double pe;
  void *f_obj = s->param->force_object;
  int32 (*f_eval)(void *, double *, MD_Dvec *, MD_Dvec *)
    = s->param->force_compute;
  MD_Dvec *f = sys->force;
  MD_Dvec *vel = sys->vel;
  MD_Dvec *pos = sys->pos;
  const double *scal_inv_mass = s->scal_inv_mass;
  const int32 natoms = s->param->natoms;
  int32 i;
  MD_Dvec *pred_pos;
  pred_pos = (MD_Dvec *) calloc(natoms, sizeof(MD_Dvec));
  MD_Dvec *pred_vel;
  pred_vel = (MD_Dvec *) calloc(natoms, sizeof(MD_Dvec));
  MD_Dvec *pred_force;
  pred_force = (MD_Dvec *) calloc(natoms, sizeof(MD_Dvec));
  double temppe = 0;
 
    /* perform two step Runge Kutta integration of system */

    /* compute force at current positions */
    if (f_eval(f_obj, &pe, f, pos)) return STEP_FAIL;

    /* predict velocities, store in temporary array */
    for (i = 0; i < natoms; i++) {
       konst = dt * scal_inv_mass[i];
       pred_vel[i].x = vel[i].x + f[i].x * konst;
       pred_vel[i].y = vel[i].y + f[i].y * konst;
       pred_vel[i].z = vel[i].z + f[i].z * konst;
    }

    /* predict new positions, store in temporary array */
    for (i = 0; i < natoms; i++) {
       konst = dt * scal_inv_mass[i];
       pred_pos[i].x = pos[i].x + pred_vel[i].x * dt;
       pred_pos[i].y = pos[i].y + pred_vel[i].y * dt;
       pred_pos[i].z = pos[i].z + pred_vel[i].z * dt;
    }
    /* finished computing rough approximation of position at the new
time step */

    /* copy old force array into pred_force array */
       for (i = 0; i < natoms; i++) {
       pred_force[i].x = f[i].x;
       pred_force[i].y = f[i].y;
       pred_force[i].z = f[i].z;
    }

    /* compute NEW force array at updated positions, saving current pe */
    temppe = pe;
    if (f_eval(f_obj, &pe, f, pred_pos)) return STEP_FAIL;
    pe = temppe;

    /* update velocities using average of two force arrays */
    for (i = 0; i < natoms; i++) {
       konst = half_dt * scal_inv_mass[i];
       vel[i].x += konst * (f[i].x+pred_force[i].x);
       vel[i].y += konst * (f[i].y+pred_force[i].y);
       vel[i].z += konst * (f[i].z+pred_force[i].z);
    }

    /* update positions using new velocities */
    for (i = 0; i < natoms; i++) {
       pos[i].x += dt * vel[i].x;
       pos[i].y += dt * vel[i].y;
       pos[i].z += dt * vel[i].z;
    }

    /* free pred_pos, pred_vel and pred_force */
    free(pred_pos);
    free(pred_vel);
    free(pred_force);
   
    return 0;
}

Thanks much!

Nick Schafer
http://sbel.wisc.edu/People/schafer/mainframeset.html

This archive was generated by hypermail 2.1.6 : Wed Feb 29 2012 - 15:44:57 CST