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.algorithms.misc;
39
40 import static java.lang.String.format;
41
42 import ffx.algorithms.AlgorithmListener;
43 import ffx.numerics.Potential;
44 import ffx.potential.MolecularAssembly;
45 import ffx.potential.bonded.AminoAcidUtils;
46 import ffx.potential.bonded.Atom;
47 import ffx.potential.bonded.Residue;
48 import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
49 import ffx.potential.bonded.NucleicAcidUtils.NucleicAcid3;
50 import ffx.potential.bonded.Rotamer;
51 import ffx.potential.bonded.RotamerLibrary;
52 import ffx.potential.parsers.PDBFilter;
53 import java.io.BufferedWriter;
54 import java.io.File;
55 import java.io.FileWriter;
56 import java.io.IOException;
57 import java.util.Arrays;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60 import java.util.regex.Matcher;
61 import java.util.regex.Pattern;
62
63
64
65
66
67
68
69
70 public class GenerateRotamers {
71
72 private static final Logger logger = Logger.getLogger(GenerateRotamers.class.getName());
73 private static final Pattern atRangePatt = Pattern.compile("(\\d+)-(\\d+)");
74 private final Residue residue;
75 private final int nChi;
76 private final double[] currentChi;
77 private final File outFile;
78 private final MolecularAssembly molecularAssembly;
79 private final AlgorithmListener listener;
80 private final RotamerLibrary library;
81 private final Potential potential;
82 private double[] x;
83 private AminoAcid3 baselineAAres;
84 private Rotamer[] baselineRotamers;
85 private double incr = 10.0;
86 private boolean aroundLibrary = false;
87 private double width = 180.0;
88 private int startDepth = 0;
89 private int endDepth;
90 private boolean print = false;
91 private int nEval = 0;
92 private boolean writeVideo = false;
93 private File videoFile;
94 private PDBFilter videoFilter;
95
96
97
98
99
100
101
102
103
104
105
106 public GenerateRotamers(MolecularAssembly molecularAssembly, Potential potential, Residue residue,
107 File file, int nChi, AlgorithmListener listener) {
108 this(molecularAssembly, potential, residue, file, nChi, listener,
109 RotamerLibrary.getDefaultLibrary());
110 }
111
112
113
114
115
116
117
118
119
120
121
122
123 public GenerateRotamers(MolecularAssembly molecularAssembly, Potential potential, Residue residue,
124 File file, int nChi, AlgorithmListener listener, RotamerLibrary library) {
125 this.residue = residue;
126 this.nChi = nChi;
127 endDepth = nChi - 1;
128 this.potential = potential;
129 this.molecularAssembly = molecularAssembly;
130 this.listener = listener;
131 this.currentChi = new double[nChi];
132 this.library = library;
133 Arrays.fill(currentChi, 0.0);
134 File outputFile = file;
135 if (outputFile.exists()) {
136 String outName = outputFile.getName();
137 for (int i = 1; i < 1000; i++) {
138 outputFile = new File(format("%s_%d", outName, i));
139 if (!outputFile.exists()) {
140 break;
141 }
142 }
143 if (outputFile.exists()) {
144 logger.severe(format(" Could not version file %s", outName));
145 }
146 }
147 outFile = outputFile;
148 this.baselineAAres = residue.getAminoAcid3();
149 baselineRotamers = library.getRotamers(baselineAAres);
150 }
151
152
153
154
155
156
157 public void applyAndSaveTorsions(String[] torSets) {
158 for (String torSet : torSets) {
159 String[] torsions = torSet.split(",");
160 double[] values = new double[nChi * 2];
161 Arrays.fill(values, 0.0);
162 for (int i = 0; i < (Math.min(torsions.length, nChi)); i++) {
163 double chival = Double.parseDouble(torsions[i]);
164 currentChi[i] = chival;
165 values[2 * i] = chival;
166 }
167 Rotamer newRot = generateRotamer(values);
168 RotamerLibrary.applyRotamer(residue, newRot);
169 writeSnapshot();
170 }
171 }
172
173
174
175
176
177
178
179 public void setBaselineAARes(AminoAcid3 aa3) {
180 this.baselineAAres = aa3;
181 if (aa3 == AminoAcidUtils.AminoAcid3.UNK) {
182 boolean orig = library.getUsingOrigCoordsRotamer();
183 library.setUseOrigCoordsRotamer(false);
184 baselineRotamers = residue.setRotamers(library);
185 library.setUseOrigCoordsRotamer(orig);
186 } else {
187 baselineRotamers = library.getRotamers(aa3);
188 }
189 aroundLibrary = true;
190 }
191
192
193
194
195
196
197
198
199 public void setDepth(int start, int end) {
200 startDepth = start;
201 if (end < 1 || end > nChi) {
202 endDepth = nChi - 1;
203 } else {
204 endDepth = end;
205 }
206 }
207
208
209
210
211
212
213 public void setElectrostatics(String electrostatics) {
214 if (electrostatics != null) {
215 String[] tokens = electrostatics.split(",");
216 Atom[] atoms = molecularAssembly.getAtomArray();
217 for (String tok : tokens) {
218 Matcher m = atRangePatt.matcher(tok);
219 if (m.matches()) {
220 int begin = Integer.parseInt(m.group(1));
221 int end = Integer.parseInt(m.group(2));
222 logger.info(format(" Inactivating electrostatics for atoms %d-%d", begin, end));
223 for (int i = begin; i <= end; i++) {
224 Atom ai = atoms[i - 1];
225 ai.setElectrostatics(false);
226 ai.print(Level.FINE);
227 }
228 } else {
229 logger.info(format(" Discarding electrostatics input %s", tok));
230 }
231 }
232 }
233 }
234
235
236
237
238
239
240 public void setInactiveAtoms(String iatoms) {
241 if (iatoms != null) {
242 String[] tokens = iatoms.split(",");
243 Atom[] atoms = molecularAssembly.getAtomArray();
244 for (String tok : tokens) {
245 Matcher m = atRangePatt.matcher(tok);
246 if (m.matches()) {
247 int begin = Integer.parseInt(m.group(1));
248 int end = Integer.parseInt(m.group(2));
249 logger.info(format(" Inactivating atoms %d-%d", begin, end));
250 for (int i = begin; i <= end; i++) {
251 Atom ai = atoms[i - 1];
252 ai.setUse(false);
253 ai.print(Level.FINE);
254 }
255 } else {
256 logger.info(format(" Discarding inactive atoms input %s", tok));
257 }
258 }
259 }
260 }
261
262
263
264
265
266
267 public void setIncrement(double incr) {
268 this.incr = incr;
269 }
270
271
272
273
274
275
276 public void setPrint(boolean print) {
277 this.print = print;
278 }
279
280
281
282
283
284
285
286 public void setSearchWidth(double width) {
287 this.width = width;
288 }
289
290
291
292
293
294
295 public void setVideo(String videoFile) {
296 if (videoFile != null) {
297 File vidFile = new File(videoFile);
298 if (vidFile.exists()) {
299 for (int i = 0; i < 1000; i++) {
300 vidFile = new File(format("%s_%d", videoFile, i));
301 if (!vidFile.exists()) {
302 this.videoFile = vidFile;
303 writeVideo = true;
304 videoFilter = new PDBFilter(this.videoFile, molecularAssembly,
305 molecularAssembly.getForceField(), null);
306 videoFilter.setLogWrites(false);
307 break;
308 }
309 }
310 if (vidFile.exists()) {
311 logger.warning(format(" Could not version video file %s", videoFile));
312 }
313 } else {
314 this.videoFile = vidFile;
315 writeVideo = true;
316 videoFilter = new PDBFilter(this.videoFile, molecularAssembly,
317 molecularAssembly.getForceField(), null);
318 videoFilter.setLogWrites(false);
319 }
320 } else {
321 writeVideo = false;
322 }
323 }
324
325
326 public void tryRotamers() {
327 try (BufferedWriter bw = new BufferedWriter(new FileWriter(outFile))) {
328 if (aroundLibrary) {
329 for (Rotamer rotamer : baselineRotamers) {
330 applyLibRotamer(rotamer);
331 turnChi(startDepth, bw);
332 }
333 } else {
334 RotamerLibrary.measureRotamer(residue, currentChi, false);
335 turnChi(startDepth, bw);
336 }
337 } catch (IOException ex) {
338 logger.warning(format(" IO exception in rotamer generation: %s", ex));
339 }
340 }
341
342
343
344
345
346
347
348 private void applyLibRotamer(Rotamer rotamer) {
349 double[] rotValues = new double[nChi * 2];
350 Arrays.fill(rotValues, 0.0);
351 Arrays.fill(currentChi, 0.0);
352 int fillTo = Math.min(startDepth, (rotamer.length - 1));
353 for (int i = 0; i < fillTo; i++) {
354 int ii = 2 * i;
355 rotValues[ii] = rotamer.angles[i];
356 currentChi[i] = rotamer.angles[i];
357 rotValues[ii + 1] = rotamer.sigmas[i];
358 }
359 Rotamer newRot = generateRotamer(rotValues);
360 RotamerLibrary.applyRotamer(residue, newRot);
361 }
362
363
364
365
366
367
368
369 private Rotamer generateRotamer(double[] values) {
370 switch (residue.getResidueType()) {
371 case AA -> {
372 AminoAcid3 aa3 = residue.getAminoAcid3();
373 return new Rotamer(aa3, values);
374 }
375 case NA -> {
376 NucleicAcid3 na3 = residue.getNucleicAcid3();
377 return new Rotamer(na3, values);
378 }
379 default -> {
380 return new Rotamer(values);
381 }
382 }
383 }
384
385
386
387
388
389
390
391
392 private void turnChi(int depth, BufferedWriter bw) throws IOException {
393 double chi = currentChi[depth];
394 for (double i = chi - width; i <= chi + width; i += incr) {
395 currentChi[depth] = i;
396 if (depth == endDepth) {
397 evaluateChi(bw);
398 if (listener != null) {
399 listener.algorithmUpdate(molecularAssembly);
400 }
401 if (writeVideo) {
402 writeSnapshot();
403 }
404 } else {
405 turnChi(depth + 1, bw);
406 }
407 }
408 currentChi[depth] = chi;
409 }
410
411 private void writeSnapshot() {
412 try (BufferedWriter bw = new BufferedWriter(new FileWriter(videoFile, true))) {
413 bw.write(format("MODEL %d", nEval));
414 bw.newLine();
415 bw.write(format("REMARK 301 TORSIONS %s", formatChi()));
416 bw.newLine();
417 bw.flush();
418 videoFilter.writeFile(videoFile, true);
419 bw.write("ENDMDL");
420 bw.newLine();
421 bw.flush();
422 } catch (IOException ex) {
423 logger.warning(
424 format(" Exception writing to video file %s: " + "%s", videoFile.getName(), ex));
425 }
426 }
427
428
429
430
431
432
433
434 private void evaluateChi(BufferedWriter bw) throws IOException {
435 try {
436 applyChi();
437 double e = currentEnergy();
438 String result = format("%s,%12f", formatChi(), e);
439 ++nEval;
440 if (print) {
441 logger.info(format(" Evaluation %10d %s, energy %10.5f kcal/mol", nEval, formatChi(), e));
442 }
443 bw.write(result);
444 bw.newLine();
445 if (nEval % 1000 == 0) {
446 logger.info(format(" %12.7e states evaluated", (double) nEval));
447 }
448 } catch (ArithmeticException ex) {
449 logger.info(format(" Force field exception at chi %s", formatChi()));
450 }
451 }
452
453
454 private void applyChi() {
455 double[] rotValues = new double[nChi * 2];
456 for (int i = 0; i < nChi; i++) {
457 int ii = 2 * i;
458 rotValues[ii] = currentChi[i];
459 rotValues[ii + 1] = 0.0;
460 }
461 RotamerLibrary.applyRotamer(residue, generateRotamer(rotValues));
462 }
463
464
465
466
467
468
469 private String formatChi() {
470 StringBuilder sb = new StringBuilder(format("%8f", currentChi[0]));
471 for (int i = 1; i < nChi; i++) {
472
473 sb.append(format(",%8f", currentChi[i]));
474 }
475 return sb.toString();
476 }
477
478
479
480
481
482
483 private double currentEnergy() throws ArithmeticException {
484 if (x == null) {
485 int nVar = potential.getNumberOfVariables();
486 x = new double[nVar * 3];
487 }
488 potential.getCoordinates(x);
489 return potential.energy(x);
490 }
491 }