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.BondedTerm;
41 import ffx.potential.bonded.Torsion;
42
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.logging.Logger;
48
49 import static java.lang.String.format;
50
51 /**
52 * Torsion potential energy term using {@link ffx.potential.bonded.Torsion} instances.
53 *
54 * @author Michael J. Schnieders
55 * @since 1.0
56 */
57 public class TorsionPotentialEnergy extends EnergyTerm {
58
59 private static final Logger logger = Logger.getLogger(TorsionPotentialEnergy.class.getName());
60
61
62 /**
63 * Internal list of Torsion instances.
64 */
65 private final List<Torsion> torsions = new ArrayList<>();
66
67 /**
68 * Create a TorsionPotentialEnergy with the provided name.
69 *
70 * @param name Name for this term.
71 */
72 public TorsionPotentialEnergy(String name) {
73 super(name);
74 }
75
76 /**
77 * Create a TorsionPotentialEnergy with the provided name and force group.
78 *
79 * @param name Name for this term.
80 * @param forceGroup Integer force group identifier.
81 */
82 public TorsionPotentialEnergy(String name, int forceGroup) {
83 super(name, forceGroup);
84 }
85
86 /**
87 * Create a TorsionPotentialEnergy initialized with a list of torsions and force group.
88 *
89 * @param name Name for this term.
90 * @param forceGroup Force group identifier.
91 * @param torsions List of Torsion instances to add (null-safe).
92 */
93 public TorsionPotentialEnergy(String name, int forceGroup, List<Torsion> torsions) {
94 super(name, forceGroup);
95 if (torsions != null) {
96 Collections.sort(torsions);
97 this.torsions.addAll(torsions);
98 logger.info(format(" Torsions: %10d", getNumberOfTorsions()));
99 }
100 }
101
102 /**
103 * {@inheritDoc}
104 */
105 @Override
106 public int getNumberOfTerms() {
107 return getNumberOfTorsions();
108 }
109
110 /**
111 * {@inheritDoc}
112 */
113 @Override
114 public BondedTerm[] getBondedTermsArray() {
115 return getTorsionArray();
116 }
117
118 /**
119 * Create a TorsionPotentialEnergy initialized with a collection of torsions.
120 *
121 * @param name Name for this term (may be null).
122 * @param torsions Collection of Torsion instances to add (null-safe).
123 */
124 public TorsionPotentialEnergy(String name, Collection<Torsion> torsions) {
125 super(name);
126 if (torsions != null) {
127 this.torsions.addAll(torsions);
128 }
129 }
130
131 /**
132 * Add a Torsion to this term.
133 *
134 * @param torsion Torsion to add (ignored if null).
135 * @return true if it was added.
136 */
137 public boolean addTorsion(Torsion torsion) {
138 if (torsion == null) {
139 return false;
140 }
141 return torsions.add(torsion);
142 }
143
144 /**
145 * Add an array of Torsions to this term.
146 *
147 * @param torsions Array of Torsion instances to add.
148 * @return true if they were added.
149 */
150 public boolean addTorsions(Torsion[] torsions) {
151 if (torsions == null) {
152 return false;
153 }
154 Collections.addAll(this.torsions, torsions);
155 return true;
156 }
157
158 /**
159 * Add a list of Torsions to this term.
160 *
161 * @param torsions List of Torsion instances to add.
162 * @return true if they were added.
163 */
164 public boolean addTorsions(List<Torsion> torsions) {
165 if (torsions == null) {
166 return false;
167 }
168 this.torsions.addAll(torsions);
169 return true;
170 }
171
172 /**
173 * Remove a Torsion from this term.
174 *
175 * @param torsion Torsion to remove (ignored if null).
176 * @return true if it was present and removed.
177 */
178 public boolean removeTorsion(Torsion torsion) {
179 if (torsion == null) {
180 return false;
181 }
182 return torsions.remove(torsion);
183 }
184
185 /**
186 * Get the Torsion at a given index.
187 *
188 * @param index Index in the internal list.
189 * @return Torsion at the specified index.
190 * @throws IndexOutOfBoundsException if index is invalid.
191 */
192 public Torsion getTorsion(int index) {
193 return torsions.get(index);
194 }
195
196 /**
197 * Get an unmodifiable view of the Torsions in this term.
198 *
199 * @return Unmodifiable List of Torsions.
200 */
201 public List<Torsion> getTorsions() {
202 return Collections.unmodifiableList(torsions);
203 }
204
205 /**
206 * Get an array of Torsions in this term.
207 *
208 * @return Array of Torsions.
209 */
210 public Torsion[] getTorsionArray() {
211 return torsions.toArray(new Torsion[0]);
212 }
213
214 /**
215 * Get the number of Torsions in this term.
216 *
217 * @return The number of Torsions.
218 */
219 public int getNumberOfTorsions() {
220 return torsions.size();
221 }
222
223 /**
224 * Set the lambda value for all Torsions in this term.
225 * @param lambda Lambda value to set for all Torsions.
226 */
227 public void setLambda(double lambda) {
228 for (Torsion torsion : torsions) {
229 torsion.setLambda(lambda);
230 }
231 }
232
233 /**
234 * Get the energy contribution from all Torsions in this term.
235 * @return Total energy from all Torsions.
236 */
237 public double getdEdL() {
238 double dEdL = 0.0;
239 for (Torsion torsion : getTorsions()) {
240 dEdL += torsion.getdEdL();
241 }
242 return dEdL;
243 }
244
245 /**
246 * Get the energy contribution from all Torsions in this term.
247 * @return Total energy from all Torsions.
248 */
249 public double getd2EdL2() {
250 double d2EdLambda2 = 0.0;
251 for (Torsion torsion : getTorsions()) {
252 d2EdLambda2 += torsion.getd2EdL2();
253 }
254 return d2EdLambda2;
255 }
256
257 /**
258 * Log the details of Torsion interactions.
259 */
260 @Override
261 public void log() {
262 if (getNumberOfTorsions() <= 0) {
263 return;
264 }
265 logger.info("\n Torsion Angle Interactions:");
266 for (Torsion torsion : getTorsions()) {
267 logger.info(" Torsion \t" + torsion.toString());
268 }
269 }
270
271 @Override
272 public String toPDBString() {
273 if (getNumberOfTorsions() <= 0) {
274 return "";
275 }
276 return format("REMARK 3 %s %g (%d)\n", "TORSIONAL ANGLE : ", getEnergy(), getNumberOfTorsions());
277 }
278
279 @Override
280 public String toString() {
281 return format(" %s %20.8f %12d %12.3f\n", "Torsional Angle ",
282 getEnergy(), getNumberOfTorsions(), getTime());
283 }
284 }