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 static ffx.potential.parameters.ForceField.ForceFieldType.ANGTORS;
41 import static ffx.utilities.Constants.DEGREES_PER_RADIAN;
42 import static ffx.utilities.PropertyGroup.EnergyUnitConversion;
43 import static ffx.utilities.PropertyGroup.PotentialFunctionParameter;
44 import static java.lang.Double.parseDouble;
45 import static java.lang.Integer.parseInt;
46 import static java.lang.String.format;
47 import static java.util.Arrays.copyOf;
48
49 import ffx.utilities.FFXProperty;
50
51 import java.util.Arrays;
52 import java.util.Comparator;
53 import java.util.HashMap;
54 import java.util.logging.Level;
55 import java.util.logging.Logger;
56
57
58
59
60
61
62
63 @FFXProperty(name = "angtors", clazz = String.class, propertyGroup = PotentialFunctionParameter, description = """
64 [4 integers and 6 reals]
65 Provides the values for a single bond angle bending-torsional angle parameter.
66 The integer modifiers give the atom class numbers for the four kinds of atoms involved in the torsion and its contained angles.
67 The real number modifiers give the force constant values for both angles coupled with 1-, 2- and 3-fold torsional terms.
68 The default units for the force constants are kcal/mole/radian, but this can be controlled via the angtorunit keyword.
69 """)
70 public final class AngleTorsionType extends BaseType implements Comparator<String> {
71
72 public static final double DEFAULT_ANGTOR_UNIT = 1.0 / DEGREES_PER_RADIAN;
73
74
75 @FFXProperty(name = "angtorunit", propertyGroup = EnergyUnitConversion, defaultValue = "Pi/180", description = """
76 Sets the scale factor needed to convert the energy value computed by the angle bending-torsional angle
77 cross term into units of kcal/mole. The correct value is force field dependent and typically provided in the
78 header of the master force field parameter file.
79 """)
80 public double angtorunit = DEFAULT_ANGTOR_UNIT;
81
82
83 private static final Logger logger = Logger.getLogger(AngleTorsionType.class.getName());
84
85 public final int[] atomClasses;
86
87 public final double[] forceConstants;
88
89
90
91
92
93
94
95 public AngleTorsionType(int[] atomClasses, double[] forceConstants) {
96 super(ANGTORS, sortKey(atomClasses));
97 this.atomClasses = atomClasses;
98 this.forceConstants = forceConstants;
99 }
100
101
102
103
104
105
106
107
108
109 public static AngleTorsionType average(AngleTorsionType angleTorsionType1,
110 AngleTorsionType angleTorsionType2, int[] atomClasses) {
111 if (angleTorsionType1 == null || angleTorsionType2 == null || atomClasses == null) {
112 return null;
113 }
114 int len = angleTorsionType1.forceConstants.length;
115 if (len != angleTorsionType2.forceConstants.length) {
116 return null;
117 }
118 double[] forceConstants = new double[len];
119 for (int i = 0; i < len; i++) {
120 forceConstants[i] =
121 (angleTorsionType1.forceConstants[i] + angleTorsionType2.forceConstants[i]) / 2.0;
122 }
123 return new AngleTorsionType(atomClasses, forceConstants);
124 }
125
126
127
128
129
130
131
132
133 public static AngleTorsionType parse(String input, String[] tokens) {
134 if (tokens.length < 10) {
135 logger.log(Level.WARNING, "Invalid ANGTORS type:\n{0}", input);
136 } else {
137 try {
138 int[] atomClasses = new int[4];
139 atomClasses[0] = parseInt(tokens[1]);
140 atomClasses[1] = parseInt(tokens[2]);
141 atomClasses[2] = parseInt(tokens[3]);
142 atomClasses[3] = parseInt(tokens[4]);
143 double[] constants = new double[6];
144 constants[0] = parseDouble(tokens[5]);
145 constants[1] = parseDouble(tokens[6]);
146 constants[2] = parseDouble(tokens[7]);
147 constants[3] = parseDouble(tokens[8]);
148 constants[4] = parseDouble(tokens[9]);
149 constants[5] = parseDouble(tokens[10]);
150 return new AngleTorsionType(atomClasses, constants);
151 } catch (NumberFormatException e) {
152 String message = "Exception parsing ANGTORS type:\n" + input + "\n";
153 logger.log(Level.SEVERE, message, e);
154 }
155 }
156 return null;
157 }
158
159
160
161
162
163
164
165
166 public static String sortKey(int[] c) {
167 return c[0] + " " + c[1] + " " + c[2] + " " + c[3];
168 }
169
170
171
172
173
174
175 @Override
176 public int compare(String s1, String s2) {
177 String[] keys1 = s1.split(" ");
178 String[] keys2 = s2.split(" ");
179 int[] c1 = new int[4];
180 int[] c2 = new int[4];
181
182 for (int i = 0; i < 4; i++) {
183 c1[i] = parseInt(keys1[i]);
184 c2[i] = parseInt(keys2[i]);
185 }
186
187 if (c1[1] < c2[1]) {
188 return -1;
189 } else if (c1[1] > c2[1]) {
190 return 1;
191 } else if (c1[2] < c2[2]) {
192 return -1;
193 } else if (c1[2] > c2[2]) {
194 return 1;
195 } else if (c1[0] < c2[0]) {
196 return -1;
197 } else if (c1[0] > c2[0]) {
198 return 1;
199 } else if (c1[3] < c2[3]) {
200 return -1;
201 } else if (c1[3] > c2[3]) {
202 return 1;
203 }
204
205 return 0;
206 }
207
208
209 @Override
210 public boolean equals(Object o) {
211 if (this == o) {
212 return true;
213 }
214 if (o == null || getClass() != o.getClass()) {
215 return false;
216 }
217 AngleTorsionType angleTorsionType = (AngleTorsionType) o;
218 return Arrays.equals(atomClasses, angleTorsionType.atomClasses);
219 }
220
221
222 @Override
223 public int hashCode() {
224 return Arrays.hashCode(atomClasses);
225 }
226
227
228
229
230
231
232 public void incrementClasses(int increment) {
233 for (int i = 0; i < atomClasses.length; i++) {
234 atomClasses[i] += increment;
235 }
236 setKey(sortKey(atomClasses));
237 }
238
239
240
241
242
243
244
245 public AngleTorsionType patchClasses(HashMap<AtomType, AtomType> typeMap) {
246 int count = 0;
247 int len = atomClasses.length;
248
249
250 for (AtomType newType : typeMap.keySet()) {
251
252 for (int atomClass : atomClasses) {
253 if (atomClass == newType.atomClass) {
254 count++;
255 }
256 }
257 }
258
259
260 if (count == 1 || count == 2) {
261 int[] newClasses = copyOf(atomClasses, len);
262 for (AtomType newType : typeMap.keySet()) {
263 for (int i = 0; i < len; i++) {
264 if (atomClasses[i] == newType.atomClass) {
265 AtomType knownType = typeMap.get(newType);
266 newClasses[i] = knownType.atomClass;
267 }
268 }
269 }
270 return new AngleTorsionType(newClasses, forceConstants);
271 }
272 return null;
273 }
274
275
276
277
278
279
280 @Override
281 public String toString() {
282 return format("angtors %5d %5d %5d %5d %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f",
283 atomClasses[0], atomClasses[1], atomClasses[2], atomClasses[3], forceConstants[0],
284 forceConstants[1], forceConstants[2], forceConstants[3], forceConstants[4],
285 forceConstants[5]);
286 }
287 }