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 }