View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.algorithms.dynamics.integrators;
39  
40  import ffx.numerics.Potential;
41  import ffx.potential.SystemState;
42  
43  import java.util.logging.Level;
44  import java.util.logging.Logger;
45  
46  import static ffx.utilities.Constants.KCAL_TO_GRAM_ANG2_PER_PS2;
47  import static java.lang.String.format;
48  
49  /**
50   * Respa performs multiple time step molecular dynamics using the reversible reference system
51   * propagation algorithm (r-RESPA) via a Verlet core with the potential split into fast- and
52   * slow-evolving portions.
53   *
54   * <p>The inner RESPA loop is position Verlet.
55   *
56   * <p>D. D. Humphreys, R. A. Friesner and B. J. Berne, "A Multiple-Time-Step Molecular Dynamics
57   * Algorithm for Macromolecules", Journal of Physical Chemistry, 98, 6885-6892 (1994)
58   *
59   * <p>X. Qian and T. Schlick, "Efficient Multiple-Time-Step Integrators with Distance-Based Force
60   * Splitting for Particle-Mesh-Ewald Molecular Dynamics Simulations", Journal of Chemical Physics,
61   * 115, 4019-4029 (2001)
62   *
63   * @author Gaurav Chattree
64   * @since 1.0
65   */
66  public class Respa extends Integrator {
67  
68    private static final Logger logger = Logger.getLogger(Respa.class.getName());
69  
70    /**
71     * Number of inner time steps.
72     */
73    private int innerSteps;
74  
75    /**
76     * Inner time step in psec.
77     */
78    private double innerTimeStep;
79  
80    /**
81     * Half the inner time step.
82     */
83    private double halfInnerTimeStep;
84  
85    private double halfStepEnergy = 0;
86  
87    private final double[] innerGradient;
88  
89    /**
90     * Initialize Respa multiple time step molecular dynamics.
91     *
92     * @param state The molecular dynamics state to be operated on.
93     */
94    public Respa(SystemState state) {
95      super(state);
96      innerGradient = new double[state.getNumberOfVariables()];
97      innerSteps = 4;
98      innerTimeStep = dt / innerSteps;
99      halfInnerTimeStep = 0.5 * innerTimeStep;
100   }
101 
102   /**
103    * Get the potential energy of the fast degrees of freedom.
104    *
105    * @return The potential energy of the fast degrees of freedom.
106    */
107   public double getHalfStepEnergy() {
108     return halfStepEnergy;
109   }
110 
111   /**
112    * {@inheritDoc}
113    *
114    * <p>The Respa full-step integration operation.
115    */
116   @Override
117   public void postForce(double[] gradient) {
118     double[] a = state.a();
119     double[] v = state.v();
120     double[] mass = state.getMass();
121     for (int i = 0; i < state.getNumberOfVariables(); i++) {
122       double m = mass[i];
123       if (m > 0.0) {
124         a[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * gradient[i] / m;
125         v[i] += a[i] * dt_2;
126       }
127     }
128   }
129 
130   /**
131    * {@inheritDoc}
132    *
133    * <p>Performs the inner RESPA loop via position Verlet.
134    */
135   @Override
136   public void preForce(Potential potential) {
137     int nVariables = state.getNumberOfVariables();
138     double[] x = state.x();
139     double[] v = state.v();
140     double[] a = state.a();
141     double[] aPrevious = state.aPrevious();
142     double[] mass = state.getMass();
143 
144     // Find half-step velocities via velocity Verlet recursion
145     for (int i = 0; i < nVariables; i++) {
146       if (mass[i] > 0.0) {
147         v[i] += a[i] * dt_2;
148       }
149     }
150 
151     // Initialize accelerations due to fast-evolving forces.
152     potential.setEnergyTermState(Potential.STATE.FAST);
153     halfStepEnergy = potential.energyAndGradient(x, innerGradient);
154     for (int i = 0; i < nVariables; i++) {
155       if (mass[i] > 0.0) {
156         aPrevious[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * innerGradient[i] / mass[i];
157       }
158     }
159 
160     // Complete the inner RESPA loop.
161     for (int j = 0; j < innerSteps; j++) {
162 
163       // Find fast-evolving velocities and positions via Verlet recursion.
164       for (int i = 0; i < nVariables; i++) {
165         if (mass[i] > 0.0) {
166           v[i] += aPrevious[i] * halfInnerTimeStep;
167           x[i] += v[i] * innerTimeStep;
168         }
169       }
170 
171       // Update accelerations from fast varying forces.
172       halfStepEnergy = potential.energyAndGradient(x, innerGradient);
173       for (int i = 0; i < nVariables; i++) {
174         /*
175          Use Newton's second law to get fast-evolving accelerations.
176          Update fast-evolving velocities using the Verlet recursion.
177         */
178         double m = mass[i];
179         if (m > 0.0) {
180           aPrevious[i] = -KCAL_TO_GRAM_ANG2_PER_PS2 * innerGradient[i] / m;
181           v[i] += aPrevious[i] * halfInnerTimeStep;
182         }
183       }
184     }
185 
186     // Revert to computing slowly varying forces.
187     potential.setEnergyTermState(Potential.STATE.SLOW);
188   }
189 
190   /**
191    * Set inner Respa number of time steps.
192    *
193    * @param n Number of inner time steps (must be greater than or equal to 2).
194    */
195   public void setInnerTimeSteps(int n) {
196     if (n < 2) {
197       n = 2;
198     }
199 
200     innerSteps = n;
201 
202     // Update inner time step
203     setTimeStep(dt);
204   }
205 
206   /**
207    * {@inheritDoc}
208    *
209    * <p>Set outer Respa time step.
210    */
211   @Override
212   public void setTimeStep(double dt) {
213     if (dt < 0.0005) {
214       dt = 0.0005;
215     }
216 
217     this.dt = dt;
218     dt_2 = 0.5 * dt;
219     innerTimeStep = dt / innerSteps;
220     halfInnerTimeStep = 0.5 * innerTimeStep;
221 
222     if (logger.isLoggable(Level.FINE)) {
223       logger.fine(
224           format(" Time step set at %f (psec) and inner time step set at %f (psec) \n", this.dt,
225               innerTimeStep));
226     }
227   }
228 
229   /**
230    * {@inheritDoc}
231    */
232   @Override
233   public String toString() {
234     return "Respa";
235   }
236 }