1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.potential.parameters;
39
40 import ffx.utilities.FFXProperty;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43
44 import java.util.Arrays;
45 import java.util.Comparator;
46 import java.util.HashMap;
47 import java.util.Map;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50
51 import static ffx.potential.parameters.ForceField.ForceFieldType.BOND;
52 import static ffx.utilities.Constants.ANG_TO_NM;
53 import static ffx.utilities.Constants.KCAL_TO_KJ;
54 import static ffx.utilities.Constants.NM_TO_ANG;
55 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
56 import static ffx.utilities.PropertyGroup.LocalGeometryFunctionalForm;
57 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
58 import static java.lang.Double.parseDouble;
59 import static java.lang.Integer.parseInt;
60 import static java.lang.String.format;
61
62
63
64
65
66
67
68 @FFXProperty(name = "bond", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
69 [2 integers and 2 reals]
70 Provides the values for a single bond stretching parameter.
71 The integer modifiers give the atom class numbers for the two kinds of atoms involved in the bond which is to be defined.
72 The real number modifiers give the force constant value for the bond and the ideal bond length in Angstroms.
73 The default value of 1.0 is used, if the bondunit keyword is not given in the force field parameter file or the keyfile.
74 """)
75 public final class BondType extends BaseType implements Comparator<String> {
76
77 public static final double DEFAULT_BOND_UNIT = 1.0;
78
79
80
81
82 public static final double DEFAULT_BOND_CUBIC = 0.0;
83
84
85
86 public static final double DEFAULT_BOND_QUARTIC = 0.0;
87
88
89
90
91 @FFXProperty(name = "bondunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
92 Sets the scale factor needed to convert the energy value computed by the bond stretching potential into units of kcal/mole.
93 The correct value is force field dependent and typically provided in the header of the master force field parameter file.
94 """)
95 public double bondUnit = DEFAULT_BOND_UNIT;
96
97
98
99
100 @FFXProperty(name = "bond-cubic", propertyGroup = LocalGeometryFunctionalForm, defaultValue = "0.0", description = """
101 Sets the value of the cubic term in the Taylor series expansion form of the bond stretching potential energy.
102 The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient.
103 This term multiplied by the bond stretching energy unit conversion factor, the force constant,
104 and the cube of the deviation of the bond length from its ideal value gives the cubic contribution to the bond stretching energy.
105 The default value in the absence of the bond-cubic keyword is zero; i.e., the cubic bond stretching term is omitted.
106 """)
107 public double cubic = DEFAULT_BOND_CUBIC;
108
109
110
111
112 @FFXProperty(name = "bond-quartic", propertyGroup = LocalGeometryFunctionalForm, defaultValue = "0.0", description = """
113 Sets the value of the quartic term in the Taylor series expansion form of the bond stretching potential energy.
114 The real number modifier gives the value of the coefficient as a multiple of the quadratic coefficient.
115 This term multiplied by the bond stretching energy unit conversion factor, the force constant,
116 and the forth power of the deviation of the bond length from its ideal value gives the quartic contribution to the bond stretching energy.
117 The default value in the absence of the bond-quartic keyword is zero; i.e., the quartic bond stretching term is omitted.
118 """)
119 public double quartic = DEFAULT_BOND_QUARTIC;
120
121
122
123
124 private static final Logger logger = Logger.getLogger(BondType.class.getName());
125
126
127
128 public final int[] atomClasses;
129
130
131
132 public final double forceConstant;
133
134
135
136 public final double distance;
137
138
139
140
141 public final double flatBottomRadius;
142
143
144
145 public BondFunction bondFunction;
146
147
148
149
150
151
152
153
154 public BondType(int[] atomClasses, double forceConstant, double distance) {
155 this(atomClasses, forceConstant, distance, BondFunction.QUARTIC, 0.0);
156 }
157
158
159
160
161
162
163
164
165
166 public BondType(int[] atomClasses, double forceConstant, double distance,
167 BondFunction bondFunction) {
168 this(atomClasses, forceConstant, distance, bondFunction, 0.0);
169 }
170
171
172
173
174
175
176
177
178
179
180 public BondType(int[] atomClasses, double forceConstant, double distance, BondFunction bondFunction,
181 double flatBottomRadius) {
182 super(BOND, sortKey(atomClasses));
183 this.atomClasses = atomClasses;
184 this.forceConstant = forceConstant;
185 this.distance = distance;
186 this.bondFunction = bondFunction;
187 this.flatBottomRadius = flatBottomRadius;
188 assert (flatBottomRadius == 0 || bondFunction == BondFunction.FLAT_BOTTOM_HARMONIC);
189 }
190
191
192
193
194
195
196
197
198
199 public static BondType average(BondType bondType1, BondType bondType2, int[] atomClasses) {
200 if (bondType1 == null || bondType2 == null || atomClasses == null) {
201 return null;
202 }
203 BondType.BondFunction bondFunction = bondType1.bondFunction;
204 if (bondFunction != bondType2.bondFunction) {
205 return null;
206 }
207
208 double forceConstant = (bondType1.forceConstant + bondType2.forceConstant) / 2.0;
209 double distance = (bondType1.distance + bondType2.distance) / 2.0;
210
211 return new BondType(atomClasses, forceConstant, distance, bondFunction);
212 }
213
214
215
216
217
218
219
220
221 public static BondType parse(String input, String[] tokens) {
222 if (tokens.length < 5) {
223 logger.log(Level.WARNING, "Invalid BOND type:\n{0}", input);
224 } else {
225 try {
226 int[] atomClasses = new int[2];
227 atomClasses[0] = parseInt(tokens[1]);
228 atomClasses[1] = parseInt(tokens[2]);
229 double forceConstant = parseDouble(tokens[3]);
230 double distance = parseDouble(tokens[4]);
231 return new BondType(atomClasses, forceConstant, distance);
232 } catch (NumberFormatException e) {
233 String message = "Exception parsing BOND type:\n" + input + "\n";
234 logger.log(Level.SEVERE, message, e);
235 }
236 }
237 return null;
238 }
239
240
241
242
243
244
245
246 public static String sortKey(int[] c) {
247 if (c == null || c.length != 2) {
248 return null;
249 }
250
251 int temp;
252 if (c[1] <= c[0]) {
253 temp = c[1];
254 c[1] = c[0];
255 c[0] = temp;
256 }
257
258 return c[0] + " " + c[1];
259 }
260
261
262
263
264 @Override
265 public int compare(String key1, String key2) {
266 String[] keys1 = key1.split(" ");
267 String[] keys2 = key2.split(" ");
268 int[] c1 = new int[2];
269 int[] c2 = new int[2];
270 for (int i = 0; i < 2; i++) {
271 c1[i] = parseInt(keys1[i]);
272 c2[i] = parseInt(keys2[i]);
273 }
274
275 if (c1[0] < c2[0]) {
276 return -1;
277 } else if (c1[0] > c2[0]) {
278 return 1;
279 } else if (c1[1] < c2[1]) {
280 return -1;
281 } else if (c1[1] > c2[1]) {
282 return 1;
283 }
284
285 return 0;
286 }
287
288
289
290
291 @Override
292 public boolean equals(Object o) {
293 if (this == o) {
294 return true;
295 }
296 if (o == null || getClass() != o.getClass()) {
297 return false;
298 }
299 BondType bondType = (BondType) o;
300 return Arrays.equals(atomClasses, bondType.atomClasses);
301 }
302
303
304
305
306 @Override
307 public int hashCode() {
308 return Arrays.hashCode(atomClasses);
309 }
310
311
312
313
314
315
316 public void incrementClasses(int increment) {
317 for (int i = 0; i < atomClasses.length; i++) {
318 atomClasses[i] += increment;
319 }
320 setKey(sortKey(atomClasses));
321 }
322
323
324
325
326
327
328
329 public BondType patchClasses(HashMap<AtomType, AtomType> typeMap) {
330
331 int count = 0;
332 int len = atomClasses.length;
333
334
335 for (AtomType newType : typeMap.keySet()) {
336
337 for (int atomClass : atomClasses) {
338 if (atomClass == newType.atomClass) {
339 count++;
340 }
341 }
342 }
343
344
345 if (count == 1) {
346 int[] newClasses = Arrays.copyOf(atomClasses, len);
347 for (AtomType newType : typeMap.keySet()) {
348 for (int i = 0; i < len; i++) {
349 if (atomClasses[i] == newType.atomClass) {
350 AtomType knownType = typeMap.get(newType);
351 newClasses[i] = knownType.atomClass;
352 }
353 }
354 }
355
356 return new BondType(newClasses, forceConstant, distance, bondFunction);
357 }
358 return null;
359 }
360
361 public void setBondFunction(BondFunction bondFunction) {
362 this.bondFunction = bondFunction;
363 }
364
365
366
367
368
369
370 @Override
371 public String toString() {
372 return format("bond %5d %5d %7.2f %7.4f", atomClasses[0], atomClasses[1], forceConstant,
373 distance);
374 }
375
376 public static Element getXMLForce(Document doc, ForceField forceField) {
377 Map<String, BondType> btMap = forceField.getBondTypes();
378 if (!btMap.values().isEmpty()) {
379 Element node = doc.createElement("AmoebaBondForce");
380 node.setAttribute("bond-cubic", format("%f", forceField.getDouble("bond-cubic", DEFAULT_BOND_CUBIC) * NM_TO_ANG));
381 node.setAttribute("bond-quartic", format("%f", forceField.getDouble("bond-quartic", DEFAULT_BOND_QUARTIC) * NM_TO_ANG * NM_TO_ANG));
382 for (BondType bondType : btMap.values()) {
383 node.appendChild(bondType.toXML(doc));
384 }
385 return node;
386 }
387 return null;
388 }
389
390
391
392
393 public Element toXML(Document doc) {
394 Element node = doc.createElement("Bond");
395 node.setAttribute("class1", format("%d", atomClasses[0]));
396 node.setAttribute("class2", format("%d", atomClasses[1]));
397 node.setAttribute("length", format("%f", distance * ANG_TO_NM));
398 node.setAttribute("k", format("%f", forceConstant * NM_TO_ANG * NM_TO_ANG * KCAL_TO_KJ));
399 return node;
400 }
401
402
403
404
405
406 public enum BondFunction {
407
408
409 HARMONIC("0.5*k*(r-r0)^2", false),
410
411
412 QUARTIC("0.5*k*dv^2*((1+cubic)*dv+(1+quartic)*dv^2);dv=r-r0", false),
413
414
415 FLAT_BOTTOM_HARMONIC(
416 "0.5*k*dv^2;dv=step(dv)*step(dv-fb)*(dv-fb)" + "+step(-dv)*step(-dv-fb)*(-dv-fb);dv=r-r0",
417 true),
418
419
420 FLAT_BOTTOM_QUARTIC("0.5*k*dv^2*((1+cubic)*dv+(1+quartic)*dv^2);"
421 + "dv=step(dv)*step(dv-fb)*(dv-fb)+step(-dv)*step(-dv-fb)*(-dv-fb);dv=r-r0", true);
422
423
424
425
426 private final String mathematicalForm;
427
428
429
430
431 private final boolean hasFlatBottom;
432
433
434
435
436
437
438
439 BondFunction(String mathForm, boolean flatBottom) {
440 this.mathematicalForm = mathForm;
441 this.hasFlatBottom = flatBottom;
442 }
443
444
445
446
447
448
449 public boolean hasFlatBottom() {
450 return hasFlatBottom;
451 }
452
453
454
455
456
457
458 public String toMathematicalForm() {
459 return mathematicalForm;
460 }
461 }
462 }