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.nonbonded;
39
40 import static java.lang.Double.parseDouble;
41 import static java.lang.Integer.parseInt;
42 import static java.lang.String.format;
43 import static org.apache.commons.math3.util.FastMath.max;
44 import static org.apache.commons.math3.util.FastMath.sqrt;
45
46 import ffx.crystal.Crystal;
47 import ffx.potential.MolecularAssembly;
48 import ffx.potential.bonded.Atom;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.HashMap;
52 import java.util.logging.Logger;
53 import org.apache.commons.configuration2.CompositeConfiguration;
54
55
56
57
58
59
60
61 public class RestrainGroups {
62
63 private static final Logger logger = Logger.getLogger(RestrainGroups.class.getName());
64
65
66 final MolecularAssembly molecularAssembly;
67
68 final Atom[] atoms;
69
70 final int nAtoms;
71
72 final int nGroups;
73
74 final int[][] groupMembers;
75
76 final int[] groupMolecule;
77
78 final double[] groupMass;
79
80 final int nRestraints;
81
82 final int[] group1;
83
84 final int[] group2;
85
86 final double[] forceConstants;
87
88 final double[] distance1;
89
90 final double[] distance2;
91
92 final boolean[] sameMolecule;
93
94
95
96
97
98
99 public RestrainGroups(MolecularAssembly molecularAssembly) {
100 this.molecularAssembly = molecularAssembly;
101 this.atoms = molecularAssembly.getAtomArray();
102 nAtoms = atoms.length;
103
104 CompositeConfiguration compositeConfiguration = molecularAssembly.getProperties();
105
106 String[] groups = compositeConfiguration.getStringArray("group");
107 if (groups == null || groups.length < 2) {
108 logger.severe(" restrain-groups requires two groups to be defined.");
109 }
110
111 String[] restrainGroups = compositeConfiguration.getStringArray("restrain-groups");
112 if (restrainGroups == null || restrainGroups.length < 1) {
113 logger.severe(" No restrain-groups property found.");
114 }
115
116 logger.fine("\n Initializing Groups");
117
118 HashMap<Integer, ArrayList<Integer>> groupMap = new HashMap<>();
119
120 for (String g : groups) {
121
122 String[] gs = g.trim().split(" +");
123
124 int groupNumber = parseInt(gs[0]) - 1;
125 ArrayList<Integer> groupAtomIDs = new ArrayList<>();
126
127 for (int i = 1; i < gs.length; i++) {
128 int start = parseInt(gs[i]);
129 if (start < 0) {
130
131 start = -start - 1;
132 i++;
133 int end = parseInt(gs[i]) - 1;
134 if (start >= nAtoms || end < start || end >= nAtoms) {
135 logger.severe(format(" Property group could not be parsed:\n %s.", g));
136 continue;
137 }
138 for (int j = start; j <= end; j++) {
139 groupAtomIDs.add(j);
140 }
141 logger.fine(format(" Group %d added atoms %d to %d.", groupNumber + 1, start + 1, end + 1));
142 } else {
143 int atomID = start - 1;
144 groupAtomIDs.add(atomID);
145 logger.fine(format(" Group %d added atom %d.", groupNumber + 1, atomID + 1));
146 }
147 groupMap.put(groupNumber, groupAtomIDs);
148 }
149 }
150
151
152 int[] molecule = molecularAssembly.getMoleculeNumbers();
153 nGroups = groupMap.size();
154 groupMembers = new int[nGroups][];
155 groupMass = new double[nGroups];
156 groupMolecule = new int[nGroups];
157 sameMolecule = new boolean[nGroups];
158 for (Integer groupNumber : groupMap.keySet()) {
159 ArrayList<Integer> members = groupMap.get(groupNumber);
160 if (groupNumber >= nGroups) {
161 logger.severe(" Please label groups from 1 to the number of groups.");
162 }
163 int groupID = groupNumber;
164 groupMembers[groupID] = new int[members.size()];
165 int mem = 0;
166 for (int member : members) {
167 groupMembers[groupID][mem++] = member;
168 }
169 logger.finer(format(" Group %d members %s.", groupID, Arrays.toString(groupMembers[groupID])));
170 int k = groupMembers[groupID][0];
171 Atom atom = atoms[k];
172 groupMass[groupID] = atom.getMass();
173 groupMolecule[groupID] = molecule[k];
174 for (int i = 1; i < groupMembers[groupID].length; i++) {
175 k = groupMembers[groupID][i];
176 atom = atoms[k];
177 groupMass[groupID] += atom.getMass();
178 if (groupMolecule[groupID] != molecule[k]) {
179 groupMolecule[groupID] = -1;
180 }
181 }
182 groupMass[groupID] = max(groupMass[groupID], 1.0);
183 }
184
185
186 logger.fine("\n Initializing Restrain-Groups");
187 nRestraints = restrainGroups.length;
188 group1 = new int[nRestraints];
189 group2 = new int[nRestraints];
190 forceConstants = new double[nRestraints];
191 distance1 = new double[nRestraints];
192 distance2 = new double[nRestraints];
193 int iRestraint = 0;
194 for (String restraint : restrainGroups) {
195 String[] values = restraint.trim().split(" +");
196 if (values.length < 3) {
197 logger.severe(format(" Property restrain-groups could not be parsed:\n %s.", restraint));
198 }
199 group1[iRestraint] = parseInt(values[0]) - 1;
200 group2[iRestraint] = parseInt(values[1]) - 1;
201 int i1 = group1[iRestraint];
202 int i2 = group2[iRestraint];
203 if (i1 >= nGroups || i2 >= nGroups) {
204 logger.severe(format(" Property restrain-groups has invalid groups:\n %s.", restraint));
205 }
206 forceConstants[iRestraint] = parseDouble(values[2]);
207 distance1[iRestraint] = 0.0;
208 distance2[iRestraint] = 0.0;
209 if (values.length > 3) {
210 distance1[iRestraint] = parseDouble(values[3]);
211 if (values.length > 4) {
212 distance2[iRestraint] = parseDouble(values[4]);
213 }
214 }
215 sameMolecule[iRestraint] = false;
216 if (groupMolecule[i1] > -1 && groupMolecule[i2] > -1) {
217 if (groupMolecule[i1] == groupMolecule[i2]) {
218 sameMolecule[iRestraint] = true;
219 }
220 }
221 logger.fine(format(" Restrain-Groups %2d %2d %8.3f %8.3f %8.3f",
222 i1 + 1, i2 + 1, forceConstants[iRestraint], distance1[iRestraint], distance2[iRestraint]));
223
224 iRestraint++;
225 }
226 logger.fine("\n");
227 }
228
229
230
231
232
233
234 public int getNumberOfGroups() {
235 return nGroups;
236 }
237
238
239
240
241
242
243
244 public int[] getGroupMembers(int group) {
245 return groupMembers[group];
246 }
247
248
249
250
251
252
253 public int[] getGroup1() {
254 return group1;
255 }
256
257
258
259
260
261
262 public int[] getGroup2() {
263 return group2;
264 }
265
266
267
268
269
270
271 public double[] getForceConstants() {
272 return forceConstants;
273 }
274
275
276
277
278
279
280 public double[] getSmallerDistance() {
281 return distance1;
282 }
283
284
285
286
287
288
289 public double[] getLargerDistance() {
290 return distance2;
291 }
292
293
294
295
296
297
298
299 public double energy(boolean gradient) {
300 double energy = 0;
301 double[] xyz = new double[3];
302 double[] dr = new double[3];
303 Crystal crystal = molecularAssembly.getCrystal();
304 for (int i = 0; i < nRestraints; i++) {
305 int i1 = group1[i];
306 int i2 = group2[i];
307
308 double xcm = 0.0;
309 double ycm = 0.0;
310 double zcm = 0.0;
311 for (int j = 0; j < groupMembers[i1].length; j++) {
312 int k = groupMembers[i1][j];
313 Atom atom = atoms[k];
314 double mass = atom.getMass();
315 atom.getXYZ(xyz);
316 xcm = xcm + xyz[0] * mass;
317 ycm = ycm + xyz[1] * mass;
318 zcm = zcm + xyz[2] * mass;
319 }
320 double mass1 = groupMass[i1];
321 double xr = xcm / mass1;
322 double yr = ycm / mass1;
323 double zr = zcm / mass1;
324
325 xcm = 0.0;
326 ycm = 0.0;
327 zcm = 0.0;
328 for (int j = 0; j < groupMembers[i2].length; j++) {
329 int k = groupMembers[i2][j];
330 Atom atom = atoms[k];
331 double mass = atom.getMass();
332 atom.getXYZ(xyz);
333 xcm = xcm + xyz[0] * mass;
334 ycm = ycm + xyz[1] * mass;
335 zcm = zcm + xyz[2] * mass;
336 }
337 double mass2 = groupMass[i2];
338 xr = xr - xcm / mass2;
339 yr = yr - ycm / mass2;
340 zr = zr - zcm / mass2;
341 double r2;
342 if (sameMolecule[i]) {
343 r2 = xr * xr + yr * yr + zr * zr;
344 } else {
345 dr[0] = xr;
346 dr[1] = yr;
347 dr[2] = zr;
348 r2 = crystal.image(dr);
349 }
350 double r = sqrt(r2);
351 double force = forceConstants[i];
352 double gf1 = distance1[i];
353 double gf2 = distance2[i];
354 double target = r;
355 if (r < gf1) {
356 target = gf1;
357 }
358 if (r > gf2) {
359 target = gf2;
360 }
361 double dt = r - target;
362 double dt2 = dt * dt;
363 double e = force * dt2;
364 energy = energy + e;
365
366 if (gradient) {
367
368 if (r == 0.0) {
369 r = 1.0;
370 }
371 double de = 2.0 * force * dt / r;
372 double dedx = de * xr;
373 double dedy = de * yr;
374 double dedz = de * zr;
375
376 for (int j = 0; j < groupMembers[i1].length; j++) {
377 int k = groupMembers[i1][j];
378 Atom atom = atoms[k];
379 double mass = atom.getMass();
380 double ratio = mass / mass1;
381 atom.addToXYZGradient(dedx * ratio, dedy * ratio, dedz * ratio);
382 }
383
384 for (int j = 0; j < groupMembers[i2].length; j++) {
385 int k = groupMembers[i2][j];
386 Atom atom = atoms[k];
387 double mass = atom.getMass();
388 double ratio = mass / mass2;
389 atom.addToXYZGradient(-dedx * ratio, -dedy * ratio, -dedz * ratio);
390 }
391 }
392 }
393 return energy;
394 }
395
396
397
398
399
400
401 public int getNumberOfRestraints() {
402 return nRestraints;
403 }
404 }