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.mc;
39  
40  import ffx.algorithms.AlgorithmListener;
41  import ffx.algorithms.cli.DynamicsOptions;
42  import ffx.algorithms.dynamics.MDVerbosity;
43  import ffx.algorithms.dynamics.MDWriteAction;
44  import ffx.algorithms.dynamics.MolecularDynamics;
45  import ffx.algorithms.dynamics.MolecularDynamicsOpenMM;
46  import ffx.algorithms.dynamics.thermostats.Thermostat;
47  import ffx.crystal.Crystal;
48  import ffx.numerics.Potential;
49  import ffx.potential.MolecularAssembly;
50  import ffx.potential.SystemState;
51  import ffx.potential.parsers.DYNFilter;
52  import ffx.utilities.Constants;
53  
54  import javax.annotation.Nullable;
55  import java.io.File;
56  import java.util.EnumSet;
57  import java.util.logging.Level;
58  import java.util.logging.Logger;
59  
60  import static java.lang.Math.abs;
61  import static java.lang.Math.min;
62  import static java.lang.String.format;
63  
64  /**
65   * Use MD as a coordinate based MC move.
66   *
67   * @author Mallory R. Tollefson
68   */
69  public class MDMove implements MCMove {
70  
71    private static final Logger logger = Logger.getLogger(MDMove.class.getName());
72  
73    /**
74     * THe MD instance that executes the move.
75     */
76    private final MolecularDynamics molecularDynamics;
77    /**
78     * Number of MD steps per move.
79     */
80    private final long mdSteps;
81    /**
82     * Time step in femtoseconds.
83     */
84    private final double timeStep;
85    /**
86     * Print interval in picoseconds.
87     */
88    private final double printInterval;
89    /**
90     * Temperature in Kelvin.
91     */
92    private final double temperature;
93    /**
94     * Snapshot appending interval in psec.
95     */
96    private final double saveInterval;
97    /**
98     * Potential to operate on.
99     */
100   private final Potential potential;
101   /**
102    * Number of MD moves.
103    */
104   private int mdMoveCounter = 0;
105   /**
106    * The total energy change for the current move.
107    */
108   private double energyChange;
109   /**
110    * Absolute energy drift over all moves.
111    */
112   private double energyDriftAbs;
113   /**
114    * Net energy drift over all moves.
115    */
116   private double energyDriftNet;
117   /**
118    * Kinetic energy at the start of the move.
119    */
120   private double initialKinetic;
121   /**
122    * Potential energy at the start of the move.
123    */
124   private double initialPotential;
125   /**
126    * Total energy at the start of the move.
127    */
128   private double initialTotal;
129 
130   private File dynRestartFile;
131 
132   /**
133    * Constructor for MDMove.
134    *
135    * @param assembly        a {@link ffx.potential.MolecularAssembly} object.
136    * @param potentialEnergy a {@link ffx.numerics.Potential} object.
137    * @param listener        a {@link ffx.algorithms.AlgorithmListener} object.
138    * @param dynamics        CLI object containing key MD information.
139    * @param stepsPerCycle   Number of MD steps per MC cycle.
140    * @param dynRestartFile  File to read restart from.
141    */
142   public MDMove(MolecularAssembly assembly, Potential potentialEnergy, AlgorithmListener listener,
143                 DynamicsOptions dynamics, long stepsPerCycle, @Nullable File dynRestartFile) {
144     this.potential = potentialEnergy;
145     molecularDynamics = MolecularDynamics.dynamicsFactory(assembly, potentialEnergy, listener,
146         dynamics.thermostat, dynamics.integrator);
147     molecularDynamics.setAutomaticWriteouts(false);
148 
149     timeStep = dynamics.getDt();
150     double dtPs = timeStep * Constants.FSEC_TO_PSEC;
151     mdSteps = stepsPerCycle;
152 
153     molecularDynamics.setVerbosityLevel(MDVerbosity.QUIET);
154     molecularDynamics.setObtainVelAcc(false);
155     molecularDynamics.setRestartFrequency(dynamics.getCheckpoint());
156     this.saveInterval = dynamics.getSnapshotInterval();
157 
158     double requestedPrint = dynamics.getReport();
159     double maxPrintInterval = dtPs * mdSteps;
160     // Log at least once/cycle.
161     printInterval = min(requestedPrint, maxPrintInterval);
162     this.temperature = dynamics.getTemperature();
163 
164     this.dynRestartFile = dynRestartFile;
165     // Load the restart file if it exists.
166     if (this.dynRestartFile != null && this.dynRestartFile.exists()) {
167       molecularDynamics.setVerbosityLevel(MDVerbosity.SILENT);
168       molecularDynamics.dynamic(1, timeStep, printInterval, saveInterval, temperature, false, dynRestartFile);
169       collectEnergies();
170       revertMove();
171       molecularDynamics.setVerbosityLevel(MDVerbosity.QUIET);
172     }
173   }
174 
175   /**
176    * Get the total energy change for the current move.
177    *
178    * @return Total energy change.
179    */
180   public double getEnergyChange() {
181     return energyChange;
182   }
183 
184   public double getInitialKinetic() {
185     return initialKinetic;
186   }
187 
188   public double getInitialPotential() {
189     return initialPotential;
190   }
191 
192   public double getInitialTotal() {
193     return initialTotal;
194   }
195 
196   /**
197    * getKineticEnergy.
198    *
199    * @return a double.
200    */
201   public double getKineticEnergy() {
202     return molecularDynamics.getKineticEnergy();
203   }
204 
205   public MolecularDynamics getMD() {
206     return molecularDynamics;
207   }
208 
209   /**
210    * getPotentialEnergy.
211    *
212    * @return a double.
213    */
214   public double getPotentialEnergy() {
215     return molecularDynamics.getPotentialEnergy();
216   }
217 
218   /**
219    * {@inheritDoc}
220    */
221   @Override
222   public void move() {
223     move(MDVerbosity.QUIET);
224   }
225 
226   /**
227    * Performs an MDMove.
228    *
229    * @param verbosityLevel How verbose to be.
230    */
231   public void move(MDVerbosity verbosityLevel) {
232     MDVerbosity origLevel = molecularDynamics.getVerbosityLevel();
233     molecularDynamics.setVerbosityLevel(verbosityLevel);
234     if (mdMoveCounter == 0 && dynRestartFile != null && dynRestartFile.exists()) {
235       molecularDynamics.dynamic(mdSteps, timeStep, printInterval, saveInterval, temperature, false, dynRestartFile);
236     } else {
237       molecularDynamics.dynamic(mdSteps, timeStep, printInterval, saveInterval, temperature, true, null);
238     }
239     mdMoveCounter++;
240     collectEnergies();
241     energyChange = molecularDynamics.getTotalEnergy() - initialTotal;
242 
243     if (molecularDynamics instanceof MolecularDynamicsOpenMM && logger.isLoggable(Level.FINE)) {
244       energyDriftNet += energyChange;
245       energyDriftAbs += abs(energyChange);
246       double energyDriftAverageNet = energyDriftNet / mdMoveCounter;
247       double energyDriftAverageAbs = energyDriftAbs / mdMoveCounter;
248       logger.fine(format(" Mean signed/unsigned energy drift:                   %8.4f/%8.4f",
249           energyDriftAverageNet, energyDriftAverageAbs));
250 
251       double dt = molecularDynamics.getTimeStep();
252       int intervalSteps = molecularDynamics.getIntervalSteps();
253       int nAtoms = potential.getNumberOfVariables() / 3;
254       // TODO: Determine if the *1000 factor is an old artifact of MolecularDynamicsOpenMM being the
255       // one thing which (used to) store dt in fsec.
256       double normalizedEnergyDriftNet = (energyDriftAverageNet / (dt * intervalSteps * nAtoms)) * 1000;
257       double normalizedEnergyDriftAbs = (energyDriftAverageAbs / (dt * intervalSteps * nAtoms)) * 1000;
258       logger.fine(format(" Mean singed/unsigned energy drift per psec per atom: %8.4f/%8.4f\n",
259           normalizedEnergyDriftNet, normalizedEnergyDriftAbs));
260     }
261     molecularDynamics.setVerbosityLevel(origLevel);
262   }
263 
264   /**
265    * {@inheritDoc}
266    */
267   @Override
268   public void revertMove() {
269     try {
270       molecularDynamics.revertState();
271     } catch (Exception ex) {
272       logger.severe(" The MD state could not be reverted.");
273     }
274   }
275 
276   public void setMDIntervalSteps(int intervalSteps) {
277     molecularDynamics.setIntervalSteps(intervalSteps);
278   }
279 
280   /**
281    * Write restart and trajectory files if the provided step matches the frequency.
282    *
283    * @param mdStep      MD step (not MC cycle number) to write files (if any) for.
284    * @param trySnapshot If false, do not write snapshot even if the time step is correct.
285    * @param tryRestart  If false, do not write a restart file even if the time step is correct.
286    * @return Returns the write actions.
287    */
288   public EnumSet<MDWriteAction> writeFilesForStep(long mdStep, boolean trySnapshot, boolean tryRestart) {
289     return molecularDynamics.writeFilesForStep(mdStep, trySnapshot, tryRestart);
290   }
291 
292   private void collectEnergies() {
293     initialTotal = molecularDynamics.getInitialTotalEnergy();
294     initialKinetic = molecularDynamics.getInitialKineticEnergy();
295     initialPotential = molecularDynamics.getInitialPotentialEnergy();
296     // If total energy is non-tiny, assert that kinetic + potential = total to within tolerance.
297     assert abs(initialTotal) < 1.0E-3
298         || abs((initialKinetic + initialPotential - initialTotal) / initialTotal) < 1.0E-7;
299   }
300 }