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.optimize.anneal;
39  
40  /**
41   * Composite annealing schedule with flat ends (i.e. spends extra time at the low and high
42   * temperatures).
43   *
44   * @author Jacob M. Litman
45   * @author Michael J. Schnieders
46   * @since 1.0
47   */
48  public class FlatEndAnnealSchedule implements AnnealingSchedule {
49  
50    private final AnnealingSchedule middle;
51    private final double tLow;
52    private final double tHigh;
53    private final double lenBefore;
54    private final double lenAfter;
55    private final int totWindows;
56    private final boolean useBefore;
57    private final boolean useAfter;
58    private final String description;
59  
60    /**
61     * Creates a flat-ended annealing schedule based on a provided schedule for the middle, which is
62     * flat for some number of steps at the ends.
63     *
64     * @param middle Annealing schedule to use in the middle.
65     * @param tLow Low temperature to use at the last window.
66     * @param tHigh High temperature to use at the first window.
67     * @param lengthBefore Relative length of the first ("flat") tempering window at the high
68     *     temperature. 0 disables this end.
69     * @param lengthAfter Relative length of the last ("flat") tempering window at the low
70     *     temperature. 0 disables this end.
71     */
72    public FlatEndAnnealSchedule(AnnealingSchedule middle, double tLow, double tHigh,
73        double lengthBefore, double lengthAfter) {
74      assert tLow < tHigh;
75      assert tLow >= 0;
76      assert Double.isFinite(tHigh);
77      assert lengthBefore >= 0;
78      assert lengthAfter >= 0;
79  
80      this.middle = middle;
81      this.tLow = tLow;
82      this.tHigh = tHigh;
83      this.lenBefore = lengthBefore;
84      this.lenAfter = lengthAfter;
85  
86      int nWin = middle.getNumWindows();
87      useBefore = lengthBefore > 0;
88      if (useBefore) {
89        ++nWin;
90      }
91      useAfter = lengthAfter > 0;
92      if (useAfter) {
93        ++nWin;
94      }
95      totWindows = nWin;
96  
97      StringBuilder sb = new StringBuilder(
98          String.format("Flat-ended annealing schedule: starts at %10.4g K, ends at %10.4g K.", tHigh,
99              tLow));
100     if (useBefore) {
101       sb.append(
102           String.format("\nMain annealing is preceded by a %9.3f-long window at %10.4g K", lenBefore,
103               tHigh));
104     }
105     if (useAfter) {
106       sb.append(
107           String.format("\nMain annealing is followed by a %9.3f-long window at %10.4g K", lenAfter,
108               tLow));
109     }
110     sb.append("\nMain annealing schedule: ").append(middle);
111     this.description = sb.toString();
112   }
113 
114   @Override
115   public double getHighTemp() {
116     return tHigh;
117   }
118 
119   @Override
120   public double getLowTemp() {
121     return tLow;
122   }
123 
124   @Override
125   public int getNumWindows() {
126     return totWindows;
127   }
128 
129   @Override
130   public double getTemperature(int i) {
131     assert i >= 0 && i < totWindows;
132     if (i == 0 && useBefore) {
133       return tHigh;
134     } else if (i == (totWindows - 1) && useAfter) {
135       return tLow;
136     } else {
137       i = useBefore ? i - 1 : i;
138       return middle.getTemperature(i);
139     }
140   }
141 
142   @Override
143   public double[] getTemperatures() {
144     double[] temps = new double[totWindows];
145     for (int i = 0; i < totWindows; i++) {
146       temps[i] = getTemperature(i);
147     }
148     return temps;
149   }
150 
151   @Override
152   public double maxWindowLength() {
153     return Math.max(Math.max(lenBefore, lenAfter), middle.maxWindowLength());
154   }
155 
156   @Override
157   public double minWindowLength() {
158     double len = middle.minWindowLength();
159     if (useAfter) {
160       len = Math.min(len, lenAfter);
161     }
162     if (useBefore) {
163       len = Math.min(len, lenBefore);
164     }
165     return len;
166   }
167 
168   @Override
169   public String toString() {
170     return description;
171   }
172 
173   @Override
174   public double totalWindowLength() {
175     return lenBefore + lenAfter + middle.totalWindowLength();
176   }
177 
178   @Override
179   public double windowLength(int window) {
180     assert window >= 0 && window < totWindows;
181 
182     if (useBefore && window == 0) {
183       return lenBefore;
184     } else if (useAfter && window == (totWindows - 1)) {
185       return lenAfter;
186     } else {
187       window = useBefore ? window - 1 : window;
188       return middle.windowLength(window);
189     }
190   }
191 }