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-2025.
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.potential.terms;
39  
40  import ffx.potential.bonded.Atom;
41  import ffx.potential.bonded.Bond;
42  import ffx.potential.bonded.BondedTerm;
43  import ffx.potential.bonded.Torsion;
44  import ffx.potential.parameters.ForceField;
45  import ffx.potential.parameters.TorsionType;
46  import org.apache.commons.configuration2.CompositeConfiguration;
47  
48  import java.util.ArrayList;
49  import java.util.Collection;
50  import java.util.Collections;
51  import java.util.EmptyStackException;
52  import java.util.List;
53  import java.util.logging.Logger;
54  
55  /**
56   * Restrain-Torsion potential energy term using {@link ffx.potential.bonded.Torsion} instances.
57   * Method names are specific to restrain torsions for clarity.
58   */
59  public class RestrainTorsionPotentialEnergy extends EnergyTerm {
60  
61    private static final Logger logger = Logger.getLogger(RestrainTorsionPotentialEnergy.class.getName());
62  
63    /**
64     * Internal list of Torsion instances for restrain torsions.
65     */
66    private final List<Torsion> restrainTorsions = new ArrayList<>();
67  
68    /**
69     * Create a RestrainTorsionPotentialEnergy with the provided name.
70     *
71     * @param name Name for this term.
72     */
73    public RestrainTorsionPotentialEnergy(String name) {
74      super(name);
75    }
76  
77    /**
78     * Create a RestrainTorsionPotentialEnergy with the provided name and force group.
79     *
80     * @param name       Name for this term.
81     * @param forceGroup Integer force group identifier.
82     */
83    public RestrainTorsionPotentialEnergy(String name, int forceGroup) {
84      super(name, forceGroup);
85    }
86  
87    /**
88     * Create a RestrainTorsionPotentialEnergy initialized with a list of torsions and force group.
89     *
90     * @param name             Name for this term.
91     * @param forceGroup       Force group identifier.
92     * @param restrainTorsions List of Torsion instances to add (null-safe).
93     */
94    public RestrainTorsionPotentialEnergy(String name, int forceGroup, List<Torsion> restrainTorsions) {
95      super(name, forceGroup);
96      if (restrainTorsions != null) {
97        Collections.sort(restrainTorsions);
98        this.restrainTorsions.addAll(restrainTorsions);
99        logger.info(String.format("  Restrain Torsions:                   %10d", getNumberOfRestrainTorsions()));
100     }
101   }
102 
103   /**
104    * {@inheritDoc}
105    */
106   @Override
107   public int getNumberOfTerms() {
108     return getNumberOfRestrainTorsions();
109   }
110 
111   /**
112    * {@inheritDoc}
113    */
114   @Override
115   public BondedTerm[] getBondedTermsArray() {
116     return getRestrainTorsionArray();
117   }
118 
119   /**
120    * Create a RestrainTorsionPotentialEnergy initialized with a collection of torsions.
121    *
122    * @param name             Name for this term (may be null).
123    * @param restrainTorsions Collection of Torsion instances to add (null-safe).
124    */
125   public RestrainTorsionPotentialEnergy(String name, Collection<Torsion> restrainTorsions) {
126     super(name);
127     if (restrainTorsions != null) {
128       this.restrainTorsions.addAll(restrainTorsions);
129     }
130   }
131 
132   /**
133    * Add a single restrain torsion.
134    *
135    * @param torsion Torsion to add (ignored if null).
136    * @return true if added.
137    */
138   public boolean addRestrainTorsion(Torsion torsion) {
139     if (torsion == null) {
140       return false;
141     }
142     return restrainTorsions.add(torsion);
143 
144   }
145 
146   /**
147    * Add an array of restrain torsions.
148    *
149    * @param torsions Array of Torsion instances to add.
150    * @return true if added.
151    */
152   public boolean addRestrainTorsions(Torsion[] torsions) {
153     if (torsions == null) {
154       return false;
155     }
156     Collections.addAll(this.restrainTorsions, torsions);
157     return true;
158   }
159 
160   /**
161    * Add a list of restrain torsions.
162    *
163    * @param torsions List of Torsion instances to add.
164    * @return true if added.
165    */
166   public boolean addRestrainTorsions(List<Torsion> torsions) {
167     if (torsions == null) {
168       return false;
169     }
170     this.restrainTorsions.addAll(torsions);
171     return true;
172   }
173 
174   /**
175    * Remove a restrain torsion.
176    *
177    * @param torsion Torsion to remove (ignored if null).
178    * @return true if removed.
179    */
180   public boolean removeRestrainTorsion(Torsion torsion) {
181     if (torsion == null) {
182       return false;
183     }
184     return restrainTorsions.remove(torsion);
185   }
186 
187   /**
188    * Get a restrain torsion at an index.
189    *
190    * @param index Index.
191    * @return Torsion.
192    * @throws IndexOutOfBoundsException if invalid index.
193    */
194   public Torsion getRestrainTorsion(int index) {
195     return restrainTorsions.get(index);
196   }
197 
198   /**
199    * Get an unmodifiable view of restrain torsions.
200    *
201    * @return List of Torsion.
202    */
203   public List<Torsion> getRestrainTorsions() {
204     return Collections.unmodifiableList(restrainTorsions);
205   }
206 
207   /**
208    * Get an array of restrain torsions.
209    *
210    * @return Array of Torsion.
211    */
212   public Torsion[] getRestrainTorsionArray() {
213     return restrainTorsions.toArray(new Torsion[0]);
214   }
215 
216   /**
217    * Get the number of restrain torsions.
218    *
219    * @return Number of restrain torsions.
220    */
221   public int getNumberOfRestrainTorsions() {
222     return restrainTorsions.size();
223   }
224 
225   @Override
226   public String toPDBString() {
227     if (getNumberOfRestrainTorsions() <= 0) {
228       return "";
229     }
230     return String.format("REMARK   3   %s %g (%d)\n", "RESTRAIN TORSION           : ", getEnergy(), getNumberOfRestrainTorsions());
231   }
232 
233   @Override
234   public String toString() {
235     return String.format("  %s %20.8f %12d %12.3f\n", "Restrain Torsion  ",
236         getEnergy(), getNumberOfRestrainTorsions(), getTime());
237   }
238 
239   /**
240    * Log the details of Restrain Torsion interactions.
241    */
242   @Override
243   public void log() {
244     if (getNumberOfRestrainTorsions() <= 0) {
245       return;
246     }
247     logger.info("\n Restrain Torsion Interactions:");
248     for (Torsion torsion : getRestrainTorsions()) {
249       logger.info(" Restrain Torsion \t" + torsion.toString());
250     }
251   }
252 
253   /**
254    * Method to parse the restrain-torsion records.
255    *
256    * @param properties Configuration properties.
257    * @param forceField Force field properties.
258    * @param atoms      Array of atoms in the system.
259    * @param torsions   Array of torsions in the system.
260    * @return An array of restrain torsions, or null if none were found.
261    */
262   public static Torsion[] configureRestrainTorsions(CompositeConfiguration properties,
263                                                     ForceField forceField,
264                                                     Atom[] atoms,
265                                                     Torsion[] torsions) {
266     StringBuilder restrainLog = new StringBuilder("\n  Restrain-Torsions");
267 
268     String[] restrainTorsions = properties.getStringArray("restrain-torsion");
269     double torsionUnits = forceField.getDouble("TORSIONUNIT", TorsionType.DEFAULT_TORSION_UNIT);
270     List<Torsion> restrainTorsionList = new ArrayList<>(restrainTorsions.length);
271     for (String restrainString : restrainTorsions) {
272       // Add the key back to the input line.
273       restrainString = "restrain-torsion " + restrainString;
274       // Split the line on the pound symbol to remove comments.
275       String input = restrainString.split("#+")[0];
276       // Split the line on whitespace.
277       String[] tokens = input.trim().split(" +");
278       // Restrain torsion records have a similar form as torsion records.
279       // The first four tokens are atom indices instead of atom classes.
280       TorsionType torsionType = TorsionType.parse(input, tokens);
281       torsionType.torsionUnit = torsionUnits;
282 
283       // Collect the atom indices.
284       int[] atomIndices = torsionType.atomClasses;
285       int ai1 = atomIndices[0] - 1;
286       int ai2 = atomIndices[1] - 1;
287       int ai3 = atomIndices[2] - 1;
288       int ai4 = atomIndices[3] - 1;
289       Atom a1 = atoms[ai1];
290       Atom a2 = atoms[ai2];
291       Atom a3 = atoms[ai3];
292       Atom a4 = atoms[ai4];
293 
294       // Collect the bonds between the atoms making up the restrain torsion.
295       Bond firstBond = a1.getBond(a2);
296       Bond middleBond = a2.getBond(a3);
297       Bond lastBond = a3.getBond(a4);
298       Torsion torsion = new Torsion(firstBond, middleBond, lastBond);
299       torsion.setTorsionType(torsionType);
300       restrainTorsionList.add(torsion);
301       restrainLog.append("\n   ").append(torsion);
302     }
303 
304     // Apply the property "restrain-all-torsions".
305     restrainTorsionList.addAll(restrainAllTorsions(torsions, properties));
306 
307     if (!restrainTorsionList.isEmpty()) {
308       logger.info(restrainLog.toString());
309       return restrainTorsionList.toArray(new Torsion[0]);
310     } else {
311       return null;
312     }
313   }
314 
315   /**
316    * Method to parse the restrain-torsion-cos records.
317    *
318    * @param torsions   Array of torsions.
319    * @param properties Configuration properties.
320    * @return An array of restrain torsions, or null if none were found.
321    */
322   private static List<Torsion> restrainAllTorsions(Torsion[] torsions,
323                                                    CompositeConfiguration properties) {
324 
325     List<Torsion> restrainTorsionList = new ArrayList<>();
326 
327     String restrainTorsions;
328     boolean noHydrogenTorsions = false;
329     if (properties.containsKey("restrain-all-torsions")) {
330       restrainTorsions = properties.getString("restrain-all-torsions");
331     } else if (properties.containsKey("restrain-heavy-torsions")) {
332       restrainTorsions = properties.getString("restrain-heavy-torsions");
333       noHydrogenTorsions = true;
334     } else {
335       return restrainTorsionList;
336     }
337 
338     logger.info(" Restrain torsions " + restrainTorsions);
339     // Split the line on the pound symbol to remove comments.
340     String input = restrainTorsions.split("#+")[0];
341     // Split the line on whitespace.
342     String[] tokens = input.trim().split(" +");
343     double forceConstant = Double.parseDouble(tokens[0]);
344     logger.info(" Force constant %10.6f".formatted(forceConstant));
345 
346     for (Torsion torsion : torsions) {
347       // Skip torsions with hydrogen
348       if (noHydrogenTorsions && torsion.containsHydrogen()) {
349         continue;
350       }
351 
352       // Re-use current atom classes (they will not be used).
353       TorsionType torsionType = torsion.torsionType;
354       int[] atomClasses = torsionType.atomClasses.clone();
355       // Single term in the Fourier series.
356       double[] amplitude = {forceConstant};
357       double[] phase = {torsion.measure() + 180.0};
358       int[] periodicity = {1};
359 
360       // Create the new TorsionType for the restraint.
361       TorsionType newTorsionType = new TorsionType(atomClasses, amplitude, phase, periodicity);
362       newTorsionType.torsionUnit = torsionType.torsionUnit;
363 
364       Bond b1 = torsion.getBond(0);
365       Bond b2 = torsion.getBond(1);
366       Bond b3 = torsion.getBond(2);
367       Torsion newTorsion = new Torsion(b1, b2, b3);
368       newTorsion.setTorsionType(newTorsionType);
369 
370       restrainTorsionList.add(newTorsion);
371     }
372 
373     return restrainTorsionList;
374   }
375 }