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