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