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