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.optimize;
39
40 import edu.rit.pj.Comm;
41 import edu.rit.pj.ParallelTeam;
42 import edu.rit.pj.WorkerTeam;
43 import ffx.algorithms.AlgorithmListener;
44 import ffx.algorithms.Terminatable;
45 import ffx.algorithms.mc.MCMove;
46 import ffx.algorithms.optimize.manybody.DistanceMatrix;
47 import ffx.algorithms.optimize.manybody.EliminatedRotamers;
48 import ffx.algorithms.optimize.manybody.EnergyExpansion;
49 import ffx.algorithms.optimize.manybody.EnergyRegion;
50 import ffx.algorithms.optimize.manybody.FourBodyEnergyRegion;
51 import ffx.algorithms.optimize.manybody.GoldsteinPairRegion;
52 import ffx.algorithms.optimize.manybody.RotamerMatrixMC;
53 import ffx.algorithms.optimize.manybody.RotamerMatrixMove;
54 import ffx.algorithms.optimize.manybody.SelfEnergyRegion;
55 import ffx.algorithms.optimize.manybody.ThreeBodyEnergyRegion;
56 import ffx.algorithms.optimize.manybody.TwoBodyEnergyRegion;
57 import ffx.numerics.Potential;
58 import ffx.potential.DualTopologyEnergy;
59 import ffx.potential.ForceFieldEnergy;
60 import ffx.potential.MolecularAssembly;
61 import ffx.potential.QuadTopologyEnergy;
62 import ffx.potential.bonded.Atom;
63 import ffx.potential.bonded.Polymer;
64 import ffx.potential.bonded.Residue;
65 import ffx.potential.bonded.ResidueState;
66 import ffx.potential.bonded.Rotamer;
67 import ffx.potential.bonded.RotamerLibrary;
68 import ffx.potential.nonbonded.NonbondedCutoff;
69 import ffx.potential.nonbonded.VanDerWaals;
70 import ffx.potential.openmm.OpenMMEnergy;
71 import ffx.potential.parameters.TitrationUtils;
72 import ffx.potential.parsers.PDBFilter;
73 import ffx.utilities.Constants;
74 import ffx.utilities.ObjectPair;
75 import ffx.utilities.Resources;
76 import org.apache.commons.configuration2.CompositeConfiguration;
77 import org.apache.commons.io.FilenameUtils;
78
79 import java.io.BufferedWriter;
80 import java.io.File;
81 import java.io.FileWriter;
82 import java.io.IOException;
83 import java.nio.file.Path;
84 import java.nio.file.Paths;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.Collections;
88 import java.util.Comparator;
89 import java.util.HashSet;
90 import java.util.List;
91 import java.util.Objects;
92 import java.util.Random;
93 import java.util.Set;
94 import java.util.function.BiFunction;
95 import java.util.function.ToDoubleFunction;
96 import java.util.logging.Level;
97 import java.util.logging.Logger;
98 import java.util.stream.Collectors;
99 import java.util.stream.IntStream;
100
101 import static ffx.potential.bonded.Residue.ResidueType.NA;
102 import static ffx.potential.bonded.RotamerLibrary.applyRotamer;
103 import static java.lang.Boolean.parseBoolean;
104 import static java.lang.Double.parseDouble;
105 import static java.lang.Integer.parseInt;
106 import static java.lang.String.format;
107 import static java.lang.System.arraycopy;
108 import static java.util.Arrays.copyOf;
109 import static org.apache.commons.math3.util.FastMath.abs;
110 import static org.apache.commons.math3.util.FastMath.exp;
111 import static org.apache.commons.math3.util.FastMath.log;
112
113
114
115
116
117
118
119
120
121
122 public class RotamerOptimization implements Terminatable {
123
124
125
126
127 private static final Logger logger = Logger.getLogger(RotamerOptimization.class.getName());
128
129
130
131 private static final double FALLBACK_TWO_BODY_CUTOFF = 0;
132
133
134
135 protected MolecularAssembly molecularAssembly;
136
137
138
139 protected final Potential potential;
140
141
142
143 protected final AlgorithmListener algorithmListener;
144
145
146
147 private final Comm world;
148
149
150
151 private final int numProc;
152
153
154
155 private final int rank;
156
157
158
159 protected final boolean rank0;
160
161
162
163 private final boolean print = false;
164
165
166
167 private final boolean verboseEnergies = true;
168
169
170
171
172 protected boolean writeEnergyRestart = true;
173
174
175
176 BoxOptimization boxOpt;
177
178
179
180
181
182
183 private final BiFunction<List<Residue>, List<Rotamer>, File> dirSupplier;
184
185
186
187
188 private final ToDoubleFunction<File> eFunction;
189
190
191
192 private final boolean verbose;
193
194
195
196 private DistanceMatrix dM;
197
198
199
200 private EnergyExpansion eE;
201
202
203
204 private EliminatedRotamers eR;
205
206
207
208 protected RotamerLibrary library = RotamerLibrary.getDefaultLibrary();
209
210
211
212 private GoldsteinPairRegion goldsteinPairRegion;
213
214
215
216 private EnergyRegion energyRegion;
217
218
219
220 private boolean terminate = false;
221
222
223
224 private boolean done = true;
225
226
227
228 private double twoBodyCutoffDist;
229
230
231
232 private boolean threeBodyTerm = false;
233
234
235
236 private double threeBodyCutoffDist;
237
238
239
240 private int[][] resNeighbors;
241
242
243
244 private int[][] bidiResNeighbors;
245
246
247
248 private boolean pruneClashes = true;
249
250
251
252 private boolean prunePairClashes = true;
253
254
255
256 private int evaluatedPermutations = 0;
257
258
259
260 private int evaluatedPermutationsPrint = 0;
261
262
263
264
265
266 private double temperature = 298.15;
267
268
269
270 private double totalBoltzmann = 0;
271
272
273
274 private double refEnergy = 0;
275
276
277
278 private double[][] fraction;
279
280
281
282 private double[][] populationBoltzmann;
283
284
285
286 private double pH;
287
288
289
290 private double pHRestraint = 0;
291
292
293
294 private boolean recomputeSelf = false;
295
296
297
298 boolean genZ = false;
299
300
301
302 private List<Residue> residueList;
303
304
305
306 protected int[] optimum;
307
308
309
310
311 private int windowSize = 7;
312
313
314
315 private int increment = 3;
316
317
318
319 protected boolean revert;
320
321
322
323 protected Direction direction = Direction.FORWARD;
324
325
326
327
328 private double distance = 2.0;
329
330
331
332 private DistanceMethod distanceMethod = DistanceMethod.RESIDUE;
333
334
335
336 private Algorithm algorithm = null;
337
338
339
340
341
342 private boolean useGoldstein = true;
343
344
345
346 private int ensembleNumber = 1;
347
348
349
350 private double ensembleBuffer = 0.0;
351
352
353
354 private double ensembleBufferStep = 0.5;
355
356
357
358 private double ensembleEnergy = 0.0;
359
360
361
362 private File ensembleFile;
363
364
365
366 private PDBFilter ensembleFilter;
367
368
369
370 private double clashThreshold = 25.0;
371
372
373
374
375 private double multiResClashThreshold = 80.0;
376
377
378
379 private double pairClashThreshold = 25.0;
380
381
382
383 private double multiResPairClashAddn = 80.0;
384
385
386
387 private double[] x = null;
388
389
390
391
392 private boolean useForceFieldEnergy = false;
393
394
395
396
397 private double nucleicCorrectionThreshold = 0;
398
399
400
401 private double approximateEnergy = 0;
402
403
404
405
406 private int minNumberAcceptedNARotamers = 10;
407
408
409
410 private double nucleicPruningFactor = 10.0;
411
412
413
414 private double nucleicPairsPruningFactor = ((1.0 + nucleicPruningFactor) / 2);
415
416
417
418 private List<Residue> allResiduesList = null;
419
420
421
422 private Residue[] allResiduesArray = null;
423
424
425
426 private int nAllResidues = 0;
427
428
429
430
431 protected int[] optimumSubset;
432
433
434
435 protected boolean loadEnergyRestart = false;
436
437
438
439 private File energyRestartFile;
440
441
442
443 private ParallelTeam parallelTeam;
444
445
446
447
448 private boolean compute4BodyEnergy = false;
449
450
451
452 protected boolean usingBoxOptimization = false;
453
454
455
456
457 private double superpositionThreshold = 0.25;
458
459
460
461 private boolean decomposeOriginal = false;
462
463
464
465 private boolean monteCarlo = false;
466
467
468
469 private int nMCSteps = 1000000;
470
471
472
473 private boolean mcUseAll = false;
474
475
476
477 private boolean mcNoEnum = false;
478
479
480
481
482 protected boolean printFiles = true;
483
484
485
486 private List<ObjectPair<ResidueState[], Double>> ensembleStates;
487
488
489
490 private int maxRotCheckDepth;
491
492
493
494 protected BufferedWriter energyWriter;
495
496
497
498
499 private boolean testing = false;
500
501
502
503 private boolean monteCarloTesting = false;
504
505
506
507 private boolean testSelfEnergyEliminations = false;
508
509
510
511
512
513 private int testPairEnergyEliminations = -1;
514
515
516
517
518
519 private int testTripleEnergyEliminations1 = -1;
520
521
522
523
524
525 private int testTripleEnergyEliminations2 = -1;
526
527
528
529 private boolean selfEliminationOn = true;
530
531
532
533 private boolean pairEliminationOn = true;
534
535
536
537
538
539
540
541
542 public RotamerOptimization(MolecularAssembly molecularAssembly, Potential potential,
543 AlgorithmListener algorithmListener) {
544
545 this.molecularAssembly = molecularAssembly;
546 this.potential = potential;
547 if (potential instanceof ForceFieldEnergy) {
548 ((ForceFieldEnergy) potential).setPrintOnFailure(false, true);
549 } else if (potential instanceof DualTopologyEnergy) {
550 ((DualTopologyEnergy) potential).setPrintOnFailure(false, true);
551 } else if (potential instanceof QuadTopologyEnergy) {
552 ((QuadTopologyEnergy) potential).setPrintOnFailure(false, true);
553 }
554 this.algorithmListener = algorithmListener;
555
556 eFunction = this::currentPE;
557 dirSupplier = (List<Residue> resList, List<Rotamer> rotList) -> null;
558
559 world = Comm.world();
560 numProc = world.size();
561 rank = world.rank();
562
563 rank0 = rank == 0;
564
565 boxOpt = new BoxOptimization(this);
566
567 CompositeConfiguration properties = molecularAssembly.getProperties();
568 verbose = properties.getBoolean("verbose", false);
569
570
571 String temp = properties.getString("temperature", "298.15");
572 if (temp != null) {
573 this.temperature = parseDouble(temp);
574 logIfRank0(format(" Temperature: %10.6f", this.temperature));
575 }
576
577
578 ForceFieldEnergy forceFieldEnergy = molecularAssembly.getPotentialEnergy();
579 VanDerWaals vdW = forceFieldEnergy.getVdwNode();
580 if (vdW != null) {
581 NonbondedCutoff nonBondedCutoff = vdW.getNonbondedCutoff();
582 twoBodyCutoffDist = nonBondedCutoff.off;
583 } else {
584 twoBodyCutoffDist = FALLBACK_TWO_BODY_CUTOFF;
585 }
586
587
588
589 boolean testing = properties.getBoolean("manybody-testing", false);
590 if (testing) {
591 turnRotamerSingleEliminationOff();
592 turnRotamerPairEliminationOff();
593 }
594 boolean monteCarloTesting = properties.getBoolean("manybody-testing-mc", false);
595 if (monteCarloTesting) {
596 setMonteCarloTesting(true);
597 }
598
599
600 String computeQuads = properties.getString("ro-compute4BodyEnergy");
601 if (computeQuads != null) {
602 this.compute4BodyEnergy = parseBoolean(computeQuads);
603 logger.info(format(" (KEY) compute4BodyEnergy: %b", this.compute4BodyEnergy));
604 }
605
606
607 String direction = properties.getString("ro-direction");
608 String boxDimensions = properties.getString("ro-boxDimensions");
609 if (direction != null) {
610 this.direction = Direction.valueOf(direction);
611 logger.info(format(" (KEY) direction: %s", this.direction));
612 }
613 if (boxDimensions != null) {
614 boxOpt.update(boxDimensions);
615 }
616
617
618 String ensembleNumber = properties.getString("ro-ensembleNumber");
619 String ensembleEnergy = properties.getString("ro-ensembleEnergy");
620 String ensembleBuffer = properties.getString("ro-ensembleBuffer");
621 if (ensembleNumber != null) {
622 this.ensembleNumber = parseInt(ensembleNumber);
623 this.ensembleBuffer = 5.0;
624 this.ensembleBufferStep = 0.1 * this.ensembleBuffer;
625 logger.info(format(" (KEY) ensembleNumber: %d", this.ensembleNumber));
626 }
627 if (ensembleBuffer != null) {
628 this.ensembleBuffer = parseDouble(ensembleBuffer);
629 this.ensembleBufferStep = 0.1 * this.ensembleBuffer;
630 logger.info(format(" (KEY) ensembleBuffer: %.2f", this.ensembleBuffer));
631 }
632 if (ensembleEnergy != null) {
633 this.ensembleEnergy = parseDouble(ensembleEnergy);
634 logger.info(format(" (KEY) ensembleEnergy: %.2f", this.ensembleEnergy));
635 }
636
637
638 String nucleicPruningFactor = properties.getString("ro-nucleicPruningFactor");
639 String nucleicCorrectionThreshold = properties.getString("ro-nucleicCorrectionThreshold");
640 String minimumNumberAcceptedNARotamers = properties.getString(
641 "ro-minimumNumberAcceptedNARotamers");
642 if (nucleicPruningFactor != null) {
643 double value = parseDouble(nucleicPruningFactor);
644 this.nucleicPruningFactor = (value >= 0 ? value : 1.0);
645 this.nucleicPairsPruningFactor = (1.0 + value) / 2;
646 logger.info(format(" (KEY) nucleicPruningFactor: %.2f", this.nucleicPruningFactor));
647 }
648 if (nucleicCorrectionThreshold != null) {
649 double value = parseDouble(nucleicCorrectionThreshold);
650 this.nucleicCorrectionThreshold = (value >= 0 ? value : 0);
651 logger.info(
652 format(" (KEY) nucleicCorrectionThreshold: %.2f", this.nucleicCorrectionThreshold));
653 }
654 if (minimumNumberAcceptedNARotamers != null) {
655 int value = parseInt(minimumNumberAcceptedNARotamers);
656 this.minNumberAcceptedNARotamers = (value > 0 ? value : 10);
657 logger.info(
658 format(" (KEY) minimumNumberAcceptedNARotamers: %d", this.minNumberAcceptedNARotamers));
659 }
660
661
662 String superpositionThreshold = properties.getString("ro-superpositionThreshold");
663 if (superpositionThreshold != null) {
664 this.superpositionThreshold = parseDouble(superpositionThreshold);
665 logger.info(format(" (KEY) superpositionThreshold: %.2f", this.superpositionThreshold));
666 }
667
668
669 String multiResClashThreshold = properties.getString("ro-multiResClashThreshold");
670 String multiResPairClashAddition = properties.getString("ro-multiResPairClashAddition");
671 if (multiResClashThreshold != null) {
672 this.multiResClashThreshold = parseDouble(multiResClashThreshold);
673 logger.info(format(" (KEY) multiResClashThreshold: %.2f", this.multiResClashThreshold));
674 }
675 if (multiResPairClashAddition != null) {
676 this.multiResPairClashAddn = parseDouble(multiResPairClashAddition);
677 logger.info(format(" (KEY) multiResPairClashAddition: %.2f", this.multiResPairClashAddn));
678 }
679
680 String mcUseAll = properties.getString("ro-mcUseAll");
681 String mcNoEnum = properties.getString("ro-debug-mcNoEnum");
682 if (mcUseAll != null) {
683 this.mcUseAll = parseBoolean(mcUseAll);
684 logIfRank0(format(" (KEY) mcUseAll: %b", this.mcUseAll));
685 }
686 if (mcNoEnum != null) {
687 this.mcNoEnum = parseBoolean(mcNoEnum);
688 logIfRank0(format(" (KEY) debug-mcNoEnum: %b", this.mcNoEnum));
689 }
690
691 String propStr = properties.getString("ro-maxRotCheckDepth");
692 int defaultMaxRotCheckDepth = 1;
693 if (propStr != null) {
694 try {
695 maxRotCheckDepth = parseInt(propStr);
696 if (maxRotCheckDepth > 3 || maxRotCheckDepth < 0) {
697 throw new IllegalArgumentException(" ro-maxRotCheckDepth must be between 0-3 inclusive!");
698 }
699 } catch (Exception ex) {
700 maxRotCheckDepth = defaultMaxRotCheckDepth;
701 logger.warning(format(" Could not parse %s value %s as valid integer; defaulting to %d",
702 "ro-maxRotCheckDepth", propStr, maxRotCheckDepth));
703 logger.warning(format(" Exception: %s", ex));
704 }
705 } else {
706 maxRotCheckDepth = defaultMaxRotCheckDepth;
707 }
708
709 setUpRestart();
710 }
711
712
713
714
715
716
717 public void setWriteEnergyRestart(boolean writeEnergyRestart) {
718 this.writeEnergyRestart = writeEnergyRestart;
719 }
720
721
722
723
724
725
726 private static void turnOffAtoms(Residue residue) {
727 switch (residue.getResidueType()) {
728 case NA:
729 case AA:
730 List<Atom> atomList = residue.getVariableAtoms();
731 for (Atom atom : atomList) {
732 atom.setUse(false);
733 }
734 break;
735 default:
736 atomList = residue.getAtomList();
737 for (Atom atom : atomList) {
738 atom.setUse(false);
739 }
740 break;
741 }
742 }
743
744
745
746
747
748
749 private static void turnOnAtoms(Residue residue) {
750 switch (residue.getResidueType()) {
751 case NA:
752 case AA:
753 List<Atom> atomList = residue.getVariableAtoms();
754 for (Atom atom : atomList) {
755 atom.setUse(true);
756 }
757 break;
758 default:
759 atomList = residue.getAtomList();
760 for (Atom atom : atomList) {
761 atom.setUse(true);
762 }
763 break;
764 }
765 }
766
767
768
769
770
771
772
773
774
775 public boolean checkNeighboringPair(int i, int j) {
776 assert i != j;
777 final int first;
778 final int second;
779 if (i > j) {
780 first = j;
781 second = i;
782 } else {
783 first = i;
784 second = j;
785 }
786 return Arrays.stream(resNeighbors[first]).anyMatch(l -> l == second);
787 }
788
789
790
791
792
793
794
795
796
797
798
799 public boolean checkNeighboringTriple(int i, int j, int k) {
800 assert i != j && i != k && j != k;
801 int[] vals = new int[]{i, j, k};
802 Arrays.sort(vals);
803 final int first = vals[0];
804 final int second = vals[1];
805 final int third = vals[2];
806 if (!checkNeighboringPair(first, second)) {
807 return false;
808 }
809 return (checkNeighboringPair(first, third) || checkNeighboringPair(second, third));
810 }
811
812
813
814
815
816
817
818
819
820 public boolean checkValidMove(int i, int ri, int[] currentRots) {
821 int nRes = currentRots.length;
822 for (int j = 0; j < nRes; j++) {
823 if (j == i) {
824 continue;
825 }
826 if (eR.check(j, currentRots[j], i, ri)) {
827 return false;
828 }
829 }
830 return true;
831 }
832
833
834
835
836
837
838
839
840
841 public double computeBackboneEnergy(Residue[] residues) throws ArithmeticException {
842
843 Atom[] atoms = molecularAssembly.getAtomArray();
844 for (Atom atom : atoms) {
845 atom.setUse(true);
846 }
847
848
849 turnOffAllResidues(residues);
850
851
852 return currentEnergy(residues);
853 }
854
855
856
857
858
859
860
861
862
863
864 public double computeEnergy(Residue[] residues, int[] rotamers, boolean print) {
865 double selfSum;
866 double pairSum;
867 double threeBodySum;
868 try {
869 if (parallelTeam == null) {
870 parallelTeam = new ParallelTeam();
871 }
872 if (energyRegion == null) {
873 energyRegion = new EnergyRegion(parallelTeam.getThreadCount());
874 }
875 energyRegion.init(eE, residues, rotamers, threeBodyTerm);
876 parallelTeam.execute(energyRegion);
877 selfSum = energyRegion.getSelf();
878 pairSum = energyRegion.getTwoBody();
879 threeBodySum = energyRegion.getThreeBody();
880 } catch (Exception e) {
881 throw new IllegalArgumentException(e);
882 }
883
884 approximateEnergy = eE.getBackboneEnergy() + selfSum + pairSum + threeBodySum;
885
886 if (print) {
887 logger.info(format(" Backbone %s", formatEnergy(eE.getBackboneEnergy())));
888 logger.info(format(" Self Energy %s", formatEnergy(selfSum)));
889 logger.info(format(" Pair Energy %s", formatEnergy(pairSum)));
890 if (!threeBodyTerm) {
891 logger.info(format(" Total Energy up to 2-Body %s", formatEnergy(approximateEnergy)));
892 } else {
893 logger.info(format(" 3-Body Energy %s", formatEnergy(threeBodySum)));
894 logger.info(format(" Total Energy up to 3-Body %s", formatEnergy(approximateEnergy)));
895 }
896 }
897 return approximateEnergy;
898 }
899
900
901
902
903
904
905
906 public double currentEnergy(Residue[] resArray) throws ArithmeticException {
907 return currentEnergy(Arrays.asList(resArray));
908 }
909
910
911
912
913
914
915
916
917
918 public double currentEnergyWrapper(List<Residue> resList) throws ArithmeticException {
919 return currentEnergy(resList);
920 }
921
922
923
924
925
926
927 public double currentFFXPE() {
928 if (x == null) {
929 int n = potential.getNumberOfVariables();
930 x = new double[n];
931 }
932 potential.getCoordinates(x);
933 return ((OpenMMEnergy) potential).energyFFX(x, false);
934 }
935
936
937
938
939
940
941
942 public String formatEnergy(double energy) {
943 if (abs(energy) < 1.0e6) {
944 return format("%16.8f", energy);
945 } else {
946 return format("*%15.4e", energy);
947 }
948 }
949
950 public double getApproximate() {
951 return approximateEnergy;
952 }
953
954 public double getBackboneEnergy() {
955 return eE.getBackboneEnergy();
956 }
957
958 public EliminatedRotamers getEliminatedRotamers() {
959 return eR;
960 }
961
962 public EnergyExpansion getEnergyExpansion() {
963 return eE;
964 }
965
966
967
968
969
970
971 public List<ResidueState[]> getEnsemble() {
972 if (ensembleStates == null) {
973 return null;
974 } else {
975 List<ResidueState[]> states = new ArrayList<>(ensembleStates.size());
976 ensembleStates.forEach((es) -> states.add(es.val()));
977 return states;
978 }
979 }
980
981
982
983
984
985
986 public void setEnsemble(int ensemble) {
987 setEnsemble(ensemble, 5.0);
988 }
989
990
991
992
993
994
995 public List<Residue> getResidues() {
996 return residueList;
997 }
998
999
1000
1001
1002
1003
1004 public void setResidues(List<Residue> residueList) {
1005 this.residueList = residueList;
1006 }
1007
1008
1009
1010
1011
1012
1013 public void initFraction(List<Residue> residueList) {
1014 fraction = new double[residueList.size()][56];
1015 genZ = true;
1016 }
1017
1018
1019
1020
1021
1022
1023 public File getRestartFile() {
1024 return energyRestartFile;
1025 }
1026
1027 public double goldsteinPairSumOverK(Residue[] residues, int lb, int ub, int i, int riA, int riB,
1028 int j, int rjC, int rjD, List<Residue> blockedResidues, int[] possK) {
1029 double sumOverK = 0.0;
1030
1031 for (int indK = lb; indK <= ub; indK++) {
1032 int k = possK[indK];
1033 double minForResK = Double.MAX_VALUE;
1034 Residue resk = residues[k];
1035 Rotamer[] rotk = resk.getRotamers();
1036 int nrk = rotk.length;
1037 int rkEvals = 0;
1038
1039 for (int rk = 0; rk < nrk; rk++) {
1040 if (eR.check(k, rk)) {
1041 continue;
1042 }
1043
1044 if (eR.check(i, riA, k, rk) || eR.check(j, rjC, k, rk)) {
1045
1046 continue;
1047 }
1048
1049 if (eR.check(i, riB, k, rk) || eR.check(j, rjD, k, rk)) {
1050 blockedResidues.add(resk);
1051 return Double.NaN;
1052 }
1053
1054 rkEvals++;
1055 double currentResK =
1056 eE.get2Body(i, riA, k, rk) - eE.get2Body(i, riB, k, rk) + eE.get2Body(j, rjC, k, rk)
1057 - eE.get2Body(j, rjD, k, rk);
1058
1059 if (threeBodyTerm) {
1060 double sumOverL = (eE.get3Body(residues, i, riA, j, rjC, k, rk) - eE.get3Body(residues, i,
1061 riB, j, rjD, k, rk));
1062
1063 int[] nK = bidiResNeighbors[k];
1064 IntStream lStream = IntStream.concat(Arrays.stream(possK), Arrays.stream(nK));
1065 int[] possL = lStream.filter(l -> (l != i && l != j && l != k)).sorted().distinct()
1066 .toArray();
1067
1068 for (int l : possL) {
1069 if (l == k || l == i || l == j) {
1070 continue;
1071 }
1072 Residue residuel = residues[l];
1073 Rotamer[] rotamersl = residuel.getRotamers();
1074 int nrl = rotamersl.length;
1075 int rlEvaluations = 0;
1076 double minForResL = Double.MAX_VALUE;
1077
1078 for (int rl = 0; rl < nrl; rl++) {
1079
1080 if (eR.check(l, rl) || eR.check(k, rk, l, rl) || eR.check(i, riA, l, rl) || eR.check(j,
1081 rjC, l, rl)) {
1082
1083
1084 continue;
1085 }
1086 if (eR.check(i, riB, l, rl) || eR.check(j, rjD, l, rl)) {
1087
1088
1089 blockedResidues.add(residuel);
1090 return Double.NaN;
1091 }
1092 rlEvaluations++;
1093 double e =
1094 eE.get3Body(residues, i, riA, k, rk, l, rl) - eE.get3Body(residues, i, riB, k, rk,
1095 l, rl) + eE.get3Body(residues, j, rjC, k, rk, l, rl) - eE.get3Body(residues, j,
1096 rjD, k, rk, l, rl);
1097 if (e < minForResL) {
1098 minForResL = e;
1099 }
1100 }
1101 if (rlEvaluations == 0) {
1102 minForResL = 0.0;
1103 }
1104 sumOverL += minForResL;
1105 }
1106 currentResK += sumOverL;
1107 }
1108 if (currentResK < minForResK) {
1109 minForResK = currentResK;
1110 }
1111 }
1112 if (rkEvals == 0) {
1113 minForResK = 0.0;
1114 blockedResidues.add(resk);
1115 }
1116 sumOverK += minForResK;
1117 }
1118 return sumOverK;
1119 }
1120
1121 public void logIfRank0(String msg) {
1122 if (rank0) {
1123 logger.info(msg);
1124 }
1125 }
1126
1127 public void logIfRank0(String msg, Level level) {
1128 if (rank0) {
1129 logger.log(level, msg);
1130 }
1131 }
1132
1133
1134
1135
1136
1137
1138
1139 public double optimize(Algorithm algorithm) {
1140 this.algorithm = algorithm;
1141 return optimize();
1142 }
1143
1144
1145
1146
1147
1148
1149 public double optimize() {
1150 double e = 0;
1151 try {
1152
1153 CompositeConfiguration properties = molecularAssembly.getProperties();
1154 boolean ignoreNA = properties.getBoolean("ignoreNA", false);
1155 if (ignoreNA) {
1156 logger.info(" Ignoring nucleic acids.");
1157 }
1158
1159 logger.info(format("\n Rotamer Library: %s", library.getLibrary()));
1160 logger.info(format(" Algorithm: %s", algorithm));
1161 logger.info(format(" Goldstein Criteria: %b", useGoldstein));
1162 logger.info(format(" Three-Body Energies: %b\n", threeBodyTerm));
1163
1164
1165
1166
1167 allResiduesList = new ArrayList<>();
1168
1169 Polymer[] polymers = molecularAssembly.getChains();
1170 for (Polymer polymer : polymers) {
1171 List<Residue> current = polymer.getResidues();
1172 for (Residue residuej : current) {
1173 residuej.setRotamers(library);
1174 if (residuej.getRotamers() != null) {
1175 if (!(ignoreNA && residuej.getResidueType() == Residue.ResidueType.NA)) {
1176 allResiduesList.add(residuej);
1177 }
1178 }
1179 }
1180 }
1181
1182 sortResidues(allResiduesList);
1183 sortResidues(residueList);
1184
1185
1186 if (ignoreNA) {
1187 List<Residue> onlyAA = new ArrayList<>();
1188 for (Residue res : residueList) {
1189 if (res.getResidueType() != Residue.ResidueType.NA) {
1190 onlyAA.add(res);
1191 }
1192 }
1193 residueList = onlyAA;
1194 }
1195
1196 RotamerLibrary.initializeDefaultAtomicCoordinates(
1197 molecularAssembly.getChains());
1198
1199 nAllResidues = allResiduesList.size();
1200 allResiduesArray = allResiduesList.toArray(new Residue[nAllResidues]);
1201
1202
1203
1204
1205
1206
1207
1208 if (distance > 0) {
1209 dM = new DistanceMatrix(molecularAssembly, algorithmListener, allResiduesArray,
1210 allResiduesList, distanceMethod, distance, twoBodyCutoffDist, threeBodyCutoffDist);
1211 }
1212
1213 if (residueList != null) {
1214
1215
1216 optimum = new int[residueList.size()];
1217
1218 done = false;
1219 terminate = false;
1220
1221 switch (algorithm) {
1222 case INDEPENDENT -> e = independent(residueList);
1223 case BRUTE_FORCE -> e = bruteForce(residueList);
1224 case ALL -> {
1225 e = globalOptimization(residueList);
1226 arraycopy(optimumSubset, 0, optimum, 0, residueList.size());
1227 }
1228 case WINDOW -> {
1229 if (genZ) {
1230 e = slidingWindowCentered(residueList);
1231 } else {
1232 e = slidingWindowOptimization(residueList, windowSize, increment, revert, distance,
1233 direction, -1);
1234 }
1235 }
1236 case BOX -> e = boxOpt.boxOptimization(residueList);
1237 default -> {
1238 }
1239 }
1240 terminate = false;
1241 done = true;
1242 }
1243 } catch (Exception exception) {
1244 exception.printStackTrace();
1245 } finally {
1246 try {
1247 if (energyWriter != null) {
1248 energyWriter.close();
1249 }
1250 } catch (IOException ex) {
1251 logger.severe(" Exception in closing buffered energy writer.");
1252 }
1253 }
1254
1255 return e;
1256 }
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 public double rotamerOptimization(MolecularAssembly molecularAssembly, Residue[] residues, int i,
1269 double lowEnergy, int[] optimum) {
1270
1271
1272 if (i == 0) {
1273 evaluatedPermutations = 0;
1274 }
1275
1276 int nResidues = residues.length;
1277 Residue current = residues[i];
1278 Rotamer[] rotamers = current.getRotamers();
1279 int lenri = rotamers.length;
1280 double currentEnergy = Double.MAX_VALUE;
1281 List<Residue> resList = Arrays.asList(residues);
1282 if (i < nResidues - 1) {
1283
1284
1285 int minRot = -1;
1286 for (int ri = 0; ri < lenri; ri++) {
1287 applyRotamer(current, rotamers[ri]);
1288 double rotEnergy = rotamerOptimization(molecularAssembly, residues, i + 1, lowEnergy,
1289 optimum);
1290 if (rotEnergy < currentEnergy) {
1291 currentEnergy = rotEnergy;
1292 }
1293 if (rotEnergy < lowEnergy) {
1294 minRot = ri;
1295 lowEnergy = rotEnergy;
1296 }
1297 }
1298 if (minRot > -1) {
1299 optimum[i] = minRot;
1300 }
1301 } else {
1302
1303
1304
1305
1306
1307 int[] rotArray = copyOf(optimum, nResidues);
1308 for (int ri = 0; ri < lenri; ri++) {
1309 applyRotamer(current, rotamers[ri]);
1310 double rotEnergy = Double.NaN;
1311 try {
1312 rotArray[nResidues - 1] = ri;
1313 rotEnergy = currentEnergy(resList) + eE.getTotalRotamerPhBias(resList, rotArray, pH, pHRestraint);
1314 logger.info(format(" %d Energy: %s", ++evaluatedPermutations, formatEnergy(rotEnergy)));
1315 } catch (ArithmeticException ex) {
1316 logger.info(
1317 format(" %d Energy set to NaN (unreasonable conformation)", ++evaluatedPermutations));
1318 }
1319 if (algorithmListener != null) {
1320 algorithmListener.algorithmUpdate(molecularAssembly);
1321 }
1322 if (rotEnergy < currentEnergy) {
1323 currentEnergy = rotEnergy;
1324 }
1325 if (rotEnergy < lowEnergy) {
1326 lowEnergy = rotEnergy;
1327 optimum[nResidues - 1] = ri;
1328 }
1329 }
1330 }
1331 return currentEnergy;
1332 }
1333
1334
1335
1336
1337
1338
1339 public void setPHRestraint(double pHRestraint) {
1340 this.pHRestraint = pHRestraint;
1341 }
1342
1343
1344
1345
1346
1347
1348 public void setpH(double pH) {
1349 this.pH = pH;
1350 }
1351
1352
1353
1354
1355
1356
1357 public void setRecomputeSelf(boolean recomputeSelf) {
1358 this.recomputeSelf = recomputeSelf;
1359 }
1360
1361
1362
1363
1364
1365
1366 public double getPHRestraint() {
1367 return pHRestraint;
1368 }
1369
1370
1371
1372
1373
1374
1375 public double getPH() {
1376 return pH;
1377 }
1378
1379
1380
1381
1382
1383
1384
1385 public void setApproxBoxLength(double approxBoxLength) {
1386 boxOpt.approxBoxLength = approxBoxLength;
1387 }
1388
1389
1390
1391
1392
1393
1394 public void setBoxBorderSize(double boxBorderSize) {
1395 boxOpt.cellBorderSize = boxBorderSize;
1396 }
1397
1398
1399
1400
1401
1402
1403 public void setBoxEnd(int boxEnd) {
1404
1405 boxOpt.cellEnd = boxEnd;
1406 }
1407
1408
1409
1410
1411
1412
1413
1414 public void setBoxInclusionCriterion(int boxInclusionCriterion) {
1415 boxOpt.boxInclusionCriterion = boxInclusionCriterion;
1416 }
1417
1418
1419
1420
1421
1422
1423 public void setBoxStart(int boxStart) {
1424 boxOpt.cellStart = boxStart;
1425 }
1426
1427
1428
1429
1430
1431
1432 public void setTitrationBoxes(boolean titrationBoxes) {
1433 boxOpt.titrationBoxes = titrationBoxes;
1434 }
1435
1436
1437
1438
1439
1440
1441 public void setTitrationBoxSize(double titrationBoxSize) {
1442 boxOpt.titrationBoxSize = titrationBoxSize;
1443 }
1444
1445
1446
1447
1448
1449
1450 public void setCoordinatesToEnsemble(int ensnum) {
1451 if (ensembleStates != null && !ensembleStates.isEmpty()) {
1452 ensnum %= ensembleStates.size();
1453 ResidueState.revertAllCoordinates(residueList, ensembleStates.get(ensnum).val());
1454 } else {
1455 throw new IllegalArgumentException(" Ensemble states not initialized!");
1456 }
1457 }
1458
1459
1460
1461
1462
1463
1464 public void setDecomposeOriginal(boolean decomposeOriginal) {
1465 this.decomposeOriginal = decomposeOriginal;
1466 }
1467
1468
1469
1470
1471
1472
1473 public void setDirection(Direction direction) {
1474 this.direction = direction;
1475 }
1476
1477
1478
1479
1480
1481
1482 public void setDistanceCutoff(double distance) {
1483 this.distance = distance;
1484 }
1485
1486
1487
1488
1489
1490
1491 public void setEnergyRestartFile(File file) {
1492 loadEnergyRestart = true;
1493 energyRestartFile = file;
1494 }
1495
1496
1497
1498
1499
1500
1501
1502 public void setEnsemble(int ensemble, double ensembleBuffer) {
1503 this.ensembleNumber = ensemble;
1504 this.ensembleBuffer = ensembleBuffer;
1505 this.ensembleBufferStep = 0.1 * ensembleBuffer;
1506 if (ensemble > 1) {
1507 setPruning(0);
1508 }
1509 }
1510
1511
1512
1513
1514
1515
1516 public void setIncrement(int increment) {
1517 this.increment = increment;
1518 }
1519
1520
1521
1522
1523
1524
1525 public void setMaxRotCheckDepth(int maxRotCheckDepth) {
1526 this.maxRotCheckDepth = maxRotCheckDepth;
1527 }
1528
1529
1530
1531
1532
1533
1534 public void setMinimumNumberAcceptedNARotamers(int minNumberAcceptedNARotamers) {
1535 if (minNumberAcceptedNARotamers > 0) {
1536 this.minNumberAcceptedNARotamers = minNumberAcceptedNARotamers;
1537 } else {
1538 logger.warning(
1539 "\n Minimum number of accepted NA rotamers must be a positive integer.\n Setting to default value 10.\n");
1540 this.minNumberAcceptedNARotamers = 10;
1541 }
1542 }
1543
1544
1545
1546
1547
1548
1549
1550 public void setMonteCarlo(boolean monteCarlo, int nMCsteps) {
1551 this.monteCarlo = monteCarlo;
1552 this.nMCSteps = nMCsteps;
1553 }
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563 public void setMonteCarloTesting(boolean bool) {
1564 this.monteCarloTesting = bool;
1565 }
1566
1567
1568
1569
1570
1571
1572 public void setNucleicCorrectionThreshold(double nucleicCorrectionThreshold) {
1573 if (nucleicCorrectionThreshold >= 0) {
1574 this.nucleicCorrectionThreshold = nucleicCorrectionThreshold;
1575 } else {
1576 logger.warning(
1577 "\n Correction threshold must be >= 0. Setting to default of 0 (threshold inactive).\n");
1578 this.nucleicCorrectionThreshold = 0;
1579 }
1580 }
1581
1582
1583
1584
1585
1586
1587 public void setNucleicPruningFactor(double nucleicPruningFactor) {
1588 this.nucleicPruningFactor = nucleicPruningFactor;
1589 this.nucleicPairsPruningFactor = ((1.0 + nucleicPruningFactor) / 2);
1590 }
1591
1592
1593
1594
1595
1596
1597 public void setNumXYZBoxes(int[] numXYZBoxes) {
1598 arraycopy(numXYZBoxes, 0, boxOpt.numXYZCells, 0, boxOpt.numXYZCells.length);
1599 }
1600
1601
1602
1603
1604
1605
1606 public void setPairClashThreshold(double pairClashThreshold) {
1607 this.pairClashThreshold = pairClashThreshold;
1608 }
1609
1610
1611
1612
1613
1614
1615
1616 public void setPrintFiles(boolean printFiles) {
1617 this.printFiles = printFiles;
1618 }
1619
1620
1621
1622
1623
1624
1625 public void setPruning(int set) {
1626 if (set == 0) {
1627 this.pruneClashes = false;
1628 this.prunePairClashes = false;
1629 } else if (set == 1) {
1630 this.pruneClashes = true;
1631 this.prunePairClashes = false;
1632 } else if (set == 2) {
1633 this.pruneClashes = true;
1634 this.prunePairClashes = true;
1635 }
1636 }
1637
1638
1639
1640
1641
1642
1643 public void setResiduesIgnoreNull(List<Residue> residues) {
1644 residueList = new ArrayList<>();
1645 logger.fine(" Optimizing these residues: ");
1646 for (Residue r : residues) {
1647 if (r.getRotamers() != null) {
1648 residueList.add(r);
1649 logger.fine(format("\t%s", r));
1650 } else {
1651 logger.fine(format(" not \t%s", r));
1652 }
1653 }
1654 }
1655
1656
1657
1658
1659
1660
1661 public void setRevert(boolean revert) {
1662 this.revert = revert;
1663 }
1664
1665 public void setRotamerLibrary(RotamerLibrary lib) {
1666 library = lib;
1667 }
1668
1669
1670
1671
1672
1673
1674 public void setSingletonClashThreshold(double singletonClashThreshold) {
1675 this.clashThreshold = singletonClashThreshold;
1676 }
1677
1678
1679
1680
1681
1682
1683 public void setSuperpositionThreshold(double superpositionThreshold) {
1684 this.superpositionThreshold = superpositionThreshold;
1685 }
1686
1687
1688
1689
1690
1691
1692
1693
1694 public void setThreeBodyCutoff(double threeBodyCutoffDist) {
1695 this.threeBodyCutoffDist = threeBodyCutoffDist;
1696 if (this.threeBodyCutoffDist < 0) {
1697 logger.info("Warning: threeBodyCutoffDist should not be less than 0.");
1698 }
1699 }
1700
1701
1702
1703
1704
1705
1706
1707 public void setThreeBodyEnergy(boolean threeBodyTerm) {
1708 this.threeBodyTerm = threeBodyTerm;
1709 }
1710
1711
1712
1713
1714
1715
1716
1717
1718 public void setTwoBodyCutoff(double twoBodyCutoffDist) {
1719 this.twoBodyCutoffDist = twoBodyCutoffDist;
1720 if (this.twoBodyCutoffDist < 0) {
1721 logger.info("Warning: threeBodyCutoffDist should not be less than 0.");
1722 }
1723 }
1724
1725
1726
1727
1728
1729
1730 public void setUseGoldstein(boolean useGoldstein) {
1731 this.useGoldstein = useGoldstein;
1732 }
1733
1734
1735
1736
1737
1738
1739 public void setWindowSize(int windowSize) {
1740 this.windowSize = windowSize;
1741 if (this.increment > windowSize) {
1742 logger.info(format(" Decreasing increment to match window size %d", windowSize));
1743 this.increment = windowSize;
1744 }
1745 }
1746
1747
1748
1749
1750 @Override
1751 public void terminate() {
1752 terminate = true;
1753 while (!done) {
1754 synchronized (this) {
1755 try {
1756 wait(1);
1757 } catch (InterruptedException e) {
1758 logger.log(Level.WARNING, " Exception terminating rotamer optimization.\n", e);
1759 }
1760 }
1761 }
1762 }
1763
1764
1765
1766
1767 @Override
1768 public String toString() {
1769 if (eR != null) {
1770 return eR.toString();
1771 } else {
1772 return null;
1773 }
1774 }
1775
1776 public void turnOffAllResidues(Residue[] residues) {
1777 if (residues == null) {
1778 return;
1779 }
1780 for (Residue residue : residues) {
1781 turnOffResidue(residue);
1782 }
1783 }
1784
1785 public void turnOffResidue(Residue residue) {
1786 turnOffAtoms(residue);
1787 applyDefaultRotamer(residue);
1788 }
1789
1790 public void turnOnAllResidues(Residue[] residues) {
1791 if (residues == null) {
1792 return;
1793 }
1794 for (Residue residue : residues) {
1795 turnOnAtoms(residue);
1796 }
1797 }
1798
1799 public void turnOnResidue(Residue residue, int ri) {
1800 turnOnAtoms(residue);
1801 Rotamer[] rotamers = residue.getRotamers();
1802 applyRotamer(residue, rotamers[ri]);
1803 }
1804
1805
1806
1807
1808 public void turnRotamerPairEliminationOff() {
1809 logger.info(" Turning off pair eliminations.");
1810 pairEliminationOn = false;
1811 }
1812
1813
1814
1815
1816 public void turnRotamerSingleEliminationOff() {
1817 logger.info(" Turning off single eliminations.");
1818 selfEliminationOn = false;
1819 }
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832 private double decomposedRotamerOptimization(Residue[] residues, int i, double lowEnergy,
1833 int[] optimum, int[] currentRotamers) {
1834
1835
1836 if (i == 0) {
1837 evaluatedPermutations = 0;
1838 }
1839
1840 int nResidues = residues.length;
1841 Residue current = residues[i];
1842 Rotamer[] rotamers = current.getRotamers();
1843 int lenri = rotamers.length;
1844 double currentEnergy = Double.MAX_VALUE;
1845 if (i < nResidues - 1) {
1846
1847
1848 for (int ri = 0; ri < lenri; ri++) {
1849 if (eR.check(i, ri)) {
1850 continue;
1851 }
1852 currentRotamers[i] = ri;
1853 double rotEnergy = decomposedRotamerOptimization(residues, i + 1, lowEnergy, optimum,
1854 currentRotamers);
1855 if (rotEnergy < lowEnergy) {
1856 lowEnergy = rotEnergy;
1857 }
1858 if (rotEnergy < currentEnergy) {
1859 currentEnergy = rotEnergy;
1860 }
1861 }
1862 } else {
1863
1864
1865 for (int ri = 0; ri < lenri; ri++) {
1866 if (eR.check(i, ri)) {
1867 continue;
1868 }
1869 currentRotamers[i] = ri;
1870 double rotEnergy = computeEnergy(residues, currentRotamers, false);
1871 ++evaluatedPermutations;
1872 if (rotEnergy < currentEnergy) {
1873 currentEnergy = rotEnergy;
1874 }
1875 if (rotEnergy < lowEnergy) {
1876
1877
1878
1879
1880
1881 arraycopy(currentRotamers, 0, optimum, 0, optimum.length);
1882 if (evaluatedPermutations > 1) {
1883 logger.info(
1884 format(" Minimum energy update: %f < %f, permutation %d", rotEnergy, lowEnergy,
1885 evaluatedPermutations));
1886 String permutation = " Rotamer permutation: " + optimum[0];
1887 for (int j = 1; j < nResidues; j++) {
1888 permutation = permutation.concat(", " + optimum[j]);
1889 }
1890 logger.info(permutation);
1891 } else {
1892 logger.info(format(" First minimum energy (permutation 1): %f", rotEnergy));
1893 }
1894 lowEnergy = rotEnergy;
1895 }
1896 }
1897 }
1898 return currentEnergy;
1899 }
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914 private double rotamerOptimizationMC(Residue[] residues, int[] optimum, int[] initialRots,
1915 int maxIters, boolean randomizeRots, boolean useAllElims) {
1916
1917 long initTime = -System.nanoTime();
1918 if (randomizeRots) {
1919 randomizeRotamers(initialRots, residues, true);
1920 }
1921
1922 int nRes = residues.length;
1923 arraycopy(initialRots, 0, optimum, 0, nRes);
1924 assert optimum.length == nRes;
1925 assert initialRots.length == nRes;
1926
1927 RotamerMatrixMC rmc = new RotamerMatrixMC(initialRots, residues, useForceFieldEnergy, this);
1928 rmc.setTemperature(temperature);
1929 RotamerMatrixMove rmove = new RotamerMatrixMove(useAllElims, initialRots, residues, this, eR,
1930 monteCarloTesting);
1931 List<MCMove> rmList = new ArrayList<>(1);
1932 rmList.add(rmove);
1933
1934 double initialEnergy = computeEnergy(residues, initialRots, false);
1935 double optimumEnergy = initialEnergy;
1936 double currentEnergy = initialEnergy;
1937
1938 int nAccept = 0;
1939 logIfRank0(
1940 format(" Beginning %d iterations of Monte Carlo search " + "starting from energy %10.6f",
1941 maxIters, initialEnergy));
1942
1943 for (int i = 0; i < maxIters; i++) {
1944 if (rmc.mcStep(rmList, currentEnergy)) {
1945 currentEnergy = rmc.lastEnergy();
1946 ++nAccept;
1947 if (currentEnergy < optimumEnergy) {
1948 optimumEnergy = currentEnergy;
1949 arraycopy(initialRots, 0, optimum, 0, nRes);
1950 }
1951 } else {
1952 currentEnergy = rmc.lastEnergy();
1953 }
1954 }
1955
1956 initTime += System.nanoTime();
1957 double fractAccept = ((double) nAccept) / ((double) maxIters);
1958 logIfRank0(
1959 format(" %d steps of DEE-MC completed in %10.6f seconds", maxIters, (initTime * 1.0E-9)));
1960 logIfRank0(format(" Number of steps accepted: %d for %10.6f of total", nAccept, fractAccept));
1961 logIfRank0(format(" Lowest energy found: %10.6f kcal/mol", optimumEnergy));
1962 logIfRank0(format(" Final energy found: %10.6f kcal/mol", currentEnergy));
1963
1964 return optimumEnergy;
1965 }
1966
1967
1968
1969
1970
1971
1972
1973
1974 private void randomizeRotamers(int[] rotamers, Residue[] residues, boolean useAllElims) {
1975 int nRes = rotamers.length;
1976 for (int i = 0; i < nRes; i++) {
1977 Rotamer[] rotsi = residues[i].getRotamers();
1978 int lenri = rotsi.length;
1979 List<Integer> allowedRots = new ArrayList<>(lenri);
1980
1981 for (int ri = 0; ri < lenri; ri++) {
1982 if (!eR.check(i, ri)) {
1983 allowedRots.add(ri);
1984 }
1985 }
1986
1987 Random rand = new Random();
1988 int nRots = allowedRots.size();
1989 if (monteCarloTesting) {
1990 rand.setSeed(nRots);
1991 }
1992 if (nRots > 1) {
1993 boolean validMove = !useAllElims;
1994 int indexRI;
1995 do {
1996 int ri = rand.nextInt(nRots);
1997 indexRI = allowedRots.get(ri);
1998 if (useAllElims) {
1999 validMove = checkValidMove(i, indexRI, rotamers);
2000 }
2001 } while (!validMove);
2002 rotamers[i] = indexRI;
2003 }
2004 }
2005 }
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020 private double rotamerOptimizationDEE(MolecularAssembly molecularAssembly, Residue[] residues,
2021 int i, int[] currentRotamers, double lowEnergy, int[] optimum, double[] permutationEnergies) {
2022
2023 if (i == 0) {
2024 evaluatedPermutations = 0;
2025 }
2026
2027 int nResidues = residues.length;
2028 Residue residuei = residues[i];
2029 Rotamer[] rotamersi = residuei.getRotamers();
2030 int lenri = rotamersi.length;
2031 double currentEnergy = Double.MAX_VALUE;
2032 List<Residue> resList = Arrays.asList(residues);
2033
2034
2035
2036 if (i < nResidues - 1) {
2037
2038 for (int ri = 0; ri < lenri; ri++) {
2039
2040 if (eR.check(i, ri)) {
2041 continue;
2042 }
2043
2044
2045 boolean deadEnd = false;
2046 for (int j = 0; j < i; j++) {
2047 int rj = currentRotamers[j];
2048 deadEnd = eR.check(j, rj, i, ri);
2049 if (deadEnd) {
2050 break;
2051 }
2052 }
2053 if (deadEnd) {
2054 continue;
2055 }
2056 applyRotamer(residuei, rotamersi[ri]);
2057 currentRotamers[i] = ri;
2058 double rotEnergy = rotamerOptimizationDEE(molecularAssembly, residues, i + 1,
2059 currentRotamers, lowEnergy, optimum, permutationEnergies);
2060 if (rotEnergy < currentEnergy) {
2061 currentEnergy = rotEnergy;
2062 }
2063 if (rotEnergy < lowEnergy) {
2064 optimum[i] = ri;
2065 lowEnergy = rotEnergy;
2066 }
2067 }
2068 } else {
2069 if (ensembleStates == null) {
2070 ensembleStates = new ArrayList<>();
2071 }
2072
2073
2074
2075
2076
2077
2078 for (int ri = 0; ri < lenri; ri++) {
2079
2080 if (eR.check(i, ri)) {
2081 continue;
2082 }
2083 currentRotamers[i] = ri;
2084
2085
2086 boolean deadEnd = false;
2087 for (int j = 0; j < i; j++) {
2088 int rj = currentRotamers[j];
2089 deadEnd = eR.check(j, rj, i, ri);
2090 if (deadEnd) {
2091 break;
2092 }
2093 }
2094 if (deadEnd) {
2095 continue;
2096 }
2097 applyRotamer(residuei, rotamersi[ri]);
2098
2099 approximateEnergy = computeEnergy(residues, currentRotamers, false);
2100 double comparisonEnergy = approximateEnergy;
2101 evaluatedPermutations++;
2102
2103 if (useForceFieldEnergy) {
2104 double amoebaEnergy = Double.NaN;
2105 try {
2106
2107 amoebaEnergy =
2108 currentEnergy(resList) + eE.getTotalRotamerPhBias(resList, currentRotamers, pH, pHRestraint);
2109 } catch (ArithmeticException ex) {
2110 logger.warning(
2111 format(" Exception %s in calculating full AMOEBA energy for permutation %d", ex,
2112 evaluatedPermutations));
2113 }
2114 comparisonEnergy = amoebaEnergy;
2115 }
2116 if (permutationEnergies != null) {
2117 permutationEnergies[evaluatedPermutations - 1] = comparisonEnergy;
2118 }
2119 if (algorithmListener != null) {
2120 algorithmListener.algorithmUpdate(molecularAssembly);
2121 }
2122 if (ensembleNumber > 1) {
2123 if (rank0 && printFiles) {
2124 try {
2125 FileWriter fw = new FileWriter(ensembleFile, true);
2126 BufferedWriter bw = new BufferedWriter(fw);
2127 bw.write(format("MODEL %d", evaluatedPermutations));
2128 for (int j = 0; j < 75; j++) {
2129 bw.write(" ");
2130 }
2131 bw.newLine();
2132 bw.flush();
2133 ensembleFilter.writeFile(ensembleFile, true);
2134 bw.write("ENDMDL");
2135 for (int j = 0; j < 64; j++) {
2136 bw.write(" ");
2137 }
2138 bw.newLine();
2139 bw.close();
2140 } catch (IOException e) {
2141 logger.warning(format("Exception writing to file: %s", ensembleFile.getName()));
2142 }
2143 }
2144 ResidueState[] states = ResidueState.storeAllCoordinates(residues);
2145 ensembleStates.add(new ObjectPair<>(states, comparisonEnergy));
2146 }
2147
2148 if (comparisonEnergy < currentEnergy) {
2149 currentEnergy = comparisonEnergy;
2150 }
2151
2152 if (comparisonEnergy < lowEnergy) {
2153 lowEnergy = comparisonEnergy;
2154 optimum[i] = ri;
2155 }
2156
2157 if (useForceFieldEnergy) {
2158
2159 logIfRank0(format(" %6e AMOEBA: %12.4f 3-Body: %12.4f Neglected: %12.4f (%12.4f)",
2160 (double) evaluatedPermutations, comparisonEnergy, approximateEnergy,
2161 comparisonEnergy - approximateEnergy, lowEnergy));
2162 } else {
2163 logIfRank0(
2164 format(" %12s %25f %25f", evaluatedPermutations, approximateEnergy, lowEnergy));
2165 }
2166 }
2167
2168 ensembleStates.sort(null);
2169 }
2170 return currentEnergy;
2171 }
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182 private double dryRun(Residue[] residues, int i, int[] currentRotamers) {
2183
2184 if (i == 0) {
2185 evaluatedPermutations = 0;
2186 evaluatedPermutationsPrint = 1000;
2187 }
2188
2189 if (evaluatedPermutations >= evaluatedPermutationsPrint) {
2190 if (evaluatedPermutations % evaluatedPermutationsPrint == 0) {
2191 logIfRank0(
2192 format(" The permutations have reached %10.4e.", (double) evaluatedPermutationsPrint));
2193 evaluatedPermutationsPrint *= 10;
2194 }
2195 }
2196
2197 int nResidues = residues.length;
2198 Residue residuei = residues[i];
2199 Rotamer[] rotamersi = residuei.getRotamers();
2200 int lenri = rotamersi.length;
2201 if (i < nResidues - 1) {
2202 for (int ri = 0; ri < lenri; ri++) {
2203 if (eR.check(i, ri)) {
2204 continue;
2205 }
2206 boolean deadEnd = false;
2207 for (int j = 0; j < i; j++) {
2208 int rj = currentRotamers[j];
2209 deadEnd = eR.check(j, rj, i, ri);
2210 if (deadEnd) {
2211 break;
2212 }
2213 }
2214 if (deadEnd) {
2215 continue;
2216 }
2217 currentRotamers[i] = ri;
2218 dryRun(residues, i + 1, currentRotamers);
2219 }
2220 } else {
2221
2222 for (int ri = 0; ri < lenri; ri++) {
2223 if (eR.check(i, ri)) {
2224 continue;
2225 }
2226 currentRotamers[i] = ri;
2227 boolean deadEnd = false;
2228 for (int j = 0; j < i; j++) {
2229 int rj = currentRotamers[j];
2230 deadEnd = eR.check(j, rj, i, ri);
2231 if (deadEnd) {
2232 break;
2233 }
2234 }
2235 if (!deadEnd) {
2236 evaluatedPermutations++;
2237 }
2238 }
2239 }
2240
2241 return 0.0;
2242 }
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255 private double dryRunForEnsemble(Residue[] residues, int i, int[] currentRotamers,
2256 double gmecEnergy, double[] permutationEnergies, int[][] permutations) {
2257
2258 if (i == 0) {
2259 evaluatedPermutations = 0;
2260 }
2261 int nResidues = residues.length;
2262 Residue residuei = residues[i];
2263 Rotamer[] rotamersi = residuei.getRotamers();
2264 int lenri = rotamersi.length;
2265 if (i < nResidues - 1) {
2266 for (int ri = 0; ri < lenri; ri++) {
2267 if (eR.check(i, ri)) {
2268 continue;
2269 }
2270 boolean deadEnd = false;
2271 for (int j = 0; j < i; j++) {
2272 int rj = currentRotamers[j];
2273 deadEnd = eR.check(j, rj, i, ri);
2274 if (deadEnd) {
2275 break;
2276 }
2277 }
2278 if (deadEnd) {
2279 continue;
2280 }
2281 currentRotamers[i] = ri;
2282 dryRunForEnsemble(residues, i + 1, currentRotamers, gmecEnergy, permutationEnergies,
2283 permutations);
2284 }
2285 } else {
2286
2287 for (int ri = 0; ri < lenri; ri++) {
2288 if (eR.check(i, ri)) {
2289 continue;
2290 }
2291 currentRotamers[i] = ri;
2292 boolean deadEnd = false;
2293 for (int j = 0; j < i; j++) {
2294 int rj = currentRotamers[j];
2295 deadEnd = eR.check(j, rj, i, ri);
2296 if (deadEnd) {
2297 break;
2298 }
2299 }
2300 if (deadEnd) {
2301 continue;
2302 }
2303 if (permutationEnergies[evaluatedPermutations] - gmecEnergy < ensembleEnergy) {
2304 permutations[evaluatedPermutations] = new int[nResidues];
2305 arraycopy(currentRotamers, 0, permutations[evaluatedPermutations], 0, nResidues);
2306 }
2307 evaluatedPermutations++;
2308 }
2309 }
2310 return 0.0;
2311 }
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321 public void partitionFunction(Residue[] residues, int i, int[] currentRotamers) throws Exception {
2322
2323 final double LOG10 = log(10.0);
2324 final double beta = 1.0 / (Constants.kB * temperature);
2325
2326 if (i == 0) {
2327 totalBoltzmann = 0;
2328 evaluatedPermutations = 0;
2329 evaluatedPermutationsPrint = 1000;
2330 }
2331
2332 if (evaluatedPermutations >= evaluatedPermutationsPrint) {
2333 if (evaluatedPermutations % evaluatedPermutationsPrint == 0) {
2334 logIfRank0(format(" The permutations have reached %10.4e.", (double) evaluatedPermutationsPrint));
2335 evaluatedPermutationsPrint *= 10;
2336 }
2337 }
2338
2339 int nResidues = residues.length;
2340 Residue residuei = residues[i];
2341 Rotamer[] rotamersi = residuei.getRotamers();
2342 int lenri = rotamersi.length;
2343 if (i < nResidues - 1) {
2344 for (int ri = 0; ri < lenri; ri++) {
2345 if (eR.check(i, ri)) {
2346 continue;
2347 }
2348 boolean deadEnd = false;
2349 for (int j = 0; j < i; j++) {
2350 int rj = currentRotamers[j];
2351 deadEnd = eR.check(j, rj, i, ri);
2352 if (deadEnd) {
2353 break;
2354 }
2355 }
2356 if (deadEnd) {
2357 continue;
2358 }
2359 currentRotamers[i] = ri;
2360 partitionFunction(residues, i + 1, currentRotamers);
2361 }
2362 } else {
2363
2364 for (int ri = 0; ri < lenri; ri++) {
2365 if (eR.check(i, ri)) {
2366 continue;
2367 }
2368 currentRotamers[i] = ri;
2369 boolean deadEnd = false;
2370 for (int j = 0; j < i; j++) {
2371 int rj = currentRotamers[j];
2372 deadEnd = eR.check(j, rj, i, ri);
2373 if (deadEnd) {
2374 break;
2375 }
2376 }
2377 if (!deadEnd) {
2378 evaluatedPermutations++;
2379
2380 energyRegion.init(eE, residues, currentRotamers, threeBodyTerm);
2381 parallelTeam.execute(energyRegion);
2382 double selfEnergy = energyRegion.getSelf();
2383
2384 if (recomputeSelf) {
2385 int count = 0;
2386 for (Residue residue : residues) {
2387 double bias7 = 0;
2388 double biasCurrent = 0;
2389 Rotamer[] rotamers = residue.getRotamers();
2390 int currentRotamer = currentRotamers[count];
2391 switch (rotamers[currentRotamer].getName()) {
2392 case "HIE" -> {
2393 bias7 = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.HIStoHIE.pKa - 7)) -
2394 TitrationUtils.Titration.HIStoHIE.freeEnergyDiff;
2395 biasCurrent = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.HIStoHIE.pKa - pH)) -
2396 TitrationUtils.Titration.HIStoHIE.freeEnergyDiff;
2397 }
2398 case "HID" -> {
2399 bias7 = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.HIStoHID.pKa - 7)) -
2400 TitrationUtils.Titration.HIStoHID.freeEnergyDiff;
2401 biasCurrent = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.HIStoHID.pKa - pH)) -
2402 TitrationUtils.Titration.HIStoHID.freeEnergyDiff;
2403 }
2404 case "ASP" -> {
2405 bias7 = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.ASHtoASP.pKa - 7)) -
2406 TitrationUtils.Titration.ASHtoASP.freeEnergyDiff;
2407 biasCurrent = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.ASHtoASP.pKa - pH)) -
2408 TitrationUtils.Titration.ASHtoASP.freeEnergyDiff;
2409 }
2410 case "GLU" -> {
2411 bias7 = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.GLHtoGLU.pKa - 7)) -
2412 TitrationUtils.Titration.GLHtoGLU.freeEnergyDiff;
2413 biasCurrent = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.GLHtoGLU.pKa - pH)) -
2414 TitrationUtils.Titration.GLHtoGLU.freeEnergyDiff;
2415 }
2416 case "LYD" -> {
2417 bias7 = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.LYStoLYD.pKa - 7)) -
2418 TitrationUtils.Titration.LYStoLYD.freeEnergyDiff;
2419 biasCurrent = (LOG10 * Constants.R * temperature * (TitrationUtils.Titration.LYStoLYD.pKa - pH)) -
2420 TitrationUtils.Titration.LYStoLYD.freeEnergyDiff;
2421 }
2422 default -> {
2423 }
2424 }
2425 selfEnergy = selfEnergy - bias7 + biasCurrent;
2426 count += 1;
2427 }
2428 }
2429
2430
2431 double totalEnergy = eE.getBackboneEnergy() + selfEnergy +
2432 energyRegion.getTwoBody() + energyRegion.getThreeBody();
2433
2434
2435 if (evaluatedPermutations == 1) {
2436 refEnergy = totalEnergy;
2437 }
2438 double boltzmannWeight = exp(-beta * (totalEnergy - refEnergy));
2439
2440
2441 for (int res = 0; res < residues.length; res++) {
2442 int currentRotamer = currentRotamers[res];
2443 populationBoltzmann[res][currentRotamer] += boltzmannWeight;
2444 }
2445
2446
2447 totalBoltzmann += boltzmannWeight;
2448 }
2449 }
2450 }
2451 }
2452
2453
2454
2455
2456
2457
2458 public double getRefEnergy() {
2459 return refEnergy;
2460 }
2461
2462
2463
2464
2465
2466
2467 public double getTotalBoltzmann() {
2468 return totalBoltzmann;
2469 }
2470
2471
2472
2473
2474
2475
2476 public double[][] getFraction() {
2477 return fraction;
2478 }
2479
2480
2481
2482
2483
2484
2485 public double[][] getPopulationBoltzmann() {
2486 return populationBoltzmann;
2487 }
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497 public void getFractions(Residue[] residues, int i, int[] currentRotamers) throws Exception {
2498 populationBoltzmann = new double[residues.length][56];
2499 if (!usingBoxOptimization) {
2500 partitionFunction(residues, i, currentRotamers);
2501 optimum = new int[residues.length];
2502 for (int m = 0; m < fraction.length; m++) {
2503 for (int n = 0; n < 56; n++) {
2504 fraction[m][n] = populationBoltzmann[m][n] / totalBoltzmann;
2505 if (n > 0 && fraction[m][n] > fraction[m][n - 1]) {
2506 optimum[m] = n;
2507 } else if (n == 0) {
2508 optimum[m] = n;
2509 }
2510 }
2511 Rotamer highestPopRot = residues[m].getRotamers()[optimum[m]];
2512 RotamerLibrary.applyRotamer(residues[m], highestPopRot);
2513 }
2514 }
2515 logger.info("\n Total permutations evaluated: " + evaluatedPermutations + "\n");
2516 }
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526 public void getFractions(Residue[] residues, int i, int[] currentRotamers, boolean usingBoxOptimization) throws Exception {
2527 double[][] fractionSubset = new double[residues.length][56];
2528 populationBoltzmann = new double[residues.length][56];
2529 partitionFunction(residues, i, currentRotamers);
2530 optimumSubset = new int[residues.length];
2531 for (int m = 0; m < fractionSubset.length; m++) {
2532 for (int n = 0; n < 56; n++) {
2533 fractionSubset[m][n] = populationBoltzmann[m][n] / totalBoltzmann;
2534 if (n > 0 && fractionSubset[m][n] > fractionSubset[m][n - 1]) {
2535 optimumSubset[m] = n;
2536 } else if (n == 0) {
2537 optimumSubset[m] = n;
2538 }
2539 }
2540 Rotamer highestPopRot = residues[m].getRotamers()[optimumSubset[m]];
2541 RotamerLibrary.applyRotamer(residues[m], highestPopRot);
2542 Residue residue = residues[m];
2543 int index = residueList.indexOf(residue);
2544 fraction[index] = fractionSubset[m];
2545 optimum[index] = optimumSubset[m];
2546 }
2547 logger.info("\n Total permutations evaluated: " + evaluatedPermutations + "\n");
2548 }
2549
2550
2551
2552
2553
2554
2555
2556 public double[][] getProtonationPopulations(Residue[] residues) {
2557 double[][] populations = new double[residues.length][3];
2558 int residueIndex = 0;
2559 for (Residue residue : residues) {
2560 int index = residueList.indexOf(residue);
2561
2562 double protSum = 0;
2563 double deprotSum = 0;
2564 double tautomerSum = 0;
2565 Rotamer[] rotamers = residue.getRotamers();
2566 for (int rotIndex = 0; rotIndex < rotamers.length; rotIndex++) {
2567 switch (rotamers[rotIndex].getName()) {
2568 case "HIS":
2569 case "LYS":
2570 case "GLH":
2571 case "ASH":
2572 case "CYS":
2573 protSum += fraction[index][rotIndex];
2574 populations[residueIndex][0] = protSum;
2575 break;
2576 case "HIE":
2577 case "LYD":
2578 case "GLU":
2579 case "ASP":
2580 case "CYD":
2581 deprotSum += fraction[index][rotIndex];
2582 populations[residueIndex][1] = deprotSum;
2583 break;
2584 case "HID":
2585 tautomerSum += fraction[index][rotIndex];
2586 populations[residueIndex][2] = tautomerSum;
2587 break;
2588 default:
2589 break;
2590 }
2591 }
2592 String formatedProtSum = format("%.6f", protSum);
2593 String formatedDeprotSum = format("%.6f", deprotSum);
2594 String formatedTautomerSum = format("%.6f", tautomerSum);
2595 switch (residue.getName()) {
2596 case "HIS":
2597 case "HIE":
2598 case "HID":
2599 logger.info(residue.getResidueNumber() + "\tHIS" + "\t" + formatedProtSum + "\t" +
2600 "HIE" + "\t" + formatedDeprotSum + "\t" +
2601 "HID" + "\t" + formatedTautomerSum);
2602 break;
2603 case "LYS":
2604 case "LYD":
2605 logger.info(residue.getResidueNumber() + "\tLYS" + "\t" + formatedProtSum + "\t" +
2606 "LYD" + "\t" + formatedDeprotSum);
2607 break;
2608 case "ASH":
2609 case "ASP":
2610 logger.info(residue.getResidueNumber() + "\tASP" + "\t" + formatedDeprotSum + "\t" +
2611 "ASH" + "\t" + formatedProtSum);
2612 break;
2613 case "GLH":
2614 case "GLU":
2615 logger.info(residue.getResidueNumber() + "\tGLU" + "\t" + formatedDeprotSum + "\t" +
2616 "GLH" + "\t" + formatedProtSum);
2617 break;
2618 case "CYS":
2619 case "CYD":
2620 logger.info(residue.getResidueNumber() + "\tCYS" + "\t" + formatedProtSum + "\t" +
2621 "CYD" + "\t" + formatedDeprotSum);
2622 break;
2623 default:
2624 break;
2625 }
2626 residueIndex++;
2627 }
2628
2629
2630 return populations;
2631 }
2632
2633 public double slidingWindowCentered(List<Residue> residueList) throws Exception {
2634 String[] titratableResidues = new String[]{"HIS", "HIE", "HID", "GLU", "GLH", "ASP", "ASH", "LYS", "LYD", "CYS", "CYD"};
2635 List<String> titratableResiudesList = Arrays.asList(titratableResidues);
2636
2637 double e = 0.0;
2638 for (Residue titrationResidue : residueList) {
2639 if (titratableResiudesList.contains(titrationResidue.getName())) {
2640 e = slidingWindowOptimization(residueList, windowSize, increment, revert, distance, Direction.FORWARD, residueList.indexOf(titrationResidue));
2641 }
2642 }
2643 return e;
2644 }
2645
2646
2647
2648
2649
2650
2651
2652 public int[][] getConformers() throws Exception {
2653 int[][] conformers = new int[fraction.length][3];
2654 for (int i = 0; i < fraction.length; i++) {
2655 double[] tempArray = new double[fraction[0].length];
2656 java.lang.System.arraycopy(fraction[i], 0, tempArray, 0, fraction[0].length);
2657 List<Double> elements = new ArrayList<Double>();
2658 for (int j = 0; j < tempArray.length; j++) {
2659 elements.add(tempArray[j]);
2660 }
2661 Arrays.sort(tempArray);
2662 int count = -1;
2663 for (int k = tempArray.length - 3; k < tempArray.length; k++) {
2664 count++;
2665 conformers[i][count] = elements.indexOf(tempArray[k]);
2666 }
2667 }
2668 return conformers;
2669 }
2670
2671
2672
2673
2674
2675
2676
2677 public int[] getOptimumRotamers() {
2678 return optimum;
2679 }
2680
2681
2682
2683
2684
2685
2686
2687 private double independent(List<Residue> residues) {
2688 double e = 0.0;
2689 List<Residue> singletonResidue = new ArrayList<>(Collections.nCopies(1, null));
2690 for (int i = 0; i < residues.size(); i++) {
2691 Residue residue = residues.get(i);
2692 singletonResidue.set(0, residue);
2693 logger.info(format(" Optimizing %s side-chain.", residue));
2694 Rotamer[] rotamers = residue.getRotamers();
2695 e = Double.MAX_VALUE;
2696 int bestRotamer = 0;
2697 double startingEnergy = 0.0;
2698 for (int j = 0; j < rotamers.length; j++) {
2699 Rotamer rotamer = rotamers[j];
2700 RotamerLibrary.applyRotamer(residue, rotamer);
2701 if (algorithmListener != null) {
2702 algorithmListener.algorithmUpdate(molecularAssembly);
2703 }
2704 double newE = Double.NaN;
2705 try {
2706 if (rotamer.isTitrating) {
2707 newE = currentEnergy(singletonResidue) + rotamer.getRotamerPhBias();
2708 } else {
2709 newE = currentEnergy(singletonResidue);
2710 }
2711
2712 if (j == 0) {
2713 startingEnergy = newE;
2714 newE = 0.0;
2715 } else {
2716 newE -= startingEnergy;
2717 }
2718 logger.info(format(" Energy %8s %-2d: %s", residue.toString(rotamers[j]), j, formatEnergy(newE)));
2719 double singularityThreshold = -100000;
2720 if (newE < singularityThreshold) {
2721 String message = format(" Rejecting as energy (%s << %s) is likely an error.", formatEnergy(newE), formatEnergy(singularityThreshold));
2722 logger.info(message);
2723 newE = Double.MAX_VALUE;
2724 }
2725 } catch (ArithmeticException ex) {
2726 logger.info(format(" Exception %s in energy calculations during independent for %s-%d", ex, residue, j));
2727 }
2728 if (newE < e) {
2729 e = newE;
2730 bestRotamer = j;
2731 }
2732 }
2733 Rotamer rotamer = rotamers[bestRotamer];
2734 RotamerLibrary.applyRotamer(residue, rotamer);
2735 optimum[i] = bestRotamer;
2736 logger.info(format(" Best Energy %8s %-2d: %s", residue.toString(rotamer), bestRotamer, formatEnergy(e)));
2737
2738 if (algorithmListener != null) {
2739 algorithmListener.algorithmUpdate(molecularAssembly);
2740 }
2741 if (terminate) {
2742 logger.info(format("\n Terminating after residue %s.\n", residue));
2743 break;
2744 }
2745 }
2746 return e;
2747 }
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757 private boolean firstValidPerm(Residue[] residues, int i, int[] currentRotamers) {
2758 int nResidues = residues.length;
2759 Residue residuei = residues[i];
2760 Rotamer[] rotamersi = residuei.getRotamers();
2761 int lenri = rotamersi.length;
2762 if (i < nResidues - 1) {
2763 for (int ri = 0; ri < lenri; ri++) {
2764 if (eR.check(i, ri)) {
2765 continue;
2766 }
2767 boolean deadEnd = false;
2768 for (int j = 0; j < i; j++) {
2769 int rj = currentRotamers[j];
2770 deadEnd = eR.check(j, rj, i, ri);
2771 if (deadEnd) {
2772 break;
2773 }
2774 }
2775 if (deadEnd) {
2776 continue;
2777 }
2778 currentRotamers[i] = ri;
2779 if (firstValidPerm(residues, i + 1, currentRotamers)) {
2780 return true;
2781 }
2782 }
2783 } else {
2784
2785 for (int ri = 0; ri < lenri; ri++) {
2786 if (eR.check(i, ri)) {
2787 continue;
2788 }
2789 currentRotamers[i] = ri;
2790 boolean deadEnd = false;
2791 for (int j = 0; j < i; j++) {
2792 int rj = currentRotamers[j];
2793 deadEnd = eR.check(j, rj, i, ri);
2794 if (deadEnd) {
2795 break;
2796 }
2797 }
2798 if (!deadEnd) {
2799 break;
2800 }
2801 }
2802 return true;
2803 }
2804 return false;
2805 }
2806
2807
2808
2809
2810
2811
2812
2813 protected double globalOptimization(List<Residue> residueList) {
2814 int currentEnsemble = Integer.MAX_VALUE;
2815 Residue[] residues = residueList.toArray(new Residue[0]);
2816 int nResidues = residues.length;
2817 int[] currentRotamers = new int[nResidues];
2818 optimumSubset = new int[nResidues];
2819
2820 int iterations = 0;
2821 boolean finalTry = false;
2822 int bestEnsembleTargetDiffThusFar = Integer.MAX_VALUE;
2823 double bestBufferThusFar = ensembleBuffer;
2824 double startingBuffer = ensembleBuffer;
2825
2826 if (ensembleEnergy > 0.0) {
2827 ensembleBuffer = ensembleEnergy;
2828 applyEliminationCriteria(residues, true, true);
2829
2830
2831 double permutations = 1;
2832 double singletonPermutations = 1;
2833 for (int i = 0; i < nResidues; i++) {
2834 Residue residue = residues[i];
2835 Rotamer[] rotamers = residue.getRotamers();
2836 int nr = rotamers.length;
2837 if (nr > 1) {
2838 int nrot = 0;
2839 for (int ri = 0; ri < nr; ri++) {
2840 if (!eR.eliminatedSingles[i][ri]) {
2841 nrot++;
2842 }
2843 }
2844 permutations *= rotamers.length;
2845 if (nrot > 1) {
2846 singletonPermutations *= nrot;
2847 }
2848 }
2849 }
2850 dryRun(residues, 0, currentRotamers);
2851 double pairTotalElimination = singletonPermutations - (double) evaluatedPermutations;
2852 double afterPairElim = singletonPermutations - pairTotalElimination;
2853 if (evaluatedPermutations == 0) {
2854 logger.severe(
2855 " No valid path through rotamer space found; try recomputing without pruning or using ensemble.");
2856 }
2857 if (rank0 && printFiles && ensembleFile == null) {
2858 File file = molecularAssembly.getFile();
2859 String filename = FilenameUtils.removeExtension(file.getAbsolutePath());
2860 ensembleFile = new File(filename + ".ens");
2861 if (ensembleFile.exists()) {
2862 for (int i = 2; i < 1000; i++) {
2863 ensembleFile = new File(filename + ".ens_" + i);
2864 if (!ensembleFile.exists()) {
2865 break;
2866 }
2867 }
2868 if (ensembleFile.exists()) {
2869 logger.warning(
2870 format(" Versioning failed: appending to end of file %s", ensembleFile.getName()));
2871 }
2872 }
2873 ensembleFilter = new PDBFilter(new File(ensembleFile.getName()), molecularAssembly, null,
2874 null);
2875 logger.info(format(" Ensemble file: %s", ensembleFile.getName()));
2876 }
2877 logIfRank0(format("%30s %35s %35s", "Condition", "Number of Permutations Left",
2878 "Number of Permutations Removed"));
2879 logIfRank0(format("%30s %35s %35s", "No Eliminations", permutations, ""));
2880 logIfRank0(format("%30s %35s %35s", "Single Eliminations", singletonPermutations,
2881 permutations - singletonPermutations));
2882 logIfRank0(
2883 format("%30s %35s %35s", "Pair Eliminations", afterPairElim, pairTotalElimination));
2884 logIfRank0(
2885 format("%30s %35s %35s", "Single and Pair Eliminations", (double) evaluatedPermutations,
2886 pairTotalElimination + (permutations - singletonPermutations)));
2887
2888 logIfRank0("\n Energy of permutations:");
2889 logIfRank0(format(" %12s %25s %25s", "Permutation", "Energy", "Lowest Possible Energy"));
2890
2891 double e;
2892 if (useMonteCarlo()) {
2893 firstValidPerm(residues, 0, currentRotamers);
2894 arraycopy(currentRotamers, 0, optimumSubset, 0, nResidues);
2895 rotamerOptimizationMC(residues, optimumSubset, currentRotamers, nMCSteps, false, mcUseAll);
2896
2897 logIfRank0(" Ensembles not currently compatible with Monte Carlo search");
2898
2899 } else {
2900 double[] permutationEnergies = new double[evaluatedPermutations];
2901 ensembleStates = new ArrayList<>();
2902
2903 e = rotamerOptimizationDEE(molecularAssembly, residues, 0, currentRotamers, Double.MAX_VALUE,
2904 optimumSubset, permutationEnergies);
2905 int[][] acceptedPermutations = new int[evaluatedPermutations][];
2906 logIfRank0(format(
2907 "\n Checking permutations for distance < %5.3f kcal/mol from GMEC energy %10.8f kcal/mol",
2908 ensembleEnergy, e));
2909 dryRunForEnsemble(residues, 0, currentRotamers, e, permutationEnergies,
2910 acceptedPermutations);
2911 int numAcceptedPermutations = 0;
2912
2913 for (int i = 0; i < acceptedPermutations.length; i++) {
2914 if (acceptedPermutations[i] != null) {
2915 ++numAcceptedPermutations;
2916 logIfRank0(
2917 format(" Accepting permutation %d at %8.6f < %8.6f", i, permutationEnergies[i] - e,
2918 ensembleEnergy));
2919 for (int j = 0; j < nResidues; j++) {
2920 Residue residuej = residues[j];
2921 Rotamer[] rotamersj = residuej.getRotamers();
2922 RotamerLibrary.applyRotamer(residuej, rotamersj[acceptedPermutations[i][j]]);
2923 }
2924
2925 ResidueState[] states = ResidueState.storeAllCoordinates(residues);
2926 ensembleStates.add(new ObjectPair<>(states, permutationEnergies[i]));
2927
2928 if (printFiles && rank0) {
2929 try {
2930 FileWriter fw = new FileWriter(ensembleFile, true);
2931 BufferedWriter bw = new BufferedWriter(fw);
2932 bw.write(format("MODEL %d", numAcceptedPermutations));
2933 for (int j = 0; j < 75; j++) {
2934 bw.write(" ");
2935 }
2936 bw.newLine();
2937 bw.flush();
2938 ensembleFilter.writeFile(ensembleFile, true);
2939 bw.write("ENDMDL");
2940 for (int j = 0; j < 64; j++) {
2941 bw.write(" ");
2942 }
2943 bw.newLine();
2944 bw.close();
2945 } catch (IOException ex) {
2946 logger.warning(format(" Exception writing to file: %s", ensembleFile.getName()));
2947 }
2948 }
2949 }
2950 }
2951 logIfRank0(format(" Number of permutations within %5.3f kcal/mol of GMEC energy: %6.4e",
2952 ensembleEnergy, (double) numAcceptedPermutations));
2953 ensembleStates.sort(null);
2954 }
2955
2956 logIfRank0(" Final rotamers:");
2957 logIfRank0(format("%17s %10s %11s %12s %11s", "Residue", "Chi 1", "Chi 2", "Chi 3", "Chi 4"));
2958 for (int i = 0; i < nResidues; i++) {
2959 Residue residue = residues[i];
2960 Rotamer[] rotamers = residue.getRotamers();
2961 int ri = optimumSubset[i];
2962 Rotamer rotamer = rotamers[ri];
2963 logIfRank0(format(" %c (%7s,%2d) %s", residue.getChainID(), residue.toString(rotamer), ri,
2964 rotamer.toAngleString()));
2965 RotamerLibrary.applyRotamer(residue, rotamer);
2966 }
2967 logIfRank0("\n");
2968
2969 double sumSelfEnergy = 0;
2970 double sumPairEnergy = 0;
2971 double sumTrimerEnergy = 0;
2972 for (int i = 0; i < nResidues; i++) {
2973 Residue residue = residues[i];
2974 Rotamer[] rotamers = residue.getRotamers();
2975 int ri = optimumSubset[i];
2976 sumSelfEnergy += eE.getSelf(i, ri);
2977 logIfRank0(
2978 format(" Final self Energy (%8s,%2d): %12.4f", residue.toString(rotamers[ri]), ri,
2979 eE.getSelf(i, ri)));
2980 }
2981 for (int i = 0; i < nResidues - 1; i++) {
2982 Residue residueI = residues[i];
2983 Rotamer[] rotI = residueI.getRotamers();
2984 int ri = optimumSubset[i];
2985 for (int j = i + 1; j < nResidues; j++) {
2986 Residue residueJ = residues[j];
2987 Rotamer[] rotJ = residueJ.getRotamers();
2988 int rj = optimumSubset[j];
2989 sumPairEnergy += eE.get2Body(i, ri, j, rj);
2990 if (eE.get2Body(i, ri, j, rj) > 10.0) {
2991 logIfRank0(format(" Large Final Pair Energy (%8s,%2d) (%8s,%2d): %12.4f",
2992 residueI.toString(rotI[ri]), ri, residueJ.toString(rotJ[rj]), rj,
2993 eE.get2Body(i, ri, j, rj)));
2994 }
2995 }
2996 }
2997
2998 try {
2999
3000 e = currentEnergy(residueList) + eE.getTotalRotamerPhBias(residueList, optimumSubset, pH, pHRestraint);
3001 } catch (ArithmeticException ex) {
3002 e = Double.NaN;
3003 logger.severe(
3004 format(" Exception %s in calculating current energy at the end of triples", ex));
3005 }
3006
3007 logIfRank0(format(" %12s %25s %25s", "Type", "Energy", "Lowest Possible Energy"));
3008 logIfRank0(format(" %12s %25f %25s", "Self:", sumSelfEnergy, ""));
3009 logIfRank0(format(" %12s %25f %25s", "Pair:", sumPairEnergy, ""));
3010
3011 approximateEnergy = eE.getBackboneEnergy() + sumSelfEnergy + sumPairEnergy;
3012
3013 if (threeBodyTerm) {
3014 for (int i = 0; i < nResidues - 2; i++) {
3015 int ri = optimumSubset[i];
3016 for (int j = i + 1; j < nResidues - 1; j++) {
3017 int rj = optimumSubset[j];
3018 for (int k = j + 1; k < nResidues; k++) {
3019 int rk = optimumSubset[k];
3020 try {
3021 sumTrimerEnergy += eE.get3Body(residues, i, ri, j, rj, k, rk);
3022 } catch (Exception ex) {
3023 logger.warning(ex.toString());
3024 }
3025 }
3026 }
3027 }
3028 approximateEnergy += sumTrimerEnergy;
3029 double higherOrderEnergy =
3030 e - sumSelfEnergy - sumPairEnergy - sumTrimerEnergy - eE.getBackboneEnergy();
3031 logIfRank0(format(" %12s %25f %25s", "Trimer:", sumTrimerEnergy, ""));
3032 logIfRank0(format(" %12s %25f %25s", "Neglected:", higherOrderEnergy, ""));
3033 } else {
3034 double higherOrderEnergy = e - sumSelfEnergy - sumPairEnergy - eE.getBackboneEnergy();
3035 logIfRank0(format(" %12s %25f %25s", "Neglected:", higherOrderEnergy, ""));
3036 }
3037 logIfRank0(format(" %12s %25f %25s", "Approximate:", approximateEnergy, ""));
3038 return e;
3039 }
3040
3041
3042
3043 int nPerms = 1;
3044 for (Residue residue : residues) {
3045 Rotamer[] rotamers = residue.getRotamers();
3046 int nr = rotamers.length;
3047 if (nr > 1) {
3048 nPerms *= rotamers.length;
3049 }
3050 if (nPerms > ensembleNumber) {
3051 break;
3052 }
3053 }
3054
3055 if (nPerms < ensembleNumber) {
3056 logger.info(format(
3057 " Requested an ensemble of %d, but only %d permutations exist; returning full ensemble",
3058 ensembleNumber, nPerms));
3059 ensembleNumber = nPerms;
3060 }
3061
3062 while (currentEnsemble != ensembleNumber) {
3063 if (monteCarlo) {
3064 logIfRank0(" Ensemble search not currently compatible with Monte Carlo");
3065 ensembleNumber = 1;
3066 }
3067
3068 if (iterations == 0) {
3069 applyEliminationCriteria(residues, true, true);
3070 } else {
3071 applyEliminationCriteria(residues, false, false);
3072 }
3073
3074
3075
3076 double permutations = 1;
3077 double singletonPermutations = 1;
3078 for (int i = 0; i < nResidues; i++) {
3079 Residue residue = residues[i];
3080 Rotamer[] rotamers = residue.getRotamers();
3081 int nr = rotamers.length;
3082 if (nr > 1) {
3083 int nrot = 0;
3084 for (int ri = 0; ri < nr; ri++) {
3085 if (!eR.eliminatedSingles[i][ri]) {
3086 nrot++;
3087 }
3088 }
3089 permutations *= rotamers.length;
3090 if (nrot > 1) {
3091 singletonPermutations *= nrot;
3092 }
3093 }
3094 }
3095
3096 logIfRank0(" Collecting Permutations:");
3097 dryRun(residues, 0, currentRotamers);
3098
3099 double pairTotalElimination = singletonPermutations - (double) evaluatedPermutations;
3100 double afterPairElim = singletonPermutations - pairTotalElimination;
3101 currentEnsemble = evaluatedPermutations;
3102 if (ensembleNumber == 1 && currentEnsemble == 0) {
3103 logger.severe(
3104 " No valid path through rotamer space found; try recomputing without pruning or using ensemble.");
3105 }
3106 if (ensembleNumber > 1) {
3107 if (rank0 && printFiles && ensembleFile == null) {
3108 File file = molecularAssembly.getFile();
3109 String filename = FilenameUtils.removeExtension(file.getAbsolutePath());
3110 ensembleFile = new File(filename + ".ens");
3111 if (ensembleFile.exists()) {
3112 for (int i = 2; i < 1000; i++) {
3113 ensembleFile = new File(filename + ".ens_" + i);
3114 if (!ensembleFile.exists()) {
3115 break;
3116 }
3117 }
3118 if (ensembleFile.exists()) {
3119 logger.warning(
3120 format(" Versioning failed: appending to end of file %s", ensembleFile.getName()));
3121 }
3122 }
3123 ensembleFilter = new PDBFilter(new File(ensembleFile.getName()), molecularAssembly, null,
3124 null);
3125 logger.info(format(" Ensemble file: %s", ensembleFile.getName()));
3126 }
3127 logIfRank0(format(" Ensemble Search Stats: (buffer: %5.3f, current: %d, target: %d)",
3128 ensembleBuffer, currentEnsemble, ensembleNumber));
3129 }
3130 if (ensembleNumber == 1 || finalTry) {
3131 logIfRank0(format("%30s %35s %35s", "Condition", "Number of Permutations Left",
3132 "Number of Permutations Removed"));
3133 logIfRank0(format("%30s %35s %35s", "No Eliminations", permutations, ""));
3134 logIfRank0(format("%30s %35s %35s", "Single Eliminations", singletonPermutations,
3135 permutations - singletonPermutations));
3136 logIfRank0(
3137 format("%30s %35s %35s", "Pair Eliminations", afterPairElim, pairTotalElimination));
3138 logIfRank0(
3139 format("%30s %35s %35s", "Single and Pair Eliminations", (double) evaluatedPermutations,
3140 pairTotalElimination + (permutations - singletonPermutations)));
3141
3142 logIfRank0("\n Energy of permutations:");
3143 logIfRank0(format(" %12s %25s %25s", "Permutation", "Energy", "Lowest Possible Energy"));
3144
3145 break;
3146 }
3147 if (abs(currentEnsemble - ensembleNumber) < bestEnsembleTargetDiffThusFar) {
3148 bestEnsembleTargetDiffThusFar = Math.abs(currentEnsemble - ensembleNumber);
3149 bestBufferThusFar = ensembleBuffer;
3150 }
3151 if (currentEnsemble > ensembleNumber) {
3152 ensembleBuffer -= ensembleBufferStep;
3153 ensembleBufferStep -= (ensembleBufferStep * 0.01);
3154 iterations++;
3155 } else if (currentEnsemble < ensembleNumber) {
3156 ensembleBuffer += ensembleBufferStep;
3157 ensembleBufferStep -= (ensembleBufferStep * 0.01);
3158 iterations++;
3159 }
3160 if (iterations > 100) {
3161 if (currentEnsemble == 0) {
3162
3163
3164 logIfRank0(" Ensemble still empty; increasing buffer energy.");
3165 startingBuffer = 3 * startingBuffer;
3166 setEnsemble(10, startingBuffer);
3167 iterations = 0;
3168 } else {
3169 ensembleBuffer = bestBufferThusFar;
3170 finalTry = true;
3171 }
3172 }
3173 }
3174
3175 if (currentEnsemble == 0) {
3176 logger.warning(
3177 " No valid rotamer permutations found; results will be unreliable. Try increasing the starting ensemble buffer.");
3178 }
3179 double[] permutationEnergyStub = null;
3180
3181 if (useMonteCarlo()) {
3182 firstValidPerm(residues, 0, currentRotamers);
3183 rotamerOptimizationMC(residues, optimumSubset, currentRotamers, nMCSteps, false, mcUseAll);
3184 } else {
3185 rotamerOptimizationDEE(molecularAssembly, residues, 0, currentRotamers, Double.MAX_VALUE,
3186 optimumSubset, permutationEnergyStub);
3187 }
3188
3189 double[] residueEnergy = new double[nResidues];
3190
3191 double sumSelfEnergy = 0;
3192 double sumLowSelfEnergy = 0;
3193
3194 logIfRank0("\n Energy contributions:");
3195 logIfRank0(format(" %15s %25s %25s", "Type", "Energy", "Lowest Possible Energy"));
3196
3197 for (int i = 0; i < nResidues; i++) {
3198 int ri = optimumSubset[i];
3199 Residue residue = residues[i];
3200 Rotamer[] rotamers = residue.getRotamers();
3201 turnOnAtoms(residue);
3202 RotamerLibrary.applyRotamer(residue, rotamers[ri]);
3203 double self = eE.getSelf(i, ri);
3204 residueEnergy[i] = self;
3205 sumSelfEnergy += self;
3206 double lowest = eE.lowestSelfEnergy(residues, i);
3207 sumLowSelfEnergy += lowest;
3208 if (self - lowest > 10.0) {
3209 logIfRank0(
3210 format(" %15s %25f %25f", "Self (" + residues[i] + "," + ri + "):", self, lowest));
3211 }
3212 }
3213
3214 double sumPairEnergy = 0.0;
3215 double sumLowPairEnergy = 0.0;
3216 double[] resPairEnergy = new double[nResidues];
3217 double[] lowPairEnergy = new double[nResidues];
3218 for (int i = 0; i < nResidues - 1; i++) {
3219 StringBuilder sb = new StringBuilder();
3220 int ri = optimumSubset[i];
3221 double sumPairEnergyI = 0;
3222 double sumLowPairEnergyI = 0;
3223 for (int j = i + 1; j < nResidues; j++) {
3224 int rj = optimumSubset[j];
3225 double pair = eE.get2Body(i, ri, j, rj);
3226 residueEnergy[i] += 0.5 * pair;
3227 residueEnergy[j] += 0.5 * pair;
3228 sumPairEnergy += pair;
3229 sumPairEnergyI += pair;
3230 double lowest = eE.lowestPairEnergy(residues, i, ri, j);
3231 sumLowPairEnergy += lowest;
3232 sumLowPairEnergyI += lowest;
3233 resPairEnergy[i] = 0.5 * pair;
3234 resPairEnergy[j] = 0.5 * pair;
3235 lowPairEnergy[i] = 0.5 * lowest;
3236 lowPairEnergy[j] = 0.5 * lowest;
3237 if (resPairEnergy[i] - lowPairEnergy[i] > 10.0) {
3238 sb.append(format(" Pair Energy (%8s,%2d) (%8s,%2d): %12.4f (Lowest: %12.4f).\n",
3239 residues[i].toFormattedString(false, true), ri,
3240 residues[j].toFormattedString(false, true), rj, pair, lowest));
3241 }
3242 }
3243 if (sumPairEnergyI - sumLowPairEnergyI > 10.0) {
3244 logIfRank0(
3245 format(" %15s %25f %25f", "Self (" + residues[i] + "," + ri + ")", sumPairEnergyI,
3246 sumLowPairEnergyI));
3247 sb.trimToSize();
3248 if (!sb.toString().isEmpty()) {
3249 logIfRank0(sb.toString());
3250 }
3251 }
3252 }
3253
3254 double e = Double.NaN;
3255 try {
3256
3257 e = currentEnergy(residueList) + eE.getTotalRotamerPhBias(residueList, optimumSubset, pH, pHRestraint);
3258 } catch (ArithmeticException ex) {
3259 logger.severe(
3260 format(" Exception %s in calculating current energy at the end of self and pairs", ex));
3261 }
3262 logIfRank0(format(" %15s %25f %25s", "Backbone:", eE.getBackboneEnergy(), ""));
3263 logIfRank0(format(" %15s %25f %25f", "Self:", sumSelfEnergy, sumLowSelfEnergy));
3264 logIfRank0(format(" %15s %25f %25f", "Pair:", sumPairEnergy, sumLowPairEnergy));
3265
3266 approximateEnergy = eE.getBackboneEnergy() + sumSelfEnergy + sumPairEnergy;
3267
3268 double sumTrimerEnergy = 0;
3269 if (threeBodyTerm) {
3270 for (int i = 0; i < nResidues - 2; i++) {
3271 int ri = optimumSubset[i];
3272 for (int j = i + 1; j < nResidues - 1; j++) {
3273 int rj = optimumSubset[j];
3274 for (int k = j + 1; k < nResidues; k++) {
3275 int rk = optimumSubset[k];
3276 try {
3277 double triple = eE.get3Body(residues, i, ri, j, rj, k, rk);
3278 double thirdTrip = triple / 3.0;
3279 residueEnergy[i] += thirdTrip;
3280 residueEnergy[j] += thirdTrip;
3281 residueEnergy[k] += thirdTrip;
3282 sumTrimerEnergy += triple;
3283 } catch (Exception ex) {
3284 logger.warning(ex.toString());
3285 }
3286 }
3287 }
3288 }
3289 approximateEnergy += sumTrimerEnergy;
3290 double higherOrderEnergy =
3291 e - sumSelfEnergy - sumPairEnergy - sumTrimerEnergy - eE.getBackboneEnergy();
3292 logIfRank0(format(" %15s %25f %25s", "Trimer:", sumTrimerEnergy, ""));
3293 logIfRank0(format(" %15s %25f %25s", "Neglected:", higherOrderEnergy, ""));
3294 } else {
3295 double higherOrderEnergy = e - sumSelfEnergy - sumPairEnergy - eE.getBackboneEnergy();
3296 logIfRank0(format(" %15s %25f %25s", "Neglected:", higherOrderEnergy, ""));
3297 }
3298
3299 logIfRank0(format(" %15s %25f %25s", "Approximate:", approximateEnergy, ""));
3300
3301 logIfRank0("\n Final rotamers:");
3302 logIfRank0(
3303 format("%17s %10s %11s %12s %11s %14s", "Residue", "Chi 1", "Chi 2", "Chi 3", "Chi 4",
3304 "Energy"));
3305 for (int i = 0; i < nResidues; i++) {
3306 Residue residue = residues[i];
3307 Rotamer[] rotamers = residue.getRotamers();
3308 int ri = optimumSubset[i];
3309 Rotamer rotamer = rotamers[ri];
3310 logIfRank0(format(" %3d %c (%7s,%2d) %s %12.4f ", i + 1, residue.getChainID(),
3311 residue.toString(rotamers[ri]), ri, rotamer.toAngleString(), residueEnergy[i]));
3312 RotamerLibrary.applyRotamer(residue, rotamer);
3313 }
3314 logIfRank0("\n");
3315 return e;
3316 }
3317
3318
3319
3320
3321
3322
3323
3324 private boolean useMonteCarlo() {
3325 return monteCarlo && (mcNoEnum || (nMCSteps < evaluatedPermutations));
3326 }
3327
3328
3329
3330
3331
3332
3333
3334 private double bruteForce(List<Residue> residueList) {
3335
3336 Residue[] residues = residueList.toArray(new Residue[0]);
3337 int nResidues = residues.length;
3338
3339
3340 double permutations = 1;
3341 for (Residue residue : residues) {
3342 Rotamer[] rotamers = residue.getRotamers();
3343 permutations *= rotamers.length;
3344 }
3345
3346 logger.info(format(" Number of permutations: %16.8e.", permutations));
3347
3348 double e;
3349 useForceFieldEnergy = molecularAssembly.getProperties()
3350 .getBoolean("ro-use-force-field-energy", false);
3351 if (!useForceFieldEnergy) {
3352
3353 setPruning(0);
3354 rotamerEnergies(residues);
3355 int[] currentRotamers = new int[nResidues];
3356 e = decomposedRotamerOptimization(residues, 0, Double.MAX_VALUE, optimum, currentRotamers);
3357
3358
3359 for (int i = 0; i < nResidues; i++) {
3360 Residue residue = residues[i];
3361 Rotamer[] rotamers = residue.getRotamers();
3362 RotamerLibrary.applyRotamer(residue, rotamers[optimum[i]]);
3363 turnOnAtoms(residue);
3364 }
3365
3366
3367 double fullEnergy = 0;
3368 try {
3369
3370 fullEnergy = currentEnergy(residueList) + eE.getTotalRotamerPhBias(residueList, optimum, pH, pHRestraint);
3371 } catch (Exception ex) {
3372 logger.severe(format(" Exception %s in calculating full energy; FFX shutting down", ex));
3373 }
3374
3375 logger.info(format(" Final summation of energies: %16.5f", e));
3376 logger.info(format(" Final energy of optimized structure: %16.5f", fullEnergy));
3377 logger.info(format(" Neglected: %16.5f", fullEnergy - e));
3378 } else {
3379 e = rotamerOptimization(molecularAssembly, residues, 0, Double.MAX_VALUE, optimum);
3380 }
3381
3382 for (int i = 0; i < nResidues; i++) {
3383 Residue residue = residues[i];
3384 Rotamer[] rotamers = residue.getRotamers();
3385 int ri = optimum[i];
3386 Rotamer rotamer = rotamers[ri];
3387 logger.info(format(" %s %s (%d)", residue.getResidueNumber(), rotamer.toString(), ri));
3388 RotamerLibrary.applyRotamer(residue, rotamer);
3389 if (useForceFieldEnergy) {
3390 try {
3391 e = currentEnergy(residueList) + eE.getTotalRotamerPhBias(residueList, optimum, pH, pHRestraint);
3392 } catch (ArithmeticException ex) {
3393 logger.fine(
3394 format(" Exception %s in calculating full AMOEBA energy at the end of brute force",
3395 ex));
3396 }
3397 }
3398 }
3399 return e;
3400 }
3401
3402 @SuppressWarnings("fallthrough")
3403 private double slidingWindowOptimization(List<Residue> residueList, int windowSize, int increment,
3404 boolean revert, double distance, Direction direction, int windowCenter) throws Exception {
3405
3406 long beginTime = -System.nanoTime();
3407 boolean incrementTruncated = false;
3408 boolean firstWindowSaved = false;
3409 int counter = 1;
3410 int windowEnd;
3411
3412 int nOptimize = residueList.size();
3413 if (nOptimize < windowSize) {
3414 windowSize = nOptimize;
3415 logger.info(" Window size too small for given residue range; truncating window size.");
3416 }
3417 switch (direction) {
3418 case BACKWARD:
3419 List<Residue> temp = new ArrayList<>();
3420 for (int i = nOptimize - 1; i >= 0; i--) {
3421 temp.add(residueList.get(i));
3422 }
3423 residueList = temp;
3424
3425 case FORWARD:
3426 for (int windowStart = 0; windowStart + (windowSize - 1) < nOptimize;
3427 windowStart += increment) {
3428 long windowTime = -System.nanoTime();
3429
3430 if (windowCenter > -1) {
3431 windowStart = windowCenter - windowSize / 2;
3432 if (windowStart < 0) {
3433 windowStart = 0;
3434 }
3435 windowEnd = windowCenter + windowSize / 2;
3436 if (windowEnd >= residueList.size()) {
3437 windowEnd = residueList.size() - 1;
3438 }
3439 } else {
3440 windowEnd = windowStart + (windowSize - 1);
3441 }
3442
3443 if (windowCenter > -1) {
3444 logIfRank0(format("\n Center window at residue %d.\n", residueList.get(windowCenter).getResidueNumber()));
3445 } else {
3446 logIfRank0(format("\n Iteration %d of the sliding window.\n", counter++));
3447 }
3448
3449 Residue firstResidue = residueList.get(windowStart);
3450 Residue lastResidue = residueList.get(windowEnd);
3451 if (firstResidue != lastResidue) {
3452 logIfRank0(
3453 format(" Residues %s ... %s", firstResidue.toString(), lastResidue.toString()));
3454 } else {
3455 logIfRank0(format(" Residue %s", firstResidue.toString()));
3456 }
3457 List<Residue> currentWindow = new ArrayList<>();
3458 for (int i = windowStart; i <= windowEnd; i++) {
3459 Residue residue = residueList.get(i);
3460 currentWindow.add(residue);
3461 }
3462
3463 if (distance > 0) {
3464 for (int i = windowStart; i <= windowEnd; i++) {
3465 if (windowCenter > -1) {
3466 i = windowCenter;
3467 }
3468 Residue residuei = residueList.get(i);
3469 int indexI = allResiduesList.indexOf(residuei);
3470 int lengthRi;
3471 lengthRi = residuei.getRotamers().length;
3472 for (int ri = 0; ri < lengthRi; ri++) {
3473 for (int j = 0; j < nAllResidues; j++) {
3474 Residue residuej = allResiduesArray[j];
3475 Rotamer[] rotamersj = residuej.getRotamers();
3476 if (currentWindow.contains(residuej) || rotamersj == null) {
3477 continue;
3478 }
3479 int lengthRj = rotamersj.length;
3480 for (int rj = 0; rj < lengthRj; rj++) {
3481 double rotamerSeparation = dM.get2BodyDistance(indexI, ri, j, rj);
3482 if (rotamerSeparation <= distance) {
3483 if (!currentWindow.contains(residuej)) {
3484 logIfRank0(format(" Adding residue %s at distance %16.8f Ang from %s %d.",
3485 residuej.toFormattedString(false, true), rotamerSeparation,
3486 residuei.toFormattedString(false, true), ri));
3487 currentWindow.add(residuej);
3488 }
3489 break;
3490 }
3491 }
3492 }
3493 }
3494 if (windowCenter > -1) {
3495 break;
3496 }
3497 }
3498 }
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520 if (counter > 2 && windowSize <= increment && firstResidue.getResidueType() == NA) {
3521 Residue prevResidue = firstResidue.getPreviousResidue();
3522 if (prevResidue != null && prevResidue.getResidueType() == NA
3523 && !currentWindow.contains(prevResidue) && prevResidue.getRotamers() != null) {
3524 logIfRank0(format(
3525 " Adding nucleic acid residue 5' of window start %s to give it flexibility about its sugar pucker.",
3526 prevResidue));
3527 currentWindow.add(prevResidue);
3528 }
3529 }
3530 sortResidues(currentWindow);
3531
3532 if (revert) {
3533 ResidueState[] coordinates = ResidueState.storeAllCoordinates(currentWindow);
3534 double startingEnergy = Double.NaN;
3535 try {
3536 startingEnergy = currentEnergy(currentWindow);
3537 } catch (ArithmeticException ex) {
3538 logger.severe(format(
3539 " Exception %s in calculating starting energy of a window; FFX shutting down",
3540 ex));
3541 }
3542 globalOptimization(currentWindow);
3543 double finalEnergy = Double.NaN;
3544 try {
3545 finalEnergy = currentEnergy(currentWindow);
3546 } catch (ArithmeticException ex) {
3547 logger.severe(
3548 format(" Exception %s in calculating final energy of a window; FFX shutting down",
3549 ex));
3550 }
3551 if (startingEnergy <= finalEnergy) {
3552 logger.info(
3553 "Optimization did not yield a better energy. Reverting to original coordinates.");
3554 ResidueState.revertAllCoordinates(currentWindow, coordinates);
3555 } else {
3556
3557 int i = 0;
3558 for (Residue residue : currentWindow) {
3559 int index = residueList.indexOf(residue);
3560 optimum[index] = optimumSubset[i++];
3561 }
3562 }
3563 } else {
3564 globalOptimization(currentWindow);
3565
3566 int i = 0;
3567 for (Residue residue : currentWindow) {
3568 int index = residueList.indexOf(residue);
3569 optimum[index] = optimumSubset[i++];
3570 }
3571 }
3572
3573 if (!incrementTruncated) {
3574 if (windowStart + (windowSize - 1) + increment > nOptimize - 1) {
3575 increment = nOptimize - windowStart - windowSize;
3576 if (increment == 0) {
3577 break;
3578 }
3579 logger.warning(" Increment truncated in order to optimize entire residue range.");
3580 incrementTruncated = true;
3581 }
3582 }
3583
3584 if (rank0 && printFiles) {
3585 File file = molecularAssembly.getFile();
3586 if (firstWindowSaved) {
3587 file.delete();
3588 }
3589
3590 if (windowStart + windowSize == nOptimize) {
3591 continue;
3592 }
3593 PDBFilter windowFilter = new PDBFilter(file, molecularAssembly, null, null);
3594
3595
3596 try {
3597 windowFilter.writeFile(file, false);
3598 if (firstResidue != lastResidue) {
3599 logger.info(
3600 format(" File with residues %s ... %s in window written to.", firstResidue,
3601 lastResidue));
3602 } else {
3603 logger.info(format(" File with residue %s in window written to.", firstResidue));
3604 }
3605 } catch (Exception e) {
3606 logger.warning(format("Exception writing to file: %s", file.getName()));
3607 }
3608 firstWindowSaved = true;
3609 }
3610 long currentTime = System.nanoTime();
3611 windowTime += currentTime;
3612 logIfRank0(format(" Time elapsed for this iteration: %11.3f sec", windowTime * 1.0E-9));
3613 logIfRank0(
3614 format(" Overall time elapsed: %11.3f sec", (currentTime + beginTime) * 1.0E-9));
3615 if (genZ) {
3616 int[] currentRotamers = new int[optimumSubset.length];
3617 usingBoxOptimization = true;
3618 Residue[] residueSubsetArray = currentWindow.toArray(new Residue[currentWindow.size()]);
3619 getFractions(residueSubsetArray, 0, currentRotamers, true);
3620 Residue[] titrationResidueArray = new Residue[]{residueList.get(windowCenter)};
3621 getProtonationPopulations(titrationResidueArray);
3622 }
3623 if (windowCenter > -1) {
3624 break;
3625 }
3626 }
3627
3628
3629 break;
3630 default:
3631
3632 break;
3633 }
3634 return 0.0;
3635 }
3636
3637
3638
3639
3640
3641
3642 private void sortResidues(List<Residue> residues) {
3643 Comparator<Residue> comparator = Comparator.comparing(Residue::getChainID)
3644 .thenComparingInt(Residue::getResidueNumber);
3645 residues.sort(comparator);
3646 }
3647
3648
3649
3650
3651
3652
3653
3654
3655 private void applyEliminationCriteria(Residue[] residues, boolean getEnergies, boolean verbose) {
3656 Level prevLevel = logger.getLevel();
3657 if (!verbose) {
3658 logger.setLevel(Level.WARNING);
3659 }
3660 if (getEnergies) {
3661 applyEliminationCriteria(residues);
3662 } else {
3663 int i = 0;
3664 boolean pairEliminated;
3665 do {
3666 pairEliminated = false;
3667 if (useGoldstein) {
3668 if (selfEliminationOn) {
3669 i++;
3670 logIfRank0(format("\n Iteration %d: Applying Single Goldstein DEE conditions ", i));
3671
3672 while (goldsteinDriver(residues)) {
3673 i++;
3674 logIfRank0(this.toString());
3675 logIfRank0(
3676 format("\n Iteration %d: Applying Single Rotamer Goldstein DEE conditions ", i));
3677 }
3678 }
3679 if (pairEliminationOn) {
3680 i++;
3681 logIfRank0(
3682 format("\n Iteration %d: Applying Rotamer Pair Goldstein DEE conditions ", i));
3683
3684 pairEliminated = false;
3685 while (goldsteinPairDriver(residues)) {
3686 pairEliminated = true;
3687 i++;
3688 logIfRank0(this.toString());
3689 logIfRank0(
3690 format("\n Iteration %d: Applying Rotamer Pair Goldstein DEE conditions ", i));
3691 }
3692 }
3693 } else {
3694 if (selfEliminationOn) {
3695 i++;
3696 logIfRank0(format("\n Iteration %d: Applying Single DEE conditions ", i));
3697
3698 while (deeRotamerElimination(residues)) {
3699 i++;
3700 logIfRank0(toString());
3701 logIfRank0(format("\n Iteration %d: Applying Single Rotamer DEE conditions ", i));
3702 }
3703 }
3704 if (pairEliminationOn) {
3705 i++;
3706 logIfRank0(format("\n Iteration %d: Applying Rotamer Pair DEE conditions ", i));
3707
3708 pairEliminated = false;
3709 while (deeRotamerPairElimination(residues)) {
3710 pairEliminated = true;
3711 i++;
3712 logIfRank0(toString());
3713 logIfRank0(format("\n Iteration %d: Applying Rotamer Pair DEE conditions ", i));
3714 }
3715 }
3716 }
3717 eR.validateDEE(residues);
3718 logIfRank0(toString());
3719 } while (pairEliminated);
3720 logIfRank0(" Self-consistent DEE rotamer elimination achieved.\n");
3721 }
3722 if (!verbose) {
3723 logger.setLevel(prevLevel);
3724 }
3725 }
3726
3727 private void applyEliminationCriteria(Residue[] residues) {
3728 if (verboseEnergies) {
3729 try {
3730 logIfRank0(format("\n Beginning Energy %s", formatEnergy(currentEnergy(residues))));
3731 } catch (ArithmeticException ex) {
3732 logger.severe(format(" Exception %s in calculating beginning energy; FFX shutting down.", ex));
3733 }
3734 }
3735
3736 rotamerEnergies(residues);
3737
3738 if (testing) {
3739 int nres = residues.length;
3740 eR.onlyPrunedSingles = new boolean[nres][];
3741 eR.onlyPrunedPairs = new boolean[nres][][][];
3742 for (int i = 0; i < nres; i++) {
3743 Residue residuei = residues[i];
3744 Rotamer[] rotamersi = residuei.getRotamers();
3745 int lenri = rotamersi.length;
3746 eR.onlyPrunedSingles[i] = new boolean[lenri];
3747 eR.onlyPrunedSingles[i] = copyOf(eR.eliminatedSingles[i], eR.eliminatedSingles[i].length);
3748 eR.onlyPrunedPairs[i] = new boolean[lenri][][];
3749
3750 for (int ri = 0; ri < lenri; ri++) {
3751 eR.onlyPrunedPairs[i][ri] = new boolean[nres][];
3752 for (int j = i + 1; j < nres; j++) {
3753 Residue residuej = residues[j];
3754 Rotamer[] rotamersj = residuej.getRotamers();
3755 int lenrj = rotamersj.length;
3756 eR.onlyPrunedPairs[i][ri][j] = new boolean[lenrj];
3757 eR.onlyPrunedPairs[i][ri][j] = copyOf(eR.eliminatedPairs[i][ri][j],
3758 eR.eliminatedPairs[i][ri][j].length);
3759 }
3760 }
3761 }
3762 }
3763
3764 if (testSelfEnergyEliminations) {
3765 testSelfEnergyElimination(residues);
3766 } else if (testPairEnergyEliminations > -1) {
3767 testPairEnergyElimination(residues, testPairEnergyEliminations);
3768 } else if (testTripleEnergyEliminations1 > -1 && testTripleEnergyEliminations2 > -1) {
3769 testTripleEnergyElimination(residues, testTripleEnergyEliminations1,
3770 testTripleEnergyEliminations2);
3771 }
3772
3773 if (pruneClashes) {
3774 eR.validateDEE(residues);
3775 }
3776
3777 int i = 0;
3778 boolean pairEliminated;
3779 do {
3780 pairEliminated = false;
3781 if (useGoldstein) {
3782 if (selfEliminationOn) {
3783 i++;
3784 logIfRank0(format("\n Iteration %d: Applying Single Goldstein DEE conditions ", i));
3785
3786 while (goldsteinDriver(residues)) {
3787 i++;
3788 logIfRank0(this.toString());
3789 logIfRank0(
3790 format("\n Iteration %d: Applying Single Rotamer Goldstein DEE conditions ", i));
3791 }
3792 }
3793 if (pairEliminationOn) {
3794 i++;
3795 logIfRank0(format("\n Iteration %d: Applying Rotamer Pair Goldstein DEE conditions ", i));
3796
3797 while (goldsteinPairDriver(residues)) {
3798 pairEliminated = true;
3799 i++;
3800 logIfRank0(this.toString());
3801 logIfRank0(
3802 format("\n Iteration %d: Applying Rotamer Pair Goldstein DEE conditions ", i));
3803 }
3804 }
3805 } else {
3806 if (selfEliminationOn) {
3807 i++;
3808 logIfRank0(format("\n Iteration %d: Applying Single DEE conditions ", i));
3809
3810 while (deeRotamerElimination(residues)) {
3811 i++;
3812 logIfRank0(toString());
3813 logIfRank0(format("\n Iteration %d: Applying Single Rotamer DEE conditions ", i));
3814 }
3815 }
3816 if (pairEliminationOn) {
3817 i++;
3818 logIfRank0(format("\n Iteration %d: Applying Rotamer Pair DEE conditions ", i));
3819
3820 while (deeRotamerPairElimination(residues)) {
3821 pairEliminated = true;
3822 i++;
3823 logIfRank0(toString());
3824 logIfRank0(format("\n Iteration %d: Applying Rotamer Pair DEE conditions ", i));
3825 }
3826 }
3827 }
3828 eR.validateDEE(residues);
3829 logIfRank0(toString());
3830 } while (pairEliminated);
3831
3832 logIfRank0(" Self-consistent DEE rotamer elimination achieved.\n");
3833 }
3834
3835
3836
3837
3838
3839
3840
3841 private double currentEnergy(List<Residue> resList) throws ArithmeticException {
3842 List<Rotamer> rots = resList.stream().filter(Objects::nonNull).map(Residue::getRotamer)
3843 .collect(Collectors.toList());
3844 File energyDir = dirSupplier.apply(resList, rots);
3845 return eFunction.applyAsDouble(energyDir);
3846 }
3847
3848
3849
3850
3851
3852
3853
3854 private double currentPE(File dir) {
3855 if (x == null) {
3856 int nVar = potential.getNumberOfVariables();
3857 x = new double[nVar];
3858 }
3859 potential.getCoordinates(x);
3860 return potential.energy(x);
3861 }
3862
3863
3864
3865
3866
3867
3868 private void generateResidueNeighbors(Residue[] residues) {
3869 int nRes = residues.length;
3870 resNeighbors = new int[nRes][];
3871 bidiResNeighbors = new int[nRes][];
3872 for (int i = 0; i < nRes; i++) {
3873 Set<Integer> nearby = new HashSet<>();
3874 Residue resi = residues[i];
3875 Rotamer[] rotsi = resi.getRotamers();
3876 int lenri = rotsi.length;
3877 int indexI = allResiduesList.indexOf(resi);
3878
3879 for (int j = 0; j < nRes; j++) {
3880 if (i == j) {
3881 continue;
3882 }
3883 Residue resj = residues[j];
3884 Rotamer[] rotsj = resj.getRotamers();
3885 int lenrj = rotsj.length;
3886 int indexJ = allResiduesList.indexOf(resj);
3887
3888 boolean foundClose = false;
3889 for (int ri = 0; ri < lenri; ri++) {
3890 for (int rj = 0; rj < lenrj; rj++) {
3891 if (!dM.checkPairDistThreshold(indexI, ri, indexJ, rj)) {
3892 foundClose = true;
3893 break;
3894 }
3895 }
3896 if (foundClose) {
3897 break;
3898 }
3899 }
3900 if (foundClose) {
3901 nearby.add(j);
3902 }
3903 }
3904
3905
3906 int[] nI = nearby.stream().mapToInt(Integer::intValue).toArray();
3907 bidiResNeighbors[i] = nI;
3908
3909
3910 final int fi = i;
3911 nI = nearby.stream().mapToInt(Integer::intValue).filter(j -> j > fi).toArray();
3912 resNeighbors[i] = nI;
3913 }
3914 }
3915
3916 private void setUpRestart() {
3917 File restartFile;
3918 if (loadEnergyRestart) {
3919 restartFile = energyRestartFile;
3920 } else {
3921 File file = molecularAssembly.getFile();
3922 String filename = FilenameUtils.removeExtension(file.getAbsolutePath());
3923 Path restartPath = Paths.get(filename + ".restart");
3924 restartFile = restartPath.toFile();
3925 energyRestartFile = restartFile;
3926 }
3927 try {
3928 energyWriter = new BufferedWriter(new FileWriter(restartFile, true));
3929 } catch (IOException ex) {
3930 logger.log(Level.SEVERE, "Couldn't open energy restart file.", ex);
3931 }
3932 logger.info(format("\n Energy restart file: %s", restartFile.getName()));
3933 }
3934
3935
3936
3937
3938
3939
3940
3941
3942 private double rotamerEnergies(Residue[] residues) {
3943
3944 if (residues == null) {
3945 logger.warning(" Attempt to compute rotamer energies for an empty array of residues.");
3946 return 0.0;
3947 }
3948
3949 int nResidues = residues.length;
3950 Atom[] atoms = molecularAssembly.getAtomArray();
3951 generateResidueNeighbors(residues);
3952
3953 eR = new EliminatedRotamers(this, dM, allResiduesList, maxRotCheckDepth, clashThreshold,
3954 pairClashThreshold, multiResClashThreshold, nucleicPruningFactor, nucleicPairsPruningFactor,
3955 multiResPairClashAddn, pruneClashes, prunePairClashes, print, residues);
3956
3957 if (decomposeOriginal) {
3958 assert library.getUsingOrigCoordsRotamer();
3959 for (int i = 0; i < nResidues; i++) {
3960 Residue resi = residues[i];
3961 Rotamer[] rotsi = resi.getRotamers();
3962 int lenri = rotsi.length;
3963
3964
3965 for (int ri = 1; ri < lenri; ri++) {
3966 eR.eliminateRotamer(residues, i, ri, false);
3967 }
3968 }
3969 }
3970
3971
3972 for (Atom atom : atoms) {
3973 atom.setUse(true);
3974 }
3975
3976 eE = new EnergyExpansion(this, dM, eR, molecularAssembly, potential, algorithmListener,
3977 allResiduesList, resNeighbors, threeBodyTerm, decomposeOriginal, usingBoxOptimization,
3978 verbose, pruneClashes, prunePairClashes, rank0);
3979
3980
3981 eR.setEnergyExpansion(eE);
3982
3983 int loaded = 0;
3984 if (loadEnergyRestart) {
3985 if (usingBoxOptimization) {
3986 loaded = eE.loadEnergyRestart(energyRestartFile, residues, boxOpt.boxLoadIndex, boxOpt.boxLoadCellIndices);
3987 } else {
3988 loaded = eE.loadEnergyRestart(energyRestartFile, residues);
3989 }
3990 }
3991
3992 long energyStartTime = System.nanoTime();
3993 WorkerTeam energyWorkerTeam = new WorkerTeam(world);
3994
3995 try {
3996 if (loaded < 1) {
3997 eE.allocateSelfJobMap(residues, nResidues, false);
3998 }
3999
4000 SelfEnergyRegion selfEnergyRegion = new SelfEnergyRegion(this, eE, eR, residues, energyWriter,
4001 world, numProc, pruneClashes, rank0, rank, verbose, writeEnergyRestart, printFiles);
4002 energyWorkerTeam.execute(selfEnergyRegion);
4003 long singlesTime = System.nanoTime() - energyStartTime;
4004 logIfRank0(format(" Time for single energies: %12.4g", (singlesTime * 1.0E-9)));
4005 if (logger.isLoggable(Level.FINE)) {
4006 Resources.logResources();
4007 }
4008
4009 if (loaded < 2) {
4010 eE.allocate2BodyJobMap(residues, nResidues, false);
4011 }
4012
4013 TwoBodyEnergyRegion twoBodyEnergyRegion = new TwoBodyEnergyRegion(this, dM, eE, eR, residues,
4014 allResiduesList, energyWriter, world, numProc, prunePairClashes, superpositionThreshold,
4015 rank0, rank, verbose, writeEnergyRestart, printFiles);
4016 energyWorkerTeam.execute(twoBodyEnergyRegion);
4017 long pairsTime = System.nanoTime() - (singlesTime + energyStartTime);
4018
4019 long triplesTime = 0;
4020 long quadsTime = 0;
4021 logIfRank0(format(" Time for 2-body energies: %12.4g", (pairsTime * 1.0E-9)));
4022 if (logger.isLoggable(Level.FINE)) {
4023 Resources.logResources();
4024 }
4025
4026 if (threeBodyTerm) {
4027 if (loaded < 3) {
4028 eE.allocate3BodyJobMap(residues, nResidues, false);
4029 }
4030 ThreeBodyEnergyRegion threeBodyEnergyRegion = new ThreeBodyEnergyRegion(this, dM, eE, eR,
4031 residues, allResiduesList, energyWriter, world, numProc, superpositionThreshold, rank0,
4032 rank, verbose, writeEnergyRestart, printFiles);
4033 energyWorkerTeam.execute(threeBodyEnergyRegion);
4034 triplesTime = System.nanoTime() - (pairsTime + singlesTime + energyStartTime);
4035 logIfRank0(format(" Time for 3-Body energies: %12.4g", (triplesTime * 1.0E-9)));
4036 if (logger.isLoggable(Level.FINE)) {
4037 Resources.logResources();
4038 }
4039 }
4040
4041 if (compute4BodyEnergy) {
4042 eE.allocate4BodyJobMap(residues, nResidues);
4043
4044 FourBodyEnergyRegion fourBodyEnergyRegion = new FourBodyEnergyRegion(this, dM, eE, eR,
4045 residues, allResiduesList, superpositionThreshold);
4046 energyWorkerTeam.execute(fourBodyEnergyRegion);
4047 quadsTime = System.nanoTime() - (triplesTime + pairsTime + singlesTime + energyStartTime);
4048 logIfRank0(format(" Time for 4-Body energies: %12.4g", quadsTime * 1.0E-9));
4049 }
4050
4051 long allTime = singlesTime + pairsTime + triplesTime + quadsTime;
4052 logIfRank0(format(" Time for all energies: %12.4g", allTime * 1.0E-9));
4053 } catch (Exception ex) {
4054 String message = " Exception computing rotamer energies in parallel.";
4055 logger.log(Level.SEVERE, message, ex);
4056 }
4057
4058
4059 for (Atom atom : atoms) {
4060 atom.setUse(true);
4061 }
4062
4063 if (verboseEnergies && rank0) {
4064 try {
4065 double defaultEnergy = currentEnergy(residues);
4066 logger.info(format(" Energy of the system with rotamers in their default conformation: %s",
4067 formatEnergy(defaultEnergy)));
4068 } catch (ArithmeticException ex) {
4069 logger.severe(format(" Exception %s in calculating default energy; FFX shutting down", ex));
4070 }
4071 }
4072 return eE.getBackboneEnergy();
4073 }
4074
4075
4076
4077
4078
4079
4080 private void applyDefaultRotamer(Residue residue) {
4081 RotamerLibrary.applyRotamer(residue, residue.getRotamers()[0]);
4082 }
4083
4084
4085
4086
4087
4088
4089
4090 private boolean deeRotamerElimination(Residue[] residues) {
4091 int nres = residues.length;
4092
4093 boolean eliminated = false;
4094
4095 double[] minMax = new double[2];
4096 double[] minEnergySingles = null;
4097 double[] maxEnergySingles = null;
4098 for (int i = 0; i < nres; i++) {
4099 Residue residuei = residues[i];
4100 Rotamer[] rotamersi = residuei.getRotamers();
4101 int lenri = rotamersi.length;
4102 if (minEnergySingles == null || minEnergySingles.length < lenri) {
4103 minEnergySingles = new double[lenri];
4104 maxEnergySingles = new double[lenri];
4105 }
4106
4107 for (int ri = 0; ri < lenri; ri++) {
4108
4109 if (eR.check(i, ri)) {
4110 continue;
4111 }
4112
4113 minEnergySingles[ri] = eE.getSelf(i, ri);
4114 maxEnergySingles[ri] = minEnergySingles[ri];
4115 for (int j = 0; j < nres; j++) {
4116 if (j == i) {
4117 continue;
4118 }
4119 if (eE.minMaxPairEnergy(residues, minMax, i, ri, j)) {
4120 if (Double.isFinite(minMax[0]) && Double.isFinite(minEnergySingles[ri])) {
4121 minEnergySingles[ri] += minMax[0];
4122 } else {
4123
4124
4125
4126 minEnergySingles[ri] = Double.NaN;
4127 }
4128 if (Double.isFinite(minMax[0]) && Double.isFinite(maxEnergySingles[ri])) {
4129 maxEnergySingles[ri] += minMax[1];
4130 } else {
4131
4132
4133 maxEnergySingles[ri] = Double.NaN;
4134 }
4135 } else {
4136 Residue residuej = residues[j];
4137 logger.info(
4138 format(" Inconsistent Pair: %8s %2d, %8s.", residuei.toFormattedString(false, true),
4139 ri, residuej.toFormattedString(false, true)));
4140
4141 }
4142 }
4143 }
4144
4145
4146
4147
4148
4149 double eliminationEnergy = Double.MAX_VALUE;
4150 int eliminatingRotamer = 0;
4151 for (int ri = 0; ri < lenri; ri++) {
4152 if (eR.check(i, ri)) {
4153 continue;
4154 }
4155 if (Double.isFinite(maxEnergySingles[ri]) && maxEnergySingles[ri] < eliminationEnergy) {
4156 eliminationEnergy = maxEnergySingles[ri];
4157 eliminatingRotamer = ri;
4158 }
4159 }
4160
4161 if (eliminationEnergy == Double.MAX_VALUE) {
4162
4163
4164 logIfRank0(" Could not eliminate any i,ri because eliminationEnergy was never set!",
4165 Level.FINE);
4166 } else {
4167
4168
4169
4170
4171 for (int ri = 0; ri < lenri; ri++) {
4172 if (eR.check(i, ri)) {
4173 continue;
4174 }
4175
4176
4177 if (!Double.isFinite(minEnergySingles[ri])) {
4178 if (eR.eliminateRotamer(residues, i, ri, print)) {
4179 logIfRank0(format(" Rotamer elimination of (%8s,%2d) that always clashes.",
4180 residuei.toFormattedString(false, true), ri));
4181 eliminated = true;
4182 }
4183 }
4184
4185
4186 if (minEnergySingles[ri] > eliminationEnergy + ensembleBuffer) {
4187 if (eR.eliminateRotamer(residues, i, ri, print)) {
4188 logIfRank0(format(" Rotamer elimination of (%8s,%2d) by (%8s,%2d): %12.4f > %6.4f.",
4189 residuei.toFormattedString(false, true), ri,
4190 residuei.toFormattedString(false, true), eliminatingRotamer, minEnergySingles[ri],
4191 eliminationEnergy + ensembleBuffer));
4192 eliminated = true;
4193 }
4194 }
4195 }
4196 }
4197 }
4198 return eliminated;
4199 }
4200
4201
4202
4203
4204
4205
4206
4207
4208 private boolean deeRotamerPairElimination(Residue[] residues) {
4209 int nres = residues.length;
4210 boolean eliminated = false;
4211
4212 for (int i = 0; i < (nres - 1); i++) {
4213 Residue residuei = residues[i];
4214 Rotamer[] rotamersi = residuei.getRotamers();
4215 int lenri = rotamersi.length;
4216
4217
4218 double[][] minPairEnergies = new double[lenri][];
4219 double[][] maxPairEnergies = new double[lenri][];
4220
4221 for (int j = i + 1; j < nres; j++) {
4222 Residue residuej = residues[j];
4223 Rotamer[] rotamersj = residuej.getRotamers();
4224 int lenrj = rotamersj.length;
4225
4226 for (int ri = 0; ri < lenri; ri++) {
4227 if (eR.check(i, ri)) {
4228 continue;
4229 }
4230 minPairEnergies[ri] = new double[lenrj];
4231 maxPairEnergies[ri] = new double[lenrj];
4232
4233 for (int rj = 0; rj < lenrj; rj++) {
4234 if (eR.check(j, rj) || eR.check(i, ri, j, rj)) {
4235 continue;
4236 }
4237 minPairEnergies[ri][rj] =
4238 eE.getSelf(i, ri) + eE.getSelf(j, rj) + eE.get2Body(i, ri, j, rj);
4239 maxPairEnergies[ri][rj] = minPairEnergies[ri][rj];
4240
4241
4242 double[] minMax = new double[2];
4243
4244
4245 if (eE.minMaxE2(residues, minMax, i, ri, j, rj)) {
4246 if (Double.isFinite(minPairEnergies[ri][rj]) && Double.isFinite(minMax[0])) {
4247 minPairEnergies[ri][rj] += minMax[0];
4248 } else {
4249 logger.severe(
4250 format(" An ri-rj pair %s-%d %s-%d with NaN minimum was caught incorrectly!",
4251 residuei.toFormattedString(false, true), ri,
4252 residuej.toFormattedString(false, true), rj));
4253 }
4254 if (Double.isFinite(maxPairEnergies[ri][rj]) && Double.isFinite(minMax[1])) {
4255 maxPairEnergies[ri][rj] += minMax[1];
4256 } else {
4257
4258 maxPairEnergies[ri][rj] = Double.NaN;
4259 }
4260 } else {
4261
4262 minPairEnergies[ri][rj] = Double.NaN;
4263 logger.info(format(" Eliminating pair %s-%d %s-%d that always clashes.",
4264 residuei.toFormattedString(false, true), ri,
4265 residuej.toFormattedString(false, true), rj));
4266 eR.eliminateRotamerPair(residues, i, ri, j, rj, print);
4267 eliminated = true;
4268 }
4269 }
4270 }
4271
4272 double pairEliminationEnergy = Double.MAX_VALUE;
4273 for (int ri = 0; ri < lenri; ri++) {
4274 if (eR.check(i, ri)) {
4275 continue;
4276 }
4277 for (int rj = 0; rj < lenrj; rj++) {
4278 if (eR.check(j, rj) || eR.check(i, ri, j, rj)) {
4279 continue;
4280 }
4281 if (Double.isFinite(maxPairEnergies[ri][rj])
4282 && maxPairEnergies[ri][rj] < pairEliminationEnergy) {
4283 pairEliminationEnergy = maxPairEnergies[ri][rj];
4284 }
4285 }
4286 }
4287
4288 if (pairEliminationEnergy == Double.MAX_VALUE) {
4289 logIfRank0(format(
4290 " All rotamer pairs for residues %s and %s have possible conflicts; cannot perform any eliminations!",
4291 residuei.toFormattedString(false, true), residuej), Level.FINE);
4292 } else {
4293 double comparisonEnergy = pairEliminationEnergy + ensembleBuffer;
4294 for (int ri = 0; ri < lenri; ri++) {
4295 if (eR.check(i, ri)) {
4296 continue;
4297 }
4298 for (int rj = 0; rj < lenrj; rj++) {
4299 if (eR.check(j, rj) || eR.check(i, ri, j, rj)) {
4300 continue;
4301 }
4302 if (minPairEnergies[ri][rj] > comparisonEnergy) {
4303 if (eR.eliminateRotamerPair(residues, i, ri, j, rj, print)) {
4304 eliminated = true;
4305 logIfRank0(format(" Eliminating rotamer pair: %s %d, %s %d (%s > %s + %6.6f)",
4306 residuei.toFormattedString(false, true), ri,
4307 residuej.toFormattedString(false, true), rj,
4308 formatEnergy(minPairEnergies[ri][rj]), formatEnergy(pairEliminationEnergy),
4309 ensembleBuffer), Level.INFO);
4310 } else {
4311
4312 logIfRank0(
4313 format(" Already eliminated rotamer pair! %s %d, %s %d (%s > %1s + %6.6f)",
4314 residuei.toFormattedString(false, true), ri,
4315 residuej.toFormattedString(false, true), rj,
4316 formatEnergy(minPairEnergies[ri][rj]), formatEnergy(pairEliminationEnergy),
4317 ensembleBuffer), Level.WARNING);
4318 }
4319 }
4320 }
4321 }
4322 }
4323
4324 if (eR.pairsToSingleElimination(residues, i, j)) {
4325 eliminated = true;
4326 }
4327 }
4328 }
4329
4330 return eliminated;
4331 }
4332
4333 private boolean goldsteinDriver(Residue[] residues) {
4334 int nres = residues.length;
4335
4336 boolean eliminated = false;
4337
4338 for (int i = 0; i < nres; i++) {
4339 Residue resi = residues[i];
4340 Rotamer[] roti = resi.getRotamers();
4341 int nri = roti.length;
4342
4343 for (int riA = 0; riA < nri; riA++) {
4344
4345 if (eR.check(i, riA)) {
4346 continue;
4347 }
4348 for (int riB = 0; riB < nri; riB++) {
4349
4350 if (riA == riB || eR.check(i, riB)) {
4351 continue;
4352 }
4353 if (goldsteinElimination(residues, i, riA, riB)) {
4354 eliminated = true;
4355 break;
4356 }
4357 }
4358 }
4359 }
4360 if (!eliminated) {
4361 logIfRank0(" No more single rotamers to eliminate.");
4362 }
4363 return eliminated;
4364 }
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375 private boolean goldsteinElimination(Residue[] residues, int i, int riA, int riB) {
4376 Residue resi = residues[i];
4377
4378
4379 double selfDiff = eE.getSelf(i, riA) - eE.getSelf(i, riB);
4380 double goldsteinEnergy = selfDiff;
4381
4382 double sumPairDiff = 0.0;
4383 double sumTripleDiff = 0.0;
4384
4385
4386 for (int j : bidiResNeighbors[i]) {
4387 Residue resj = residues[j];
4388 Rotamer[] rotj = resj.getRotamers();
4389 int nrj = rotj.length;
4390 double minForResJ = Double.MAX_VALUE;
4391 double minPairDiff = 0.0;
4392 double minTripleDiff = 0.0;
4393 int rjEvals = 0;
4394
4395
4396 for (int rj = 0; rj < nrj; rj++) {
4397 if (eR.check(j, rj)) {
4398 continue;
4399 }
4400
4401 if (eR.check(i, riA, j, rj)) {
4402 continue;
4403 }
4404 if (eR.check(i, riB, j, rj)) {
4405
4406
4407
4408
4409
4410 return false;
4411 }
4412
4413 double pairI = eE.get2Body(i, riA, j, rj);
4414 double pairJ = eE.get2Body(i, riB, j, rj);
4415 double pairDiff = pairI - pairJ;
4416
4417 rjEvals++;
4418
4419
4420 double tripleDiff = 0.0;
4421 if (threeBodyTerm) {
4422 IntStream kStream = IntStream.concat(Arrays.stream(bidiResNeighbors[i]),
4423 Arrays.stream(bidiResNeighbors[j]));
4424 int[] possKs = kStream.distinct().sorted().toArray();
4425 for (int k : possKs) {
4426 if (k == i || k == j) {
4427 continue;
4428 }
4429 Residue resk = residues[k];
4430 Rotamer[] rotk = resk.getRotamers();
4431 int nrk = rotk.length;
4432 int rkEvals = 0;
4433 double minForResK = Double.MAX_VALUE;
4434 for (int rk = 0; rk < nrk; rk++) {
4435
4436
4437
4438
4439 if (eR.check(k, rk) || eR.check(j, rj, k, rk) || eR.check(i, riA, k, rk)) {
4440
4441
4442 continue;
4443 }
4444
4445
4446
4447
4448 if (eR.check(i, riB, k, rk)) {
4449
4450 return false;
4451 }
4452
4453 rkEvals++;
4454 double e =
4455 eE.get3Body(residues, i, riA, j, rj, k, rk) - eE.get3Body(residues, i, riB, j, rj,
4456 k, rk);
4457 if (e < minForResK) {
4458 minForResK = e;
4459 }
4460 }
4461
4462 if (rkEvals == 0) {
4463 minForResK = 0.0;
4464 }
4465 tripleDiff += minForResK;
4466 }
4467 }
4468
4469 double sumOverK = pairDiff + tripleDiff;
4470 if (sumOverK < minForResJ) {
4471 minForResJ = sumOverK;
4472 minPairDiff = pairDiff;
4473 minTripleDiff = tripleDiff;
4474 }
4475 }
4476
4477 if (rjEvals == 0) {
4478 minForResJ = 0.0;
4479 minPairDiff = 0.0;
4480 minTripleDiff = 0.0;
4481 }
4482 sumPairDiff += minPairDiff;
4483 sumTripleDiff += minTripleDiff;
4484 goldsteinEnergy += minForResJ;
4485 }
4486
4487 if (goldsteinEnergy > ensembleBuffer) {
4488 if (eR.eliminateRotamer(residues, i, riA, print)) {
4489 logIfRank0(format(" Rotamer elimination of (%8s,%2d) by (%8s,%2d): %12.4f > %6.4f.",
4490 resi.toFormattedString(false, true), riA, resi.toFormattedString(false, true), riB,
4491 goldsteinEnergy, ensembleBuffer));
4492 logIfRank0(format(" Self: %12.4f, Pairs: %12.4f, Triples: %12.4f.", selfDiff, sumPairDiff,
4493 sumTripleDiff));
4494 return true;
4495 }
4496 }
4497 return false;
4498 }
4499
4500
4501
4502
4503
4504
4505
4506 private boolean goldsteinPairDriver(Residue[] residues) {
4507 int nRes = residues.length;
4508 boolean eliminated = false;
4509
4510
4511 for (int i = 0; i < nRes; i++) {
4512 Residue resi = residues[i];
4513 Rotamer[] rotsi = resi.getRotamers();
4514 int lenri = rotsi.length;
4515
4516 for (int riA = 0; riA < lenri; riA++) {
4517
4518 if (eR.check(i, riA)) {
4519 continue;
4520 }
4521
4522
4523 for (int j = 0; j < nRes; j++) {
4524
4525 if (i == j) {
4526 continue;
4527 }
4528 Residue resj = residues[j];
4529 Rotamer[] rotsj = resj.getRotamers();
4530 int lenrj = rotsj.length;
4531 for (int rjC = 0; rjC < lenrj; rjC++) {
4532
4533 if (eR.check(j, rjC) || eR.check(i, riA, j, rjC)) {
4534 continue;
4535 }
4536 boolean breakOut = false;
4537
4538
4539
4540 for (int riB = 0; riB < lenri; riB++) {
4541 if (breakOut) {
4542 break;
4543 }
4544 if (eR.check(i, riB)) {
4545 continue;
4546 }
4547 for (int rjD = 0; rjD < lenrj; rjD++) {
4548 if (breakOut) {
4549 break;
4550 }
4551
4552 if (eR.check(j, rjD) || eR.check(i, riB, j, rjD)) {
4553 continue;
4554 }
4555
4556 if (riA == riB && rjC == rjD) {
4557 continue;
4558 }
4559 if (goldsteinPairElimination(residues, i, riA, riB, j, rjC, rjD)) {
4560 breakOut = true;
4561 eliminated = true;
4562 }
4563 }
4564 }
4565 }
4566 if (eR.pairsToSingleElimination(residues, i, j)) {
4567 eliminated = true;
4568 }
4569 }
4570 }
4571 }
4572 return eliminated;
4573 }
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587 private boolean goldsteinPairElimination(Residue[] residues, int i, int riA, int riB, int j,
4588 int rjC, int rjD) {
4589
4590 List<Residue> missedResidues = null;
4591
4592
4593 double goldsteinEnergy =
4594 eE.getSelf(i, riA) + eE.getSelf(j, rjC) + eE.get2Body(i, riA, j, rjC) - eE.getSelf(i, riB)
4595 - eE.getSelf(j, rjD) - eE.get2Body(i, riB, j, rjD);
4596
4597 try {
4598 if (parallelTeam == null) {
4599 parallelTeam = new ParallelTeam();
4600 }
4601 if (goldsteinPairRegion == null) {
4602 goldsteinPairRegion = new GoldsteinPairRegion(parallelTeam.getThreadCount());
4603 }
4604 goldsteinPairRegion.init(residues, i, riA, riB, j, rjC, rjD, bidiResNeighbors, this);
4605 parallelTeam.execute(goldsteinPairRegion);
4606 goldsteinEnergy += goldsteinPairRegion.getSumOverK();
4607 missedResidues = goldsteinPairRegion.getMissedResidues();
4608 } catch (Exception e) {
4609 logger.log(Level.WARNING, " Exception in GoldsteinPairRegion.", e);
4610 }
4611 if (missedResidues != null && !missedResidues.isEmpty()) {
4612 logIfRank0(format(
4613 " Skipping energy comparison due to a missed residue: i %d riA %d riB %d j %d rjC %d rjD %d",
4614 i, riA, riB, j, rjC, rjD), Level.FINE);
4615 return false;
4616 }
4617
4618 if (goldsteinEnergy > ensembleBuffer) {
4619 if (missedResidues.isEmpty()) {
4620 if (eR.eliminateRotamerPair(residues, i, riA, j, rjC, print)) {
4621 logIfRank0(format(
4622 " Pair elimination of [(%8s,%2d),(%8s,%2d)] by [(%8s,%2d),(%8s,%2d)]: %12.4f > %6.4f",
4623 residues[i].toFormattedString(false, true), riA,
4624 residues[j].toFormattedString(false, true), rjC,
4625 residues[i].toFormattedString(false, true), riB,
4626 residues[j].toFormattedString(false, true), rjD, goldsteinEnergy, ensembleBuffer));
4627 return true;
4628 }
4629 } else {
4630 logIfRank0(format(
4631 " No Pair elimination of [(%8s,%2d),(%8s,%2d)] by [(%8s,%2d),(%8s,%2d)]: %12.4f > %6.4f",
4632 residues[i].toFormattedString(false, true), riA,
4633 residues[j].toFormattedString(false, true), rjC,
4634 residues[i].toFormattedString(false, true), riB,
4635 residues[j].toFormattedString(false, true), rjD, goldsteinEnergy, ensembleBuffer));
4636 StringBuilder sb = new StringBuilder();
4637 for (Residue residueM : missedResidues) {
4638 sb.append(residueM);
4639 }
4640 logIfRank0(format(" due to %s.", sb));
4641 }
4642 }
4643 return false;
4644 }
4645
4646
4647
4648
4649
4650
4651 void setTestOverallOpt(boolean testing) {
4652 this.testing = testing;
4653 distanceMethod = DistanceMethod.ROTAMER;
4654 setTwoBodyCutoff(Double.MAX_VALUE);
4655 setThreeBodyCutoff(9.0);
4656 }
4657
4658
4659
4660
4661
4662
4663
4664
4665 void setTestSelfEnergyEliminations(boolean testSelfEnergyEliminations) {
4666 this.testing = true;
4667 this.testSelfEnergyEliminations = testSelfEnergyEliminations;
4668 testPairEnergyEliminations = -1;
4669 testTripleEnergyEliminations1 = -1;
4670 testTripleEnergyEliminations2 = -1;
4671 distanceMethod = DistanceMethod.ROTAMER;
4672 setTwoBodyCutoff(Double.MAX_VALUE);
4673 setThreeBodyCutoff(9.0);
4674 }
4675
4676
4677
4678
4679
4680
4681
4682
4683 void setTestPairEnergyEliminations(int testPairEnergyEliminations) {
4684 this.testing = true;
4685 this.testPairEnergyEliminations = testPairEnergyEliminations;
4686 testSelfEnergyEliminations = false;
4687 testTripleEnergyEliminations1 = -1;
4688 testTripleEnergyEliminations2 = -1;
4689 distanceMethod = DistanceMethod.ROTAMER;
4690 setTwoBodyCutoff(Double.MAX_VALUE);
4691 setThreeBodyCutoff(9.0);
4692 }
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703 void setTestTripleEnergyEliminations(int testTripleEnergyEliminations1,
4704 int testTripleEnergyEliminations2) {
4705 this.testing = true;
4706 this.testTripleEnergyEliminations1 = testTripleEnergyEliminations1;
4707 this.testTripleEnergyEliminations2 = testTripleEnergyEliminations2;
4708 testSelfEnergyEliminations = false;
4709 testPairEnergyEliminations = -1;
4710 distanceMethod = DistanceMethod.ROTAMER;
4711 setTwoBodyCutoff(Double.MAX_VALUE);
4712 setThreeBodyCutoff(9.0);
4713 }
4714
4715
4716
4717
4718
4719
4720 private void testSelfEnergyElimination(Residue[] residues) {
4721 int nRes = residues.length;
4722 for (int i = 0; i < nRes; i++) {
4723 Residue resI = residues[i];
4724 Rotamer[] rotI = resI.getRotamers();
4725 int nI = rotI.length;
4726 for (int ri = 0; ri < nI; ri++) {
4727 for (int j = i + 1; j < nRes; j++) {
4728 Residue resJ = residues[j];
4729 Rotamer[] rotJ = resJ.getRotamers();
4730 int nJ = rotJ.length;
4731 for (int rj = 0; rj < nJ; rj++) {
4732 try {
4733 eE.set2Body(i, ri, j, rj, 0, true);
4734 } catch (Exception e) {
4735
4736 }
4737 if (threeBodyTerm) {
4738 for (int k = j + 1; k < nRes; k++) {
4739 Residue resK = residues[k];
4740 Rotamer[] rotK = resK.getRotamers();
4741 int nK = rotK.length;
4742 for (int rk = 0; rk < nK; rk++) {
4743 try {
4744 eE.set3Body(residues, i, ri, j, rj, k, rk, 0, true);
4745 } catch (Exception e) {
4746
4747 }
4748 }
4749 }
4750 }
4751 }
4752 }
4753 }
4754 }
4755 }
4756
4757
4758
4759
4760
4761
4762
4763 private void testPairEnergyElimination(Residue[] residues, int resID) {
4764 int nRes = residues.length;
4765
4766 if (resID >= nRes) {
4767 return;
4768 }
4769
4770 for (int i = 0; i < nRes; i++) {
4771 Residue resI = residues[i];
4772 Rotamer[] rotI = resI.getRotamers();
4773 int nI = rotI.length;
4774 for (int ri = 0; ri < nI; ri++) {
4775 try {
4776 eE.setSelf(i, ri, 0, true);
4777 } catch (Exception e) {
4778
4779 }
4780 for (int j = i + 1; j < nRes; j++) {
4781 Residue resJ = residues[j];
4782 Rotamer[] rotJ = resJ.getRotamers();
4783 int nJ = rotJ.length;
4784 for (int rj = 0; rj < nJ; rj++) {
4785 if (i != resID && j != resID) {
4786 try {
4787 eE.set2Body(i, ri, j, rj, 0, true);
4788 } catch (Exception e) {
4789
4790 }
4791 }
4792 if (threeBodyTerm) {
4793 for (int k = j + 1; k < nRes; k++) {
4794 Residue resK = residues[k];
4795 Rotamer[] rotK = resK.getRotamers();
4796 int nK = rotK.length;
4797 for (int rk = 0; rk < nK; rk++) {
4798 try {
4799 eE.set3Body(residues, i, ri, j, rj, k, rk, 0, true);
4800 } catch (Exception e) {
4801
4802 }
4803 }
4804 }
4805 }
4806 }
4807 }
4808 }
4809 }
4810 }
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820 private void testTripleEnergyElimination(Residue[] residues, int resID1, int resID2) {
4821 int nRes = residues.length;
4822
4823 if (resID1 >= nRes) {
4824 return;
4825 }
4826 if (resID2 >= nRes) {
4827 return;
4828 }
4829 if (resID1 == resID2) {
4830 return;
4831 }
4832
4833 for (int i = 0; i < nRes; i++) {
4834 Residue resI = residues[i];
4835 Rotamer[] rotI = resI.getRotamers();
4836 int nI = rotI.length;
4837 for (int ri = 0; ri < nI; ri++) {
4838 try {
4839 eE.setSelf(i, ri, 0, true);
4840 } catch (Exception e) {
4841
4842 }
4843 for (int j = i + 1; j < nRes; j++) {
4844 Residue resJ = residues[j];
4845 Rotamer[] rotJ = resJ.getRotamers();
4846 int nJ = rotJ.length;
4847 for (int rj = 0; rj < nJ; rj++) {
4848
4849
4850
4851
4852 try {
4853 eE.set2Body(i, ri, j, rj, 0, true);
4854 } catch (Exception e) {
4855
4856 }
4857 if (threeBodyTerm) {
4858 for (int k = j + 1; k < nRes; k++) {
4859 Residue resK = residues[k];
4860 Rotamer[] rotK = resK.getRotamers();
4861 int nK = rotK.length;
4862 for (int rk = 0; rk < nK; rk++) {
4863
4864 if (i != resID1 && j != resID1 && k != resID1) {
4865 try {
4866 eE.set3Body(residues, i, ri, j, rj, k, rk, 0, true);
4867 } catch (Exception e) {
4868
4869 }
4870 }
4871 if (i != resID2 && j != resID2 && k != resID2) {
4872 try {
4873 eE.set3Body(residues, i, ri, j, rj, k, rk, 0, true);
4874 } catch (Exception e) {
4875
4876 }
4877 }
4878 }
4879 }
4880 }
4881 }
4882 }
4883 }
4884 }
4885 }
4886
4887
4888
4889
4890 public enum Algorithm {
4891 INDEPENDENT,
4892 ALL,
4893 BRUTE_FORCE,
4894 WINDOW,
4895 BOX;
4896
4897 public static Algorithm getAlgorithm(int algorithm) {
4898 switch (algorithm) {
4899 case 1:
4900 return INDEPENDENT;
4901 case 2:
4902 return ALL;
4903 case 3:
4904 return BRUTE_FORCE;
4905 case 4:
4906 return WINDOW;
4907 case 5:
4908 return BOX;
4909 default:
4910 throw new IllegalArgumentException(
4911 format(" Algorithm choice was %d, not in range 1-5!", algorithm));
4912 }
4913 }
4914 }
4915
4916
4917
4918
4919 public enum DistanceMethod {
4920 ROTAMER, RESIDUE
4921 }
4922
4923 public enum Direction {
4924 FORWARD, BACKWARD
4925 }
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088 }