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.STRTORS;
52 import static ffx.utilities.Constants.ANG_TO_NM;
53 import static ffx.utilities.Constants.KCAL_TO_KJ;
54 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
55 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
56 import static java.lang.Double.parseDouble;
57 import static java.lang.Integer.parseInt;
58 import static java.lang.String.format;
59 import static java.util.Arrays.copyOf;
60
61
62
63
64
65
66
67 @FFXProperty(name = "strtors", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
68 [2 integers and 1 real]
69 Provides the values for a single stretch-torsion cross term potential parameter.
70 The two integer modifiers give the atom class numbers for the atoms involved in the central bond of the torsional angles to be parameterized.
71 The real modifier gives the value of the stretch-torsion force constant for all torsional angles with the defined atom classes for the central bond.
72 The default units for the stretch-torsion force constant can be controlled via the strtorunit keyword.
73 """)
74 public final class StretchTorsionType extends BaseType implements Comparator<String> {
75
76 public static final double DEFAULT_STRTOR_UNIT = 1.0;
77
78
79
80
81 @FFXProperty(name = "strtorunit", propertyGroup = EnergyUnitConversion, defaultValue = "1.0", description = """
82 Sets the scale factor needed to convert the energy value computed by the bond stretching-torsional
83 angle cross term potential into units of kcal/mole. The correct value is force field dependent and typically
84 provided in the header of the master force field parameter file.
85 """)
86 public double strTorUnit = DEFAULT_STRTOR_UNIT;
87
88
89
90
91 private static final Logger logger = Logger.getLogger(StretchTorsionType.class.getName());
92
93
94
95 public final int[] atomClasses;
96
97
98
99 public final double[] forceConstants;
100
101
102
103
104
105
106
107 public StretchTorsionType(int[] atomClasses, double[] forceConstants) {
108
109 super(STRTORS, sortKey(atomClasses));
110 this.atomClasses = atomClasses;
111 this.forceConstants = forceConstants;
112 }
113
114
115
116
117
118
119
120
121
122 public static StretchTorsionType average(StretchTorsionType stretchTorsionType1,
123 StretchTorsionType stretchTorsionType2, int[] atomClasses) {
124 if (stretchTorsionType1 == null || stretchTorsionType2 == null || atomClasses == null) {
125 return null;
126 }
127 int len = stretchTorsionType1.forceConstants.length;
128 if (len != stretchTorsionType2.forceConstants.length) {
129 return null;
130 }
131 double[] forceConstants = new double[len];
132 for (int i = 0; i < len; i++) {
133 forceConstants[i] =
134 (stretchTorsionType1.forceConstants[i] + stretchTorsionType2.forceConstants[i]) / 2.0;
135 }
136 return new StretchTorsionType(atomClasses, forceConstants);
137 }
138
139
140
141
142
143
144
145
146 public static StretchTorsionType parse(String input, String[] tokens) {
147 if (tokens.length < 13) {
148 logger.log(Level.WARNING, "Invalid STRTORS type:\n{0}", input);
149 } else {
150 try {
151 int[] atomClasses = new int[4];
152 atomClasses[0] = parseInt(tokens[1]);
153 atomClasses[1] = parseInt(tokens[2]);
154 atomClasses[2] = parseInt(tokens[3]);
155 atomClasses[3] = parseInt(tokens[4]);
156 double[] constants = new double[9];
157 constants[0] = parseDouble(tokens[5]);
158 constants[1] = parseDouble(tokens[6]);
159 constants[2] = parseDouble(tokens[7]);
160 constants[3] = parseDouble(tokens[8]);
161 constants[4] = parseDouble(tokens[9]);
162 constants[5] = parseDouble(tokens[10]);
163 constants[6] = parseDouble(tokens[11]);
164 constants[7] = parseDouble(tokens[12]);
165 constants[8] = parseDouble(tokens[13]);
166 return new StretchTorsionType(atomClasses, constants);
167 } catch (NumberFormatException e) {
168 String message = "Exception parsing STRTORS type:\n" + input + "\n";
169 logger.log(Level.SEVERE, message, e);
170 }
171 }
172 return null;
173 }
174
175
176
177
178
179
180
181
182 public static String sortKey(int[] c) {
183 return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
184 }
185
186
187
188
189
190
191 @Override
192 public int compare(String s1, String s2) {
193 String[] keys1 = s1.split(" ");
194 String[] keys2 = s2.split(" ");
195 int[] c1 = new int[4];
196 int[] c2 = new int[4];
197
198 for (int i = 0; i < 4; i++) {
199 c1[i] = parseInt(keys1[i]);
200 c2[i] = parseInt(keys2[i]);
201 }
202
203 if (c1[1] < c2[1]) {
204 return -1;
205 } else if (c1[1] > c2[1]) {
206 return 1;
207 } else if (c1[2] < c2[2]) {
208 return -1;
209 } else if (c1[2] > c2[2]) {
210 return 1;
211 } else if (c1[0] < c2[0]) {
212 return -1;
213 } else if (c1[0] > c2[0]) {
214 return 1;
215 } else if (c1[3] < c2[3]) {
216 return -1;
217 } else if (c1[3] > c2[3]) {
218 return 1;
219 }
220
221 return 0;
222 }
223
224
225
226
227 @Override
228 public boolean equals(Object o) {
229 if (this == o) {
230 return true;
231 }
232 if (o == null || getClass() != o.getClass()) {
233 return false;
234 }
235 StretchTorsionType stretchTorsionType = (StretchTorsionType) o;
236 return Arrays.equals(atomClasses, stretchTorsionType.atomClasses);
237 }
238
239
240
241
242 @Override
243 public int hashCode() {
244 return Arrays.hashCode(atomClasses);
245 }
246
247
248
249
250
251
252 public void incrementClasses(int increment) {
253 for (int i = 0; i < atomClasses.length; i++) {
254 atomClasses[i] += increment;
255 }
256 setKey(sortKey(atomClasses));
257 }
258
259
260
261
262
263
264
265 public StretchTorsionType patchClasses(HashMap<AtomType, AtomType> typeMap) {
266 int count = 0;
267 int len = atomClasses.length;
268
269
270 for (AtomType newType : typeMap.keySet()) {
271 for (int atomClass : atomClasses) {
272 if (atomClass == newType.atomClass) {
273 count++;
274 }
275 }
276 }
277
278
279 if (count == 1 || count == 2) {
280 int[] newClasses = copyOf(atomClasses, len);
281 for (AtomType newType : typeMap.keySet()) {
282 for (int i = 0; i < len; i++) {
283 if (atomClasses[i] == newType.atomClass) {
284 AtomType knownType = typeMap.get(newType);
285 newClasses[i] = knownType.atomClass;
286 }
287 }
288 }
289 return new StretchTorsionType(newClasses, forceConstants);
290 }
291 return null;
292 }
293
294
295
296
297
298
299 @Override
300 public String toString() {
301 return format(
302 "strtors %5d %5d %5d %5d %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f",
303 atomClasses[0], atomClasses[1], atomClasses[2], atomClasses[3], forceConstants[0],
304 forceConstants[1], forceConstants[2], forceConstants[3], forceConstants[4],
305 forceConstants[5], forceConstants[6], forceConstants[7], forceConstants[8]);
306 }
307
308
309
310
311
312
313
314
315 public static Element getXMLForce(Document doc, ForceField forceField) {
316 Map<String, StretchTorsionType> types = forceField.getStretchTorsionTypes();
317 if (!types.values().isEmpty()) {
318 Element node = doc.createElement("AmoebaStretchTorsionForce");
319 for (StretchTorsionType stretchTorsionType : types.values()) {
320 node.appendChild(stretchTorsionType.toXML(doc));
321 }
322 return node;
323 }
324 return null;
325 }
326
327
328
329
330
331
332
333 public Element toXML(Document doc) {
334 Element node = doc.createElement("Torsion");
335 node.setAttribute("class1", format("%d", atomClasses[0]));
336 node.setAttribute("class2", format("%d", atomClasses[1]));
337 node.setAttribute("class3", format("%d", atomClasses[2]));
338 node.setAttribute("class4", format("%d", atomClasses[3]));
339 node.setAttribute("v11", format("%f", forceConstants[0] * KCAL_TO_KJ / ANG_TO_NM));
340 node.setAttribute("v12", format("%f", forceConstants[1] * KCAL_TO_KJ / ANG_TO_NM));
341 node.setAttribute("v13", format("%f", forceConstants[2] * KCAL_TO_KJ / ANG_TO_NM));
342 node.setAttribute("v21", format("%f", forceConstants[3] * KCAL_TO_KJ / ANG_TO_NM));
343 node.setAttribute("v22", format("%f", forceConstants[4] * KCAL_TO_KJ / ANG_TO_NM));
344 node.setAttribute("v23", format("%f", forceConstants[5] * KCAL_TO_KJ / ANG_TO_NM));
345 node.setAttribute("v31", format("%f", forceConstants[6] * KCAL_TO_KJ / ANG_TO_NM));
346 node.setAttribute("v32", format("%f", forceConstants[7] * KCAL_TO_KJ / ANG_TO_NM));
347 node.setAttribute("v33", format("%f", forceConstants[8] * KCAL_TO_KJ / ANG_TO_NM));
348 return node;
349 }
350 }