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