1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.potential.utils;
39
40 import static ffx.crystal.SymOp.Tr_0_0_0;
41 import static ffx.crystal.SymOp.ZERO_ROTATION;
42 import static ffx.crystal.SymOp.applyCartesianSymOp;
43 import static ffx.crystal.SymOp.asMatrixString;
44 import static ffx.crystal.SymOp.invertSymOp;
45 import static ffx.numerics.math.ScalarMath.mod;
46 import static ffx.numerics.math.DoubleMath.dist2;
47 import static ffx.numerics.math.MatrixMath.mat4Mat4;
48 import static ffx.potential.parsers.DistanceMatrixFilter.writeDistanceMatrixRow;
49 import static ffx.potential.utils.StructureMetrics.momentsOfInertia;
50 import static ffx.potential.utils.StructureMetrics.radiusOfGyration;
51 import static ffx.potential.utils.StructureMetrics.radiusOfGyrationComponents;
52 import static ffx.potential.utils.Superpose.applyRotation;
53 import static ffx.potential.utils.Superpose.applyTranslation;
54 import static ffx.potential.utils.Superpose.calculateRotation;
55 import static ffx.potential.utils.Superpose.calculateTranslation;
56 import static ffx.potential.utils.Superpose.rmsd;
57 import static ffx.potential.utils.Superpose.rotate;
58 import static ffx.potential.utils.Superpose.translate;
59 import static ffx.utilities.StringUtils.parseAtomRanges;
60 import static ffx.utilities.Resources.logResources;
61 import static ffx.utilities.StringUtils.writeAtomRanges;
62 import static java.lang.String.format;
63 import static java.lang.System.arraycopy;
64 import static java.util.Arrays.copyOf;
65 import static java.util.Arrays.fill;
66 import static java.util.Arrays.sort;
67 import static org.apache.commons.io.FilenameUtils.getName;
68 import static org.apache.commons.io.FilenameUtils.removeExtension;
69 import static org.apache.commons.io.FilenameUtils.getBaseName;
70 import static org.apache.commons.math3.util.FastMath.abs;
71 import static org.apache.commons.math3.util.FastMath.sqrt;
72 import static org.apache.commons.math3.util.FastMath.max;
73 import static org.apache.commons.math3.util.FastMath.ceil;
74 import static org.apache.commons.math3.util.FastMath.min;
75 import static org.apache.commons.math3.util.FastMath.cbrt;
76 import static org.apache.commons.math3.util.FastMath.PI;
77
78 import edu.rit.mp.DoubleBuf;
79 import edu.rit.pj.Comm;
80 import ffx.crystal.Crystal;
81 import ffx.crystal.ReplicatesCrystal;
82 import ffx.crystal.SymOp;
83 import ffx.numerics.math.RunningStatistics;
84 import ffx.potential.MolecularAssembly;
85 import ffx.potential.Utilities;
86 import ffx.potential.bonded.Atom;
87 import ffx.potential.bonded.Bond;
88 import ffx.potential.bonded.MSNode;
89 import ffx.potential.parameters.ForceField;
90 import ffx.potential.parsers.DistanceMatrixFilter;
91 import ffx.potential.parsers.PDBFilter;
92 import ffx.potential.parsers.SystemFilter;
93 import ffx.potential.parsers.XYZFilter;
94 import ffx.utilities.DoubleIndexPair;
95 import ffx.utilities.IndexIndexPair;
96
97 import java.io.File;
98 import java.io.FileWriter;
99 import java.io.BufferedWriter;
100 import java.io.FileReader;
101 import java.io.BufferedReader;
102 import java.io.InputStream;
103 import java.io.FileInputStream;
104 import java.io.OutputStream;
105 import java.io.FileOutputStream;
106 import java.util.ArrayList;
107 import java.util.Arrays;
108 import java.util.List;
109 import java.util.logging.Level;
110 import java.util.logging.Logger;
111 import java.util.stream.IntStream;
112
113 import org.apache.commons.configuration2.CompositeConfiguration;
114 import org.apache.commons.io.FileUtils;
115 import org.apache.commons.lang3.ArrayUtils;
116
117
118
119
120
121
122
123
124 public class ProgressiveAlignmentOfCrystals {
125
126
127
128
129 private static final Logger logger = Logger.getLogger(
130 ProgressiveAlignmentOfCrystals.class.getName());
131
132
133
134 private final SystemFilter baseFilter;
135
136
137
138 private final int baseSize;
139
140
141
142 private final String baseLabel;
143
144
145
146 private final SystemFilter targetFilter;
147
148
149
150 private final int targetSize;
151
152
153
154 private final String targetLabel;
155
156
157
158 private final int numWorkItems;
159
160
161
162
163 private final boolean isSymmetric;
164
165
166
167 private String rmsdLabel;
168
169
170
171 public final double[] distRow;
172
173
174
175
176 private int restartRow = 0;
177
178
179
180
181
182 private int restartColumn = 0;
183
184
185
186 private final Comm world;
187
188
189
190 private final boolean useMPI;
191
192
193
194 private final int numProc;
195
196
197
198 private final int rank;
199
200
201
202
203 private final double[][] distances;
204
205
206
207 private final DoubleBuf[] buffers;
208
209
210
211 private final double[] myDistances;
212
213
214
215 private final DoubleBuf myBuffer;
216
217
218
219 private double[] massN;
220
221
222
223 private double massSum = 0;
224
225
226
227 private int[] comparisonAtoms;
228
229
230
231 private DoubleIndexPair[] baseAUDist;
232
233
234
235 private DoubleIndexPair[] baseAUDist_2;
236
237
238
239 private DoubleIndexPair[] targetAUDist;
240
241
242
243 private DoubleIndexPair[] targetAUDist_2;
244
245
246
247 private final List<Integer> tempListIndices = new ArrayList<>();
248
249
250
251 private Integer[] uniqueBase;
252
253
254
255 private Integer[] uniqueTarget;
256
257
258
259
260 private double[] baseXYZoriginal;
261
262
263
264
265
266 private double[] baseXYZ;
267
268
269
270
271 private double[] targetXYZoriginal;
272
273
274
275
276
277 private double[] targetXYZ;
278
279
280
281
282 private double[][][] baseCoM;
283
284
285
286
287 private double[][] baseAUoriginal;
288
289
290
291 private double[] baseAU;
292
293
294
295 private double[] base3AUs;
296
297
298
299
300 private double[][] baseNAUs;
301
302
303
304 private double[][][] bestBaseNAUs;
305
306
307
308 private double[][] targetCoM;
309
310
311
312
313 private double[][] targetAUoriginal;
314
315
316
317 private double[] targetAU;
318
319
320
321 private double[] target3AUs;
322
323
324
325 private double[] targetNAUs;
326
327
328
329
330 private double[][][] bestTargetNAUs;
331
332
333
334 private DoubleIndexPair[] pairedAUs;
335
336
337
338 private int numberOfHits = 0;
339
340
341
342 private File[] fileCache;
343
344
345
346 private String[] nameCache;
347
348
349
350 private ArrayList<ArrayList<Bond>> bondCache;
351
352
353
354 private Atom[][] atomCache;
355
356
357
358
359 private ForceField[] forceFieldCache;
360
361
362
363 private Crystal[] crystalCache;
364
365
366
367
368 private final double[] gyrations = new double[2];
369
370
371
372 private double[][] bestBaseMandV = new double[3][4];
373
374
375
376 private double[][] bestTargetMandV = new double[3][4];
377
378
379
380 private double[] bestBaseRg = new double[3];
381
382
383
384 private double[] bestTargetRg = new double[3];
385
386
387
388 private SymOp baseSymOp;
389
390
391
392 private SymOp[][] bestBaseSymOp;
393
394
395
396 private SymOp targetSymOp;
397
398
399
400 private SymOp[][] bestTargetSymOp;
401
402
403
404 private ArrayList<SymOp> baseSymOps = new ArrayList<>();
405
406
407
408 private ArrayList<Integer> baseDistMap = new ArrayList<>();
409
410
411
412 private ArrayList<SymOp> targetSymOps = new ArrayList<>();
413
414
415
416 private ArrayList<Integer> targetDistMap = new ArrayList<>();
417
418
419
420 private double printSym;
421
422
423
424 private final StringBuilder stringBuilder = new StringBuilder();
425
426
427
428
429 private SymOp baseTransformSymOp;
430
431
432
433
434 private SymOp targetTransformSymOp;
435
436
437
438 private SymOp[][] bestBaseTransformSymOp;
439
440
441
442 private SymOp[][] bestTargetTransformSymOp;
443
444
445
446 private static final double MATCH_TOLERANCE = 1.0E-12;
447
448
449
450
451 private static final double SYM_TOLERANCE = 2.0E-8;
452
453
454
455
456
457
458
459
460 public ProgressiveAlignmentOfCrystals(SystemFilter baseFilter, SystemFilter targetFilter,
461 boolean isSymmetric) {
462
463 this.baseFilter = baseFilter;
464 this.targetFilter = targetFilter;
465
466 this.isSymmetric = isSymmetric;
467
468
469 baseSize = baseFilter.countNumModels();
470 baseLabel = getName(baseFilter.getFile().getAbsolutePath());
471 targetSize = targetFilter.countNumModels();
472 targetLabel = getName(targetFilter.getFile().getAbsolutePath());
473
474 assert !isSymmetric || (baseSize == targetSize);
475
476 if (logger.isLoggable(Level.FINER)) {
477 logger.finer(format("\n Conformations for %s: %d", baseLabel, baseSize));
478 logger.finer(format(" Conformations for %s: %d", targetLabel, targetSize));
479 }
480
481
482 distRow = new double[targetSize];
483
484 CompositeConfiguration properties = baseFilter.getActiveMolecularSystem().getProperties();
485 useMPI = properties.getBoolean("pj.use.mpi", true);
486
487 if (useMPI) {
488 world = Comm.world();
489
490 numProc = world.size();
491
492 rank = world.rank();
493 } else {
494 world = null;
495 numProc = 1;
496 rank = 0;
497 }
498
499
500
501
502 int extra = targetSize % numProc;
503 int paddedTargetSize = targetSize;
504 if (extra != 0) {
505 paddedTargetSize = targetSize - extra + numProc;
506 }
507 numWorkItems = paddedTargetSize / numProc;
508
509 if (numProc > 1) {
510 logger.info(format(" Number of MPI Processes: %d", numProc));
511 logger.info(format(" Rank of this MPI Process: %d", rank));
512 logger.info(format(" Work per process per row: %d", numWorkItems));
513 }
514
515
516 fill(distRow, -1.0);
517
518
519 distances = new double[numProc][numWorkItems];
520
521
522 for (int i = 0; i < numProc; i++) {
523 fill(distances[i], -2.0);
524 }
525
526
527 buffers = new DoubleBuf[numProc];
528 for (int i = 0; i < numProc; i++) {
529 buffers[i] = DoubleBuf.buffer(distances[i]);
530 }
531
532
533 myDistances = distances[rank];
534 myBuffer = buffers[rank];
535 }
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568 private double compare(final File file1, final String name1, final List<Bond> bondList1,
569 final Atom[] atoms1, final ForceField forceField1, final File file2, final String name2,
570 final List<Bond> bondList2, final Atom[] atoms2, final ForceField forceField2, final int z1,
571 final int z2, final int compareAtomsSize, final int nAU, final int baseSearchValue,
572 final int targetSearchValue, final double matchTol, final int compNum, final boolean strict,
573 final int saveClusters, final boolean machineLearning, final int linkage, final boolean inertia,
574 final boolean gyrationComponents) {
575
576
577 boolean useSave = saveClusters > 0;
578 boolean useSym = printSym >= 0.0;
579 int nCoords = compareAtomsSize * 3;
580
581 int nBaseMols = baseXYZ.length / nCoords;
582 int nTargetMols = targetXYZ.length / nCoords;
583
584 if (logger.isLoggable(Level.FINER)) {
585 stringBuilder.append(format("\n Base: %s Target: %s", name1, name2));
586 stringBuilder.append(format("""
587
588 Comparing %3d of %5d in base sphere.
589 Comparing %3d of %5d in target sphere.
590 """, nAU, nBaseMols, nAU, nTargetMols));
591 }
592
593 if (useSym) {
594
595 if (bestBaseSymOp == null || bestBaseSymOp.length != z1 || bestBaseSymOp[0].length != z2) {
596 bestBaseSymOp = new SymOp[z1][z2];
597 }
598 if (bestTargetSymOp == null || bestTargetSymOp.length != z1
599 || bestTargetSymOp[0].length != z2) {
600 bestTargetSymOp = new SymOp[z1][z2];
601 }
602 if (bestBaseTransformSymOp == null || bestBaseTransformSymOp.length != z1
603 || bestBaseTransformSymOp[0].length != z2) {
604 bestBaseTransformSymOp = new SymOp[z1][z2];
605 }
606 if (bestTargetTransformSymOp == null || bestTargetTransformSymOp.length != z1
607 || bestTargetTransformSymOp[0].length != z2) {
608 bestTargetTransformSymOp = new SymOp[z1][z2];
609 }
610 }
611
612
613 if (logger.isLoggable(Level.FINER)) {
614 stringBuilder.append(" Prioritize target system.\n");
615
616 stringBuilder.append(" Search conformations of each crystal:\n");
617 }
618
619 arraycopy(baseXYZ, baseAUDist[0].index() * nCoords, baseAU, 0, nCoords);
620
621 tempListIndices.clear();
622
623 numberUniqueAUs(baseXYZ, baseAUDist, baseAU.clone(), nCoords, baseSearchValue, strict, nBaseMols,
624 massN, matchTol);
625 int baseLength = tempListIndices.size();
626 if (uniqueBase == null || uniqueBase.length != baseLength) {
627 uniqueBase = new Integer[baseLength];
628 }
629 tempListIndices.toArray(uniqueBase);
630
631
632 arraycopy(targetXYZ, targetAUDist[0].index() * nCoords, targetAU, 0, nCoords);
633 tempListIndices.clear();
634
635 numberUniqueAUs(targetXYZ, targetAUDist, targetAU.clone(), nCoords, targetSearchValue, strict,
636 nTargetMols, massN, matchTol);
637 int targetLength = tempListIndices.size();
638 if (uniqueTarget == null || uniqueTarget.length != targetLength) {
639 uniqueTarget = new Integer[targetLength];
640 }
641 tempListIndices.toArray(uniqueTarget);
642 final boolean reverse = baseLength > targetLength;
643 if (logger.isLoggable(Level.FINE)) {
644 stringBuilder.append(format("""
645
646 %d conformations detected out of %d in base crystal.
647 %d conformations detected out of %d in target crystal.
648 """, baseLength, baseSearchValue, targetLength, targetSearchValue));
649 }
650
651 if (useSym) {
652
653 if (z1 > 1 || z2 > 1) {
654 stringBuilder.append(
655 "\n Utilizing Sym Ops with Z''>1. Attempting to map all unique structures.\n");
656 }
657 }
658
659
660
661 double min1 = Double.MAX_VALUE;
662 double max1 = Double.MIN_VALUE;
663 int minBIndex = -1;
664 int minTIndex = -1;
665 int maxBIndex = -1;
666 int maxTIndex = -1;
667
668
669 IndexIndexPair[][] minZinds = new IndexIndexPair[z1][z2];
670 for (IndexIndexPair[] row : minZinds) {
671 fill(row, new IndexIndexPair(-1, -1));
672 }
673
674
675 double[][] minZdiffs = new double[z1][z2];
676 for (double[] row : minZdiffs) {
677 fill(row, Double.MAX_VALUE);
678 }
679
680 double[][] minDiffs = new double[baseLength][targetLength];
681 for (double[] row : minDiffs) {
682 fill(row, Double.MAX_VALUE);
683 }
684
685 for (int i = 0; i < baseLength; i++) {
686
687 int bIndex = uniqueBase[i];
688
689 int baseInd = baseAUDist[bIndex].index();
690
691 int currZ1 = baseDistMap.get(baseInd) % z1;
692
693 double[] baseXYZ = new double[nCoords];
694
695 for (int j = 0; j < targetLength; j++) {
696
697 arraycopy(this.baseXYZ, baseInd * nCoords, baseXYZ, 0, nCoords);
698
699 int tIndex = uniqueTarget[j];
700
701 int targetInd = targetAUDist[tIndex].index();
702
703 int currZ2 = targetDistMap.get(targetInd) % z2;
704
705 double[] targetXYZ = new double[nCoords];
706
707 arraycopy(this.targetXYZ, targetInd * nCoords, targetXYZ, 0, nCoords);
708
709 double[] tempTranB = calculateTranslation(baseXYZ, massN);
710 applyTranslation(baseXYZ, tempTranB);
711
712 double[] tempTranT = calculateTranslation(targetXYZ, massN);
713 applyTranslation(targetXYZ, tempTranT);
714
715 double[][] tempRotT = calculateRotation(baseXYZ, targetXYZ, massN);
716 applyRotation(targetXYZ, tempRotT);
717
718 double value = rmsd(baseXYZ, targetXYZ, massN);
719
720 if (logger.isLoggable(Level.FINEST)) {
721 stringBuilder.append(format(
722 "\n Comp %3d: Base %2d IND %3d (Z''=%2d) \t Target %2d IND %3d (Z''=%2d) Diff: %8.4f\n",
723 j + i * targetLength, bIndex, baseInd, currZ1, tIndex, targetInd, currZ2, value));
724 if (useSym) {
725 stringBuilder.append(" Base Temp Translation: ").append(Arrays.toString(tempTranB))
726 .append("\n");
727 stringBuilder.append(" Target Temp Translation: ").append(Arrays.toString(tempTranT));
728 stringBuilder.append(matrixToString(tempRotT, j + i * targetLength, "Temp Rot"))
729 .append("\n");
730 }
731 }
732
733 minDiffs[i][j] = value;
734
735 if (nAU == 1 && !strict) {
736
737 arraycopy(baseXYZ, 0, baseNAUs[0], 0, nAU * nCoords);
738 arraycopy(targetXYZ, 0, targetNAUs, 0, nAU * nCoords);
739 arraycopy(baseXYZ, 0, bestBaseNAUs[currZ1][currZ2], 0, nAU * nCoords);
740 arraycopy(targetXYZ, 0, bestTargetNAUs[currZ1][currZ2], 0, nAU * nCoords);
741 }
742 if (useSym && value < minZdiffs[currZ1][currZ2]) {
743 minZdiffs[currZ1][currZ2] = value;
744 minZinds[currZ1][currZ2] = new IndexIndexPair(baseInd, targetInd);
745 arraycopy(baseXYZ, 0, baseAU, 0, nCoords);
746 arraycopy(targetXYZ, 0, targetAU, 0, nCoords);
747 if (logger.isLoggable(Level.FINER)) {
748 stringBuilder.append(
749 format("\n Base AU: %2d of %2d" + "\t\tvs\t\t Target AU: %2d of %2d \t (%9.4f)",
750 currZ1 + 1, z1, currZ2 + 1, z2, minZdiffs[currZ1][currZ2]));
751 if (logger.isLoggable(Level.FINER)) {
752 stringBuilder.append("\n Base Translation: ").append(Arrays.toString(tempTranB))
753 .append("\n");
754 stringBuilder.append(" Target Translation: ").append(Arrays.toString(tempTranT));
755 stringBuilder.append(matrixToString(tempRotT, currZ1, "Target Rotation"));
756 }
757 }
758 baseTransformSymOp = new SymOp(ZERO_ROTATION, tempTranB);
759 bestBaseTransformSymOp[currZ1][currZ2] = baseTransformSymOp;
760
761 targetTransformSymOp = new SymOp(ZERO_ROTATION, tempTranT);
762 targetTransformSymOp = targetTransformSymOp.append(new SymOp(tempRotT, Tr_0_0_0));
763 bestTargetTransformSymOp[currZ1][currZ2] = targetTransformSymOp;
764 SymOp base = baseSymOps.get(baseDistMap.get(minZinds[currZ1][currZ2].sortedIndex()));
765 baseSymOp = new SymOp(base.rot, base.tr);
766 SymOp target = targetSymOps.get(
767 targetDistMap.get(minZinds[currZ1][currZ2].referenceIndex()));
768 targetSymOp = new SymOp(target.rot, target.tr);
769 bestBaseSymOp[currZ1][currZ2] = baseSymOp;
770 bestTargetSymOp[currZ1][currZ2] = targetSymOp;
771
772 if (logger.isLoggable(Level.FINEST)) {
773 stringBuilder.append("\n Base Sym Op Translation: ")
774 .append(Arrays.toString(bestBaseSymOp[currZ1][currZ2].tr));
775 stringBuilder.append(
776 matrixToString(bestBaseSymOp[currZ1][currZ2].rot, currZ2 + currZ1 * z2,
777 "Base Sym Op Rot")).append("\n");
778 stringBuilder.append(" Base Transform Translation: ")
779 .append(Arrays.toString(bestBaseTransformSymOp[currZ1][currZ2].tr));
780 stringBuilder.append(
781 matrixToString(bestBaseTransformSymOp[currZ1][currZ2].rot, currZ2 + currZ1 * z2,
782 "Base Transform Rot")).append("\n");
783 stringBuilder.append(" Target Sym Op Translation: ")
784 .append(Arrays.toString(bestTargetSymOp[currZ1][currZ2].tr));
785 stringBuilder.append(
786 matrixToString(bestTargetSymOp[currZ1][currZ2].rot, currZ2 + currZ1 * z2,
787 "Target Sym Op Rot")).append("\n");
788 stringBuilder.append(" Target Transform Translation: ")
789 .append(Arrays.toString(bestTargetTransformSymOp[currZ1][currZ2].tr));
790 stringBuilder.append(
791 matrixToString(bestTargetTransformSymOp[currZ1][currZ2].rot, currZ2 + currZ1 * z2,
792 "Target Transform Rot")).append("\n");
793 }
794 if (logger.isLoggable(Level.FINER)) {
795 stringBuilder.append(format(
796 " \n Saved %3d: Base %2d IND %3d (Z''=%2d) \t Target %2d IND %3d (Z''=%2d) Diff: %8.4f\n",
797 j + i * targetLength, bIndex, baseInd, currZ1, tIndex, targetInd, currZ2, value));
798 }
799 }
800
801 if (minDiffs[i][j] < min1) {
802 min1 = minDiffs[i][j];
803 minBIndex = bIndex;
804 minTIndex = tIndex;
805 }
806
807 if (minDiffs[i][j] > max1) {
808 max1 = minDiffs[i][j];
809 maxBIndex = bIndex;
810 maxTIndex = tIndex;
811 }
812 }
813 }
814
815 int baseBestZ = baseDistMap.get(baseAUDist[minBIndex].index()) % z1;
816 int targetBestZ = targetDistMap.get(targetAUDist[minTIndex].index()) % z2;
817 if (logger.isLoggable(Level.FINER)) {
818 stringBuilder.append(
819 format("\n Base min index: %d (Z''=%d) Target min Index %d (Z''=%d)", minBIndex, baseBestZ,
820 minTIndex, targetBestZ));
821 }
822
823 int maxLength = max(baseLength, targetLength);
824 int[] baseTargetMap = new int[maxLength];
825
826 if (reverse) {
827 for (int i = 0; i < baseLength; i++) {
828 double minValue = Double.MAX_VALUE;
829 for (int j = 0; j < targetLength; j++) {
830 if (minValue > minDiffs[i][j]) {
831 minValue = minDiffs[i][j];
832 baseTargetMap[i] = uniqueTarget[j];
833 }
834 }
835 }
836 } else {
837 for (int i = 0; i < targetLength; i++) {
838 double minValue = Double.MAX_VALUE;
839 for (int j = 0; j < baseLength; j++) {
840 if (minValue > minDiffs[j][i]) {
841 minValue = minDiffs[j][i];
842 baseTargetMap[i] = uniqueBase[j];
843 }
844 }
845 }
846 }
847
848
849 if (nAU == 1 && !strict) {
850
851
852 double baseGyration = radiusOfGyration(bestBaseNAUs[baseBestZ][targetBestZ].clone(), massN);
853 gyrations[0] = baseGyration;
854 double targetGyration = radiusOfGyration(bestTargetNAUs[baseBestZ][targetBestZ].clone(),
855 massN);
856 gyrations[1] = targetGyration;
857 if (useSym) {
858
859 final int numAtomsPerMol1 = atoms1.length/z1;
860 final int numAtomsPerMol2 = atoms2.length/z2;
861
862 boolean multisym = z1 > 1 || z2 > 1;
863 StringBuilder alchemicalAtoms = null;
864 StringBuilder alchemicalAtoms2 = null;
865 StringBuilder separation = new StringBuilder();
866 for (int i = 0; i < z1; i++) {
867 for (int j = 0; j < z2; j++) {
868 separation.append("\n Atom Separation (A) Description");
869 if (multisym) {
870 separation.append(format(" (Z1'' =%2d Z2'' =%2d)\n Base: Target: Distance:\n", i + 1, j + 1));
871 } else {
872 separation.append(" \n");
873 }
874 for (int k = 0; k < compareAtomsSize; k++) {
875 final int index = k * 3;
876 final double value = rmsd(
877 new double[]{bestBaseNAUs[i][j][index], bestBaseNAUs[i][j][index + 1],
878 bestBaseNAUs[i][j][index + 2]},
879 new double[]{bestTargetNAUs[i][j][index], bestTargetNAUs[i][j][index + 1],
880 bestTargetNAUs[i][j][index + 2]}, massN);
881 if (logger.isLoggable(Level.INFO)) {
882 if (printSym < value) {
883
884 separation.append(
885 format(" %4d %4d %14.6f\n", i * numAtomsPerMol1 + comparisonAtoms[k] + 1,
886 j * numAtomsPerMol2 + comparisonAtoms[k] + 1, value));
887
888 if (alchemicalAtoms == null) {
889 alchemicalAtoms = new StringBuilder();
890 if (multisym) {
891 alchemicalAtoms.append(format(" %3d %3d ", i, j));
892 }
893 alchemicalAtoms.append(i * numAtomsPerMol1 + comparisonAtoms[k] + 1);
894 alchemicalAtoms2 = new StringBuilder();
895 if (multisym) {
896 alchemicalAtoms2.append(format(" %3d %3d ", i, j));
897 }
898 alchemicalAtoms2.append(j * numAtomsPerMol2 + comparisonAtoms[k] + 1);
899 } else if (alchemicalAtoms.charAt(alchemicalAtoms.length() - 1) == '\n') {
900 alchemicalAtoms.append(format(" %3d %3d ", i, j))
901 .append(i * numAtomsPerMol1 + comparisonAtoms[k] + 1);
902 alchemicalAtoms2.append(format(" %3d %3d ", i, j))
903 .append(j * numAtomsPerMol2 + comparisonAtoms[k] + 1);
904 } else {
905 alchemicalAtoms.append(",")
906 .append(i * numAtomsPerMol1 + comparisonAtoms[k] + 1);
907 alchemicalAtoms2.append(",")
908 .append(j * numAtomsPerMol2 + comparisonAtoms[k] + 1);
909 }
910 }
911 }
912 }
913 if (useSave) {
914 if (machineLearning) {
915 saveAssembly(file1, name1, bondList1, atoms1, forceField1, bestBaseNAUs[i][j],
916 comparisonAtoms, nAU, "_c1", 0.000, compNum, saveClusters);
917 saveAssembly(file2, name2, bondList2, atoms2, forceField2, bestTargetNAUs[i][j],
918 comparisonAtoms, nAU, "_c2", min1, compNum, saveClusters);
919 } else {
920 saveAssembly(file1, name1, bondList1, atoms1, forceField1, bestBaseNAUs[i][j],
921 comparisonAtoms, nAU, "_c1", compNum, saveClusters);
922 saveAssembly(file2, name2, bondList2, atoms2, forceField2, bestTargetNAUs[i][j],
923 comparisonAtoms, nAU, "_c2", compNum, saveClusters);
924 }
925 }
926 if (alchemicalAtoms != null) {
927 alchemicalAtoms.append("\n");
928 alchemicalAtoms2.append("\n");
929 }
930 }
931 }
932 if (alchemicalAtoms != null) {
933 stringBuilder.append(separation);
934 stringBuilder.append("\n Suggested Alchemical Atoms:\n");
935
936 if (multisym) {
937 stringBuilder.append(" Base: (").append(baseLabel).append(")\n");
938 }
939 stringBuilder.append(alchemicalAtoms).append("\n");
940 if (multisym) {
941 stringBuilder.append(" Target: (").append(targetLabel).append(")\n")
942 .append(alchemicalAtoms2).append("\n");
943 }
944 }
945 if(logger.isLoggable(Level.FINE)){
946 stringBuilder.append(format("\n Max RMSD: %9.4f B Index: %d T Index: %d\n", max1, maxBIndex, maxTIndex));
947 }
948 }
949 if (useSave && !useSym) {
950 if (machineLearning) {
951 saveAssembly(file1, name1, bondList1, atoms1, forceField1,
952 bestBaseNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c1", 0.000, compNum,
953 saveClusters);
954 saveAssembly(file2, name2, bondList2, atoms2, forceField2,
955 bestTargetNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c2", min1, compNum,
956 saveClusters);
957 } else {
958 saveAssembly(file1, name1, bondList1, atoms1, forceField1,
959 bestBaseNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c1", compNum, saveClusters);
960 saveAssembly(file2, name2, bondList2, atoms2, forceField2,
961 bestTargetNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c2", compNum, saveClusters);
962 }
963 }
964 if (logger.isLoggable(Level.FINER) && useSym) {
965 for (int i = 0; i < z1; i++) {
966 for (int j = 0; j < z2; j++) {
967 printSym(compareAtomsSize, file2, name2, bondList2, atoms2, forceField2, saveClusters, j);
968 }
969 }
970 }
971 return min1;
972 }
973 if (logger.isLoggable(Level.FINER)) {
974 stringBuilder.append(
975 " Minimum RMSD_1 Between Unique Base and Target AUs:\n i j (bInd tInd) RMSD_1\n");
976 for (int i = 0; i < baseLength; i++) {
977 for (int j = 0; j < targetLength; j++) {
978 stringBuilder.append(
979 format(" %d %d (%4d %4d) %4.4f\n", i, j, baseAUDist[uniqueBase[i]].index(),
980 targetAUDist[uniqueTarget[j]].index(), minDiffs[i][j]));
981 }
982 }
983 }
984
985
986 double bestRMSD = Double.MAX_VALUE;
987
988 for (double[] row : minZdiffs) {
989 fill(row, Double.MAX_VALUE);
990 }
991 if (logger.isLoggable(Level.FINE)) {
992 stringBuilder.append(
993 format("\n Trial RMSD_1 (%8s) RMSD_3 (%8s) %8s G(r1) G(r2)\n", rmsdLabel,
994 rmsdLabel, rmsdLabel));
995 }
996 baseAUDist_2 = new DoubleIndexPair[nBaseMols];
997 targetAUDist_2 = new DoubleIndexPair[nTargetMols];
998
999
1000 int currentComparison = 1;
1001 for (int l = 0; l < baseLength; l++) {
1002 final int bIndex = uniqueBase[l];
1003 final int baseInd = baseAUDist[bIndex].index();
1004 final int currZ1 = baseDistMap.get(baseInd) % z1;
1005
1006 if (l > 0) {
1007
1008 arraycopy(baseXYZoriginal, 0, baseXYZ, 0, baseXYZoriginal.length);
1009 }
1010
1011 prioritizeReplicates(baseXYZ, baseCoM[0], compareAtomsSize, baseAUDist_2,
1012 baseInd, linkage);
1013
1014 int baseAUIndex = baseAUDist_2[0].index() * nCoords;
1015 arraycopy(baseXYZ, baseAUIndex, baseAU, 0, nCoords);
1016 for (int i = 0; i < 4; i++) {
1017 for (int j = 0; j < nAU; j++) {
1018 final int molIndex = baseAUDist_2[j].index() * nCoords;
1019 arraycopy(baseXYZ, molIndex, baseNAUs[i], j * nCoords, nCoords);
1020 }
1021 }
1022
1023 double[] translation = calculateTranslation(baseAU, massN);
1024 applyTranslation(baseAU, translation);
1025 applyTranslation(baseNAUs[1], translation);
1026 applyTranslation(baseNAUs[2], translation);
1027 applyTranslation(baseNAUs[3], translation);
1028 applyTranslation(baseXYZ, translation);
1029
1030 centerOfMass(baseCoM[1], baseXYZ, massN, massSum, compareAtomsSize);
1031 if (useSym) {
1032
1033 baseTransformSymOp = new SymOp(ZERO_ROTATION, Tr_0_0_0);
1034
1035 final SymOp base = baseSymOps.get(baseInd);
1036 baseSymOp = new SymOp(base.rot, base.tr);
1037 baseTransformSymOp = baseTransformSymOp.append(new SymOp(ZERO_ROTATION, translation));
1038 if (logger.isLoggable(Level.FINEST)) {
1039 stringBuilder.append(format("\n Base %d (%2d) to Origin Translation: ", l, bIndex))
1040 .append(Arrays.toString(translation)).append("\n");
1041 }
1042 }
1043
1044
1045 if (logger.isLoggable(Level.FINEST)) {
1046 stringBuilder.append("\n Base 3 Conformations:\n");
1047 }
1048 for (int i = 0; i < 3; i++) {
1049 int baseIndex = baseAUDist_2[i].index() * nCoords;
1050 arraycopy(baseXYZ, baseIndex, base3AUs, i * nCoords, nCoords);
1051 }
1052 translation = calculateTranslation(base3AUs, massN);
1053 applyTranslation(base3AUs, translation);
1054 applyTranslation(baseNAUs[2], translation);
1055 applyTranslation(baseNAUs[3], translation);
1056 applyTranslation(baseXYZ, translation);
1057
1058 centerOfMass(baseCoM[2], baseXYZ, massN, massSum, compareAtomsSize);
1059 if (useSym) {
1060 baseTransformSymOp = baseTransformSymOp.append(new SymOp(ZERO_ROTATION, translation));
1061 if (logger.isLoggable(Level.FINEST)) {
1062 stringBuilder.append(format("\n Base %d (%2d) 2nd Translation: ", l, bIndex))
1063 .append(Arrays.toString(translation)).append("\n");
1064 }
1065 }
1066
1067
1068 final double maxDist = baseAUDist[baseAUDist.length - 1].doubleValue();
1069 if (logger.isLoggable(Level.FINEST)) {
1070 stringBuilder.append(format("\n System 1 farthest distance: %4.8f", sqrt(maxDist)));
1071 }
1072 for (int i = 0; i < nAU; i++) {
1073 final int molIndex = baseAUDist_2[i].index() * nCoords;
1074 arraycopy(baseXYZ, molIndex, baseNAUs[3], i * nCoords, nCoords);
1075 }
1076 translation = calculateTranslation(baseNAUs[3], massN);
1077
1078 applyTranslation(baseNAUs[3], translation);
1079 applyTranslation(baseXYZ, translation);
1080 if (useSym) {
1081 baseTransformSymOp = baseTransformSymOp.append(new SymOp(ZERO_ROTATION, translation));
1082 if (logger.isLoggable(Level.FINEST)) {
1083 stringBuilder.append(format("\n Base %d (%2d) Final Translation: ", l, bIndex))
1084 .append(Arrays.toString(translation)).append("\n");
1085 }
1086 }
1087
1088 for (int m = 0; m < targetLength; m++) {
1089 final int tIndex = uniqueTarget[m];
1090 final int targetInd = targetAUDist[tIndex].index();
1091 final int currZ2 = targetDistMap.get(targetInd) % z2;
1092 if (strict || !reverse && baseTargetMap[m] == bIndex
1093 || reverse && baseTargetMap[l] == tIndex) {
1094
1095 arraycopy(targetXYZoriginal, 0, targetXYZ, 0, targetXYZoriginal.length);
1096
1097
1098 prioritizeReplicates(targetXYZ, targetCoM, compareAtomsSize,
1099 targetAUDist_2, targetInd, linkage);
1100
1101
1102 arraycopy(targetXYZ, targetAUDist_2[0].index() * nCoords, targetAU, 0, nCoords);
1103
1104
1105 translation = calculateTranslation(targetAU, massN);
1106
1107 applyTranslation(targetAU, translation);
1108
1109 applyTranslation(targetXYZ, translation);
1110
1111
1112 double[][] rotation = calculateRotation(baseAU, targetAU, massN);
1113 applyRotation(targetAU, rotation);
1114 applyRotation(targetXYZ, rotation);
1115
1116 centerOfMass(targetCoM, targetXYZ, massN, massSum, compareAtomsSize);
1117
1118 if (useSym) {
1119
1120 targetTransformSymOp = new SymOp(ZERO_ROTATION, Tr_0_0_0);
1121
1122 final SymOp target = targetSymOps.get(targetInd);
1123 targetSymOp = new SymOp(target.rot, target.tr);
1124
1125
1126 targetTransformSymOp = targetTransformSymOp.append(new SymOp(ZERO_ROTATION, translation))
1127 .append(new SymOp(rotation, Tr_0_0_0));
1128
1129 if (logger.isLoggable(Level.FINEST)) {
1130 stringBuilder.append(matrixToString(baseSymOp.rot, bIndex, "Base Sym Rot"));
1131 stringBuilder.append(format("\n Base %d Sym Op Translation: ", bIndex))
1132 .append(Arrays.toString(baseSymOp.tr)).append("\n");
1133 stringBuilder.append(matrixToString(targetSymOp.rot, tIndex, "Target Sym Rot"));
1134 stringBuilder.append(format("\n Target %d Sym Op Translation: ", tIndex))
1135 .append(Arrays.toString(targetSymOp.tr)).append("\n");
1136 stringBuilder.append(format("\n Trans target %d (%2d) sys to origin: \n\t", m, tIndex))
1137 .append(Arrays.toString(translation)).append("\n");
1138 stringBuilder.append(matrixToString(rotation, tIndex, "1st Rot"));
1139 }
1140 }
1141
1142
1143
1144
1145 pairEntities(max(3, nAU), 1);
1146
1147 double checkRMSD1 = -3.0;
1148 double n1RMSD = -4.0;
1149 if (logger.isLoggable(Level.FINE)) {
1150 checkRMSD1 = rmsd(baseAU, targetAU, massN);
1151 for (int i = 0; i < nAU; i++) {
1152 final int offset = i * nCoords;
1153 final int molIndex = pairedAUs[i].index() * nCoords;
1154 arraycopy(targetXYZ, molIndex, targetNAUs, offset, nCoords);
1155 }
1156 n1RMSD = rmsd(baseNAUs[1], targetNAUs, massN);
1157 if (logger.isLoggable(Level.FINEST) && useSave) {
1158 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseAU, comparisonAtoms, 1,
1159 "_c1_1", compNum, saveClusters);
1160 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseNAUs[1],
1161 comparisonAtoms, nAU, "_c1_1N", compNum, saveClusters);
1162 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetAU, comparisonAtoms,
1163 1, "_c2_1", compNum, saveClusters);
1164 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetNAUs, comparisonAtoms,
1165 nAU, "_c2_1N", compNum, saveClusters);
1166 }
1167 }
1168
1169
1170 for (int i = 0; i < 3; i++) {
1171 final int targetIndex = pairedAUs[i].index() * nCoords;
1172 final int coordIndex = i * nCoords;
1173 arraycopy(targetXYZ, targetIndex, target3AUs, coordIndex, nCoords);
1174 }
1175
1176 translation = calculateTranslation(target3AUs, massN);
1177 applyTranslation(target3AUs, translation);
1178 applyTranslation(targetNAUs, translation);
1179 applyTranslation(targetXYZ, translation);
1180
1181
1182
1183 rotation = calculateRotation(base3AUs, target3AUs, massN);
1184 applyRotation(target3AUs, rotation);
1185 applyRotation(targetNAUs, rotation);
1186 applyRotation(targetXYZ, rotation);
1187
1188 centerOfMass(targetCoM, targetXYZ, massN, massSum, compareAtomsSize);
1189 if (useSym) {
1190 applyTranslation(targetAU, translation);
1191 applyRotation(targetAU, rotation);
1192 targetTransformSymOp = targetTransformSymOp.append(new SymOp(ZERO_ROTATION, translation))
1193 .append(new SymOp(rotation, Tr_0_0_0));
1194 if (logger.isLoggable(Level.FINEST)) {
1195 stringBuilder.append(
1196 format("\n Target %d (%2d) 2nd Translation/Rotation: ", m, tIndex))
1197 .append(Arrays.toString(translation)).append("\n");
1198 printSym(compareAtomsSize, file2, name2, bondList2, atoms2, forceField2, saveClusters, currZ2);
1199 }
1200 }
1201
1202 double checkRMSD2 = -5.0;
1203 double n3RMSD = -6.0;
1204 if (logger.isLoggable(Level.FINE)) {
1205 checkRMSD2 = rmsd(base3AUs, target3AUs, massN);
1206 n3RMSD = rmsd(baseNAUs[2], targetNAUs, massN);
1207 if (logger.isLoggable(Level.FINEST) && useSave) {
1208 saveAssembly(file1, name1, bondList1, atoms1, forceField1, base3AUs, comparisonAtoms,
1209 3, "_c1_3", compNum, saveClusters);
1210 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseNAUs[2],
1211 comparisonAtoms, nAU, "_c1_3N", compNum, saveClusters);
1212 saveAssembly(file2, name2, bondList2, atoms2, forceField2, target3AUs, comparisonAtoms,
1213 3, "_c2_3", compNum, saveClusters);
1214 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetNAUs, comparisonAtoms,
1215 nAU, "_c2_3N", compNum, saveClusters);
1216 }
1217 }
1218
1219
1220
1221 pairEntities(nAU, 2);
1222
1223 for (int i = 0; i < nAU; i++) {
1224 final int offset = i * nCoords;
1225 final int molIndex = pairedAUs[i].index() * nCoords;
1226 arraycopy(targetXYZ, molIndex, targetNAUs, offset, nCoords);
1227 }
1228
1229 translation = calculateTranslation(targetNAUs, massN);
1230 applyTranslation(targetNAUs, translation);
1231 applyTranslation(targetXYZ, translation);
1232 rotation = calculateRotation(baseNAUs[3], targetNAUs, massN);
1233 if (useSym) {
1234 applyTranslation(targetAU, translation);
1235 applyRotation(targetAU, rotation);
1236 targetTransformSymOp = targetTransformSymOp.append(new SymOp(ZERO_ROTATION, translation))
1237 .append(new SymOp(rotation, Tr_0_0_0));
1238
1239 if (logger.isLoggable(Level.FINEST)) {
1240 stringBuilder.append(matrixToString(rotation, bIndex, "Target System Final Rotation"));
1241 stringBuilder.append(format("\n Target %d Final Translation: ", bIndex))
1242 .append(Arrays.toString(translation)).append("\n");
1243 printSym(compareAtomsSize, file2, name2, bondList2, atoms2, forceField2, saveClusters, currZ2);
1244 }
1245 }
1246 if (logger.isLoggable(Level.FINEST) && useSave) {
1247 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseNAUs[3], comparisonAtoms,
1248 nAU, "_c1_N", compNum, saveClusters);
1249 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetNAUs, comparisonAtoms,
1250 nAU, "_c2_N", compNum, saveClusters);
1251 }
1252 applyRotation(targetNAUs, rotation);
1253 applyRotation(targetXYZ, rotation);
1254 final double rmsdSymOp = rmsd(baseNAUs[3], targetNAUs, massN);
1255
1256 if (logger.isLoggable(Level.FINEST) && useSave) {
1257 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseNAUs[3], comparisonAtoms,
1258 nAU, "_c1_N2", compNum, saveClusters);
1259 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetNAUs, comparisonAtoms,
1260 nAU, "_c2_N2", compNum, saveClusters);
1261 }
1262
1263 final double baseGyration = radiusOfGyration(baseNAUs[3], massN);
1264 final double targetGyration = radiusOfGyration(targetNAUs, massN);
1265
1266 if (logger.isLoggable(Level.FINE)) {
1267 final int totalComparisons = (strict) ? baseLength * targetLength : maxLength;
1268 String output = format(" %2d of %2d: %7.4f (%8.4f) %7.4f (%8.4f) %8.4f %8.4f %8.4f",
1269 currentComparison, totalComparisons, checkRMSD1, n1RMSD, checkRMSD2, n3RMSD,
1270 rmsdSymOp, baseGyration, targetGyration);
1271
1272 if (logger.isLoggable(Level.FINER)) {
1273 if (reverse) {
1274 output += format(" b: %2d t: %2d bt: %2d", baseAUDist[bIndex].index(),
1275 targetAUDist[uniqueTarget[m]].index(), targetAUDist[baseTargetMap[m]].index());
1276 } else {
1277 output += format(" b: %2d t: %2d tb: %2d", baseAUDist[bIndex].index(),
1278 targetAUDist[uniqueTarget[m]].index(), baseAUDist[baseTargetMap[m]].index());
1279 }
1280 }
1281 stringBuilder.append(output).append("\n");
1282 }
1283
1284 if (useSym && rmsdSymOp < minZdiffs[currZ1][currZ2]) {
1285 minZdiffs[currZ1][currZ2] = rmsdSymOp;
1286 bestTargetTransformSymOp[currZ1][currZ2] = new SymOp(targetTransformSymOp.rot,
1287 targetTransformSymOp.tr);
1288 bestTargetSymOp[currZ1][currZ2] = new SymOp(targetSymOp.rot, targetSymOp.tr);
1289 bestBaseSymOp[currZ1][currZ2] = new SymOp(baseSymOp.rot, baseSymOp.tr);
1290 bestBaseTransformSymOp[currZ1][currZ2] = new SymOp(baseTransformSymOp.rot,
1291 baseTransformSymOp.tr);
1292 }
1293 if (rmsdSymOp < bestRMSD) {
1294
1295 gyrations[0] = baseGyration;
1296 gyrations[1] = targetGyration;
1297 bestRMSD = rmsdSymOp;
1298 baseBestZ = currZ1;
1299 targetBestZ = currZ2;
1300 arraycopy(baseNAUs[3], 0, bestBaseNAUs[currZ1][currZ2], 0, nAU * nCoords);
1301 arraycopy(targetNAUs, 0, bestTargetNAUs[currZ1][currZ2], 0, nAU * nCoords);
1302 }
1303 currentComparison++;
1304 }
1305 }
1306 }
1307
1308 double finalRMSD;
1309 if (bestRMSD < Double.MAX_VALUE) {
1310 finalRMSD = bestRMSD;
1311 } else {
1312 stringBuilder.append(" This RMSD was filtered out! Try the --st flag or increasing --if.\n");
1313
1314 finalRMSD = -7.0;
1315 }
1316 if (nAU == 1 && abs(min1 - finalRMSD) > MATCH_TOLERANCE) {
1317 stringBuilder.append(
1318 format(" WARNING: Single molecule overlay (%8.4f) does not match PAC RMSD_1 (%8.4f)", min1,
1319 finalRMSD));
1320 }
1321 if (useSave) {
1322 if (machineLearning) {
1323 saveAssembly(file1, name1, bondList1, atoms1, forceField1,
1324 bestBaseNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c1", 0.000, compNum, saveClusters);
1325 saveAssembly(file2, name2, bondList2, atoms2, forceField2,
1326 bestTargetNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c2", finalRMSD, compNum,
1327 saveClusters);
1328 } else {
1329 saveAssembly(file1, name1, bondList1, atoms1, forceField1,
1330 bestBaseNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c1", compNum, saveClusters);
1331 saveAssembly(file2, name2, bondList2, atoms2, forceField2,
1332 bestTargetNAUs[baseBestZ][targetBestZ], comparisonAtoms, nAU, "_c2", compNum, saveClusters);
1333 }
1334 }
1335
1336 if (inertia) {
1337 bestBaseMandV = momentsOfInertia(bestBaseNAUs[baseBestZ][targetBestZ], massN, false, false,
1338 true);
1339 bestTargetMandV = momentsOfInertia(bestTargetNAUs[baseBestZ][targetBestZ], massN, false, false,
1340 true);
1341 }
1342
1343 if (gyrationComponents) {
1344 bestBaseRg = radiusOfGyrationComponents(bestBaseNAUs[baseBestZ][targetBestZ], massN, true);
1345 bestTargetRg = radiusOfGyrationComponents(bestTargetNAUs[baseBestZ][targetBestZ], massN, true);
1346 }
1347
1348 return finalRMSD;
1349 }
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388 public RunningStatistics comparisons(final int nAU, final double inflationFactor, double matchTol, final double hitTol, final int zPrime,
1389 final int zPrime2, final String excludeAtomsA, final String excludeAtomsB, final boolean alphaCarbons,
1390 final boolean includeHydrogen, final boolean massWeighted, final int crystalPriority, final boolean strict,
1391 int saveClusters, final double save, final boolean restart, final boolean write, final boolean machineLearning, boolean inertia,
1392 final boolean gyrationComponents, int linkage, final double printSym, boolean lowMemory, final boolean createFE, final boolean writeSym,
1393 final String pacFileName, final StringBuilder symOpsA, final StringBuilder symOpsB) {
1394 this.printSym = printSym;
1395
1396
1397
1398
1399 RunningStatistics runningStatistics;
1400 if (restart) {
1401 runningStatistics = readMatrix(pacFileName);
1402 if (runningStatistics == null) {
1403 runningStatistics = new RunningStatistics();
1404 }
1405 } else {
1406 runningStatistics = new RunningStatistics();
1407 File file = new File(pacFileName);
1408 if (file.exists() && file.delete()) {
1409 logger.info(format(" PAC RMSD file (%s) was deleted.", pacFileName));
1410 logger.info(" To restart from a previous run, use the '-r' flag.");
1411 }
1412 }
1413
1414
1415
1416
1417
1418 for (int row = 0; row < restartRow; row++) {
1419 baseFilter.readNext(false, false, row + 1 >= restartRow);
1420 }
1421
1422 MolecularAssembly baseAssembly = baseFilter.getActiveMolecularSystem();
1423
1424
1425 Atom[] atoms1 = baseAssembly.getAtomArray();
1426 final int nAtoms = atoms1.length;
1427
1428
1429 MolecularAssembly targetAssembly = targetFilter.getActiveMolecularSystem();
1430 Atom[] atoms2 = targetAssembly.getAtomArray();
1431 final int nAtoms2 = atoms2.length;
1432
1433
1434 ArrayList<Integer> unique = new ArrayList<>(parseAtomRanges("Base Assembly", excludeAtomsA, nAtoms));
1435 if (invalidAtomSelection(unique, atoms1, alphaCarbons, includeHydrogen)) {
1436 logger.warning("\n No atoms were selected for the PAC RMSD in first crystal.");
1437 return runningStatistics;
1438 }
1439 int[] comparisonAtoms = unique.stream().mapToInt(i -> i).toArray();
1440 List<MSNode> bondedEntities = baseAssembly.getNodeList();
1441
1442
1443
1444 int z1;
1445 if(zPrime > 0){
1446 z1 = zPrime;
1447 }else{
1448 z1 = guessZPrime(unique, baseAssembly.getMoleculeNumbers(), bondedEntities.size());
1449 }
1450
1451
1452 unique = new ArrayList<>(parseAtomRanges("target", excludeAtomsB, nAtoms2));
1453 if (invalidAtomSelection(unique, atoms2, alphaCarbons, includeHydrogen)) {
1454 logger.warning("\n No atoms were selected for the PAC RMSD in second crystal.");
1455 return runningStatistics;
1456 }
1457 int[] comparisonAtoms2 = unique.stream().mapToInt(i -> i).toArray();
1458
1459 List<MSNode> bondedEntities2 = targetAssembly.getAllBondedEntities();
1460 int z2;
1461 if(zPrime2 > 0){
1462 z2 = zPrime2;
1463 }else{
1464 z2 = guessZPrime(unique, targetAssembly.getMoleculeNumbers(), bondedEntities2.size());
1465 }
1466
1467
1468 int compareAtomsSize = comparisonAtoms.length;
1469 int compareAtomsSize2 = comparisonAtoms2.length;
1470 if(logger.isLoggable(Level.FINE)){
1471 logger.fine(format(" Z'1: %2d Z'2: %2d\n" +
1472 "Base Compare Size: %2d Target Compare Size: %2d", z1, z2, compareAtomsSize, compareAtomsSize2));
1473 }
1474
1475
1476
1477
1478 if (z1 > 1) {
1479 compareAtomsSize /= z1;
1480 }
1481 if (z2 > 1) {
1482 compareAtomsSize2 /= z2;
1483 }
1484 if(z1 != z2){
1485 logger.warning(format(" Z1 (%2d) does not equal Z2 (%2d).", z1, z2));
1486 }
1487
1488
1489 final boolean useSym = printSym >= 0.0;
1490 if (useSym && (z1 > 1 || z2 > 1)) {
1491 matchTol = SYM_TOLERANCE;
1492 }
1493 if (useSym && z1 != z2) {
1494 logger.warning(
1495 " Comparisons to determine symmetry should have the same number of molecules in the asymmetric unit.");
1496 }
1497
1498 Crystal baseCrystal = baseAssembly.getCrystal().getUnitCell();
1499 Crystal targetCrystal = targetFilter.getActiveMolecularSystem().getCrystal().getUnitCell();
1500
1501 int baseSearchValue = (baseCrystal.spaceGroup.respectsChirality()) ? z1 : 2 * z1;
1502 int targetSearchValue = (targetCrystal.spaceGroup.respectsChirality()) ? z2 : 2 * z2;
1503
1504
1505 final int nCoords = compareAtomsSize * 3;
1506
1507 if (this.comparisonAtoms == null || this.comparisonAtoms.length != compareAtomsSize) {
1508 this.comparisonAtoms = new int[compareAtomsSize];
1509 arraycopy(comparisonAtoms, 0, this.comparisonAtoms, 0, compareAtomsSize);
1510 }
1511
1512 int massIndex = 0;
1513 final double[] mass = new double[compareAtomsSize];
1514 for (Integer value : this.comparisonAtoms) {
1515 Atom atom = atoms1[value];
1516 final double m = atom.getMass();
1517 mass[massIndex++] = (massWeighted) ? m : 1.0;
1518 }
1519
1520 massIndex = 0;
1521 final double[] mass2 = new double[compareAtomsSize2];
1522 for (Integer value : comparisonAtoms2) {
1523 if (massIndex == compareAtomsSize2) {
1524
1525 break;
1526 }
1527 Atom atom = atoms2[value];
1528 final double m = atom.getMass();
1529 mass2[massIndex++] = (massWeighted) ? m : 1.0;
1530 }
1531
1532 if (!Arrays.equals(mass, mass2)) {
1533 logger.warning(" Mass arrays do not match. Check atom ordering or size.");
1534 if (logger.isLoggable(Level.FINE)) {
1535 logger.fine(format(" Number Base Masses: %2d Number Target Masses: %2d\n Checking %2d masses from both systems.", mass.length, mass2.length, compareAtomsSize));
1536 for (int i = 0; i < compareAtomsSize; i++) {
1537 if (mass[i] != mass2[i]) {
1538 logger.fine(format(" Index: %d Mass 1: %4.4f Mass 2: %4.4f", i + 1, mass[i], mass2[i]));
1539 }
1540 }
1541 }
1542 }
1543
1544
1545 if (massN == null) {
1546 massN = (nAU > 3) ? new double[compareAtomsSize * nAU] : new double[compareAtomsSize * 3];
1547 for (int i = 0; i < nAU; i++) {
1548 arraycopy(mass, 0, massN, i * compareAtomsSize, compareAtomsSize);
1549 }
1550 }
1551
1552 if (massSum == 0) {
1553 for (int i = 0; i < compareAtomsSize; i++) {
1554 massSum += massN[i];
1555 }
1556 }
1557
1558 if (machineLearning) {
1559 saveClusters = 1;
1560 }
1561 if (saveClusters > 2) {
1562 saveClusters = 0;
1563 logger.info(" Save flag specified incorrectly (1:PDB; 2:XYZ). Not saving files.");
1564 }
1565
1566 if (logger.isLoggable(Level.FINER)) {
1567 if (linkage == 0) {
1568 logger.finer(" Single linkage will be used.");
1569 } else if (linkage == 2) {
1570 logger.finer(" Complete linkage will be used.");
1571 } else if (linkage == 1) {
1572 logger.finer(" Average linkage will be used.");
1573 }
1574 }
1575 if (linkage != 1 && linkage != 0 && linkage != 2) {
1576 logger.warning(
1577 "Prioritization method specified incorrectly (--pm {0, 1, 2}). Using default of average linkage.");
1578 linkage = 1;
1579 }
1580
1581
1582 if (compareAtomsSize == 0 || compareAtomsSize2 == 0) {
1583 logger.severe(" No atoms were selected for comparison.");
1584 return runningStatistics;
1585 }
1586 logger.info(format("\n %d atoms will be used for the PAC RMSD out of %d in first crystal.",
1587 compareAtomsSize * z1, nAtoms));
1588 logger.info(format(" %d atoms will be used for the PAC RMSD out of %d in second crystal.\n",
1589 compareAtomsSize2 * z2, nAtoms2));
1590
1591
1592 rmsdLabel = format("RMSD_%d", nAU);
1593
1594
1595 double minTime = Double.MAX_VALUE;
1596 double maxTime = -Double.MIN_VALUE;
1597
1598 if (!lowMemory) {
1599
1600 int size = (int) ceil((double) targetSize / (double) numProc);
1601 atomCache = new Atom[size][nAtoms2];
1602 fileCache = new File[size];
1603 nameCache = new String[size];
1604 bondCache = new ArrayList<>();
1605 for (int i = 0; i < size; i++) {
1606 bondCache.add(new ArrayList<>());
1607 }
1608 forceFieldCache = new ForceField[size];
1609 crystalCache = new Crystal[size];
1610
1611
1612 for (int column = restartColumn; column < targetSize; column++) {
1613 final int targetRank = column % numProc;
1614 if (targetRank == rank) {
1615 final int assemblyNum = column / numProc;
1616 MolecularAssembly currentAssembly = targetFilter.getActiveMolecularSystem();
1617 Atom[] arrayAtom = currentAssembly.getAtomArray();
1618 final int atomSize = arrayAtom.length;
1619 for (int i = 0; i < atomSize; i++) {
1620 final double[] xyz = new double[3];
1621 arrayAtom[i].getXYZ(xyz);
1622 atomCache[assemblyNum][i] = new Atom(i, arrayAtom[i].getName(),
1623 arrayAtom[i].getAtomType(), xyz);
1624 }
1625 List<Bond> currentBonds = currentAssembly.getBondList();
1626 List<Bond> currentBondCache = bondCache.get(assemblyNum);
1627 for (Bond b : currentBonds) {
1628 if(!currentBondCache.contains(b)) {
1629 currentBondCache.add(b);
1630 }
1631 }
1632 ForceField currentForcefield = currentAssembly.getForceField();
1633 fileCache[assemblyNum] = new File(currentAssembly.getFile().getName());
1634 forceFieldCache[assemblyNum] = new ForceField(currentForcefield.getProperties());
1635 nameCache[assemblyNum] = currentAssembly.getName();
1636 Crystal cXtal = currentAssembly.getCrystal().getUnitCell();
1637 crystalCache[assemblyNum] = new Crystal(cXtal.a, cXtal.b, cXtal.c, cXtal.alpha, cXtal.beta,
1638 cXtal.gamma, cXtal.spaceGroup.pdbName);
1639 }
1640 targetFilter.readNext(false, false, (column + 1) % numProc == rank);
1641 }
1642 }
1643
1644 if (logger.isLoggable(Level.FINER)) {
1645 logResources();
1646 }
1647
1648
1649 File saveLocation1 = new File(removeExtension(baseAssembly.getFile().getAbsoluteFile().getAbsolutePath()) + "_lt1_" + save + ".arc");
1650 File saveLocation2 = new File(removeExtension(targetAssembly.getFile().getAbsoluteFile().getAbsolutePath()) + "_lt2_" + save + ".arc");
1651 if(save >= 0.0) {
1652 logger.info(format(" Saving trajectories less than %7.4f to :\n %s\n %s", save, saveLocation1.getName(), saveLocation2.getName()));
1653 }
1654
1655
1656 File feDirectory = null;
1657 if(createFE) {
1658 feDirectory = new File(removeExtension(baseAssembly.getFile().getAbsoluteFile().getParent()) + File.separator + "FE" + File.separator);
1659 if(!feDirectory.exists()){
1660 try {
1661 if(feDirectory.mkdir()){
1662 logger.info(format(" Base directory: %s", baseAssembly.getFile().getAbsoluteFile().getParent()));
1663 logger.info(format(" Free energy path: %s", feDirectory.getAbsolutePath()));
1664 }
1665 }catch(Exception ex){
1666 logger.warning(ex + Utilities.stackTraceToString(ex));
1667 }
1668 }
1669 }
1670
1671
1672 for (int row = restartRow; row < baseSize; row++) {
1673
1674 fill(myDistances, -8.0);
1675 int myIndex = 0;
1676
1677 baseAssembly = baseFilter.getActiveMolecularSystem();
1678 baseCrystal = baseAssembly.getCrystal().getUnitCell();
1679 atoms1 = baseAssembly.getAtomArray();
1680
1681 int baseFormat = 1;
1682 while(baseSize/(10*baseFormat) >= 10){
1683 baseFormat++;
1684 }
1685 if(baseSize > 10){
1686 baseFormat++;
1687 }
1688
1689 File baseFE = null;
1690 if(createFE) {
1691 baseFE = new File(feDirectory.getAbsolutePath() + File.separator + format("%0"+baseFormat+"d", row) + File.separator);
1692 if (!baseFE.exists()) {
1693 try {
1694 if (baseFE.mkdir()) {
1695 logger.info(format(" Base free energy path: %s", baseFE.getAbsolutePath()));
1696 }
1697 } catch (Exception ex) {
1698 logger.warning(ex + Utilities.stackTraceToString(ex));
1699 }
1700 }
1701 }
1702
1703 final double[] reducedBaseCoords = reduceSystem(atoms1, comparisonAtoms);
1704 final double baseDensity = baseCrystal.getUnitCell().getDensity(baseAssembly.getMass());
1705 if (baseCrystal == null || baseCrystal.aperiodic()) {
1706 logger.warning(" File " + baseAssembly.getFile().getName() + ": (" + baseAssembly.getName() + ") does not have a crystal. Consider using Superpose command.\n");
1707 continue;
1708 }
1709
1710
1711
1712
1713
1714 if (logger.isLoggable(Level.FINER)) {
1715 logger.finer(format(" Unit Cell Volume: (Base) %4.2f (Target) %4.2f", baseCrystal.volume,
1716 targetCrystal.volume));
1717 logger.finer(format(" Unit Cell Symm Ops: (Base) %d (Target) %d", baseCrystal.getNumSymOps(),
1718 targetCrystal.getNumSymOps()));
1719 logger.finer(format(" Z'': (Base) %d (Target) %d", z1, z2));
1720 }
1721
1722
1723
1724
1725
1726
1727
1728
1729 final int[] baseLMN = new int[3];
1730 double inflationFactorOrig = inflationFactor;
1731 baseXYZoriginal = generateInflatedSphere(baseCrystal.getUnitCell(), reducedBaseCoords, z1, nAU,
1732 linkage, mass, baseLMN, strict, baseSymOps, baseDistMap, inflationFactor);
1733 if (inflationFactorOrig != inflationFactor) {
1734 logger.warning(format(
1735 " Default replicates crystal was too small for comparison. Increasing inflation factor from %9.3f to %9.3f",
1736 inflationFactor, inflationFactorOrig));
1737 }
1738 int nBaseCoords = baseXYZoriginal.length;
1739 baseXYZ = new double[nBaseCoords];
1740 arraycopy(baseXYZoriginal, 0, baseXYZ, 0, nBaseCoords);
1741
1742
1743 int nBaseMols = nBaseCoords / (compareAtomsSize * 3);
1744 if (baseAUDist == null || baseAUDist.length != nBaseMols) {
1745 baseAUDist = new DoubleIndexPair[nBaseMols];
1746 }
1747 if (baseCoM == null || baseCoM.length != nBaseMols) {
1748 baseCoM = new double[3][nBaseMols][3];
1749 }
1750 centerOfMass(baseCoM[0], baseXYZ, massN, massSum, compareAtomsSize);
1751 prioritizeReplicates(baseXYZ, baseCoM[0], compareAtomsSize, baseAUDist, 0,
1752 linkage);
1753
1754 for (int column = restartColumn; column < targetSize; column++) {
1755 final int targetRank = column % numProc;
1756 if (targetRank == rank) {
1757 stringBuilder.setLength(0);
1758 long time = -System.nanoTime();
1759 final int assemblyNum = column / numProc;
1760 if (!lowMemory) {
1761 targetCrystal = crystalCache[assemblyNum];
1762 atoms2 = atomCache[assemblyNum];
1763 } else {
1764 targetAssembly = targetFilter.getActiveMolecularSystem();
1765 targetCrystal = targetAssembly.getCrystal().getUnitCell();
1766 atoms2 = targetAssembly.getAtomArray();
1767 }
1768 if (targetCrystal == null || targetCrystal.aperiodic()) {
1769 if (!lowMemory) {
1770 logger.warning(" File " + fileCache[assemblyNum].getName() + ": (" + nameCache[assemblyNum] + ") does not have a crystal. Consider using Superpose command.\n");
1771 } else {
1772 logger.warning(" File " + baseAssembly.getFile().getName() + ": (" + targetAssembly.getName() + ") does not have a crystal. Consider using Superpose command.\n");
1773 }
1774 continue;
1775 }
1776
1777 int targetFormat = 1;
1778 while (targetSize/(10*targetFormat) >= 10) {
1779 targetFormat++;
1780 }
1781 if (baseSize > 10) {
1782 targetFormat++;
1783 }
1784 double rmsd;
1785
1786 boolean densityCheck;
1787 if (isSymmetric && row == column) {
1788 stringBuilder.append(
1789 format("\n Comparing Model %4d (%s) of %s\n with Model %4d (%s) of %s\n",
1790 row + 1, baseCrystal.toShortString(), baseLabel, column + 1,
1791 targetCrystal.toShortString(), targetLabel));
1792
1793 rmsd = 0.0;
1794
1795 stringBuilder.append(format("\n PAC %s: %12s %7.4f A\n", rmsdLabel, "", rmsd));
1796 } else if (isSymmetric && row > column) {
1797
1798 rmsd = -10.0;
1799 } else {
1800 File targetFE = null;
1801 if (createFE) {
1802 targetFE = new File(baseFE.getAbsolutePath() + File.separator + format("%0"+targetFormat+"d",column) + File.separator);
1803 if (!targetFE.exists()) {
1804 try {
1805 if (targetFE.mkdir()) {
1806 logger.info(format(" Target free energy path: %s", targetFE.getAbsolutePath()));
1807 }
1808 } catch (Exception ex) {
1809 logger.warning(ex.toString() + Utilities.stackTraceToString(ex));
1810 }
1811 }
1812 }
1813
1814 stringBuilder.append(
1815 format("\n Comparing Model %4d (%s) of %s\n with Model %4d (%s) of %s\n",
1816 row + 1, baseCrystal.toShortString(), baseLabel, column + 1,
1817 targetCrystal.toShortString(), targetLabel));
1818
1819
1820 final double[] reducedTargetCoords = reduceSystem(atoms2, comparisonAtoms2);
1821
1822
1823
1824 final int[] targetLMN = new int[3];
1825 inflationFactorOrig = inflationFactor;
1826 targetXYZoriginal = generateInflatedSphere(targetCrystal.getUnitCell(),
1827 reducedTargetCoords, z2, nAU, linkage, mass2, targetLMN, strict, targetSymOps,
1828 targetDistMap, inflationFactor);
1829 if (inflationFactorOrig != inflationFactor) {
1830 logger.warning(format(
1831 " Default replicates crystal was too small for comparison. Increasing inflation factor from %9.3f to %9.3f",
1832 inflationFactor, inflationFactorOrig));
1833 }
1834 int nTargetCoords = targetXYZoriginal.length;
1835 targetXYZ = new double[nTargetCoords];
1836 arraycopy(targetXYZoriginal, 0, targetXYZ, 0, nTargetCoords);
1837
1838
1839 double densityMass = 0;
1840 if (!lowMemory) {
1841 for (Atom atom : atomCache[assemblyNum]) {
1842 densityMass += atom.getMass();
1843 }
1844 } else {
1845 densityMass = targetFilter.getActiveMolecularSystem().getMass();
1846 }
1847 final double targetDensity = targetCrystal.getUnitCell().getDensity(densityMass);
1848 if (logger.isLoggable(Level.FINER)) {
1849 stringBuilder.append(
1850 format("\n Base Density: %4.4f Target Density: %4.4f\n", baseDensity,
1851 targetDensity));
1852 }
1853
1854 densityCheck =
1855 (crystalPriority == 1) ? baseDensity < targetDensity : baseDensity > targetDensity;
1856
1857
1858 int nTargetMols = targetXYZ.length / (compareAtomsSize2 * 3);
1859 if (targetAUDist == null || targetAUDist.length != nTargetMols) {
1860 targetAUDist = new DoubleIndexPair[nTargetMols];
1861 }
1862 if (targetCoM == null || targetCoM.length != nTargetMols) {
1863 targetCoM = new double[nTargetMols][3];
1864 }
1865
1866 centerOfMass(targetCoM, targetXYZ, massN, massSum, compareAtomsSize);
1867 prioritizeReplicates(targetXYZ, targetCoM, compareAtomsSize,
1868 targetAUDist, 0, linkage);
1869
1870
1871 File file1;
1872 File file2;
1873 String name1;
1874 String name2;
1875 List<Bond> bondList1;
1876 List<Bond> bondList2;
1877 ForceField forceField1;
1878 ForceField forceField2;
1879 arraycopy(baseXYZoriginal, 0, baseXYZ, 0, nBaseCoords);
1880 if (densityCheck || crystalPriority == 2) {
1881
1882 atoms1 = baseAssembly.getAtomArray();
1883 file1 = baseAssembly.getFile();
1884 name1 = baseAssembly.getName();
1885 bondList1 = baseAssembly.getBondList();
1886 forceField1 = baseAssembly.getForceField();
1887 if (!lowMemory) {
1888 atoms2 = atomCache[assemblyNum];
1889 file2 = fileCache[assemblyNum];
1890 name2 = nameCache[assemblyNum];
1891 bondList2 = bondCache.get(assemblyNum);
1892 forceField2 = forceFieldCache[assemblyNum];
1893 } else {
1894 MolecularAssembly mobileAssembly = targetFilter.getActiveMolecularSystem();
1895 atoms2 = mobileAssembly.getAtomArray();
1896 file2 = mobileAssembly.getFile();
1897 name2 = mobileAssembly.getName();
1898 bondList2 = mobileAssembly.getBondList();
1899 forceField2 = mobileAssembly.getForceField();
1900 }
1901 } else {
1902
1903 double[] tempXYZ = new double[nBaseCoords];
1904 arraycopy(baseXYZ, 0, tempXYZ, 0, nBaseCoords);
1905 baseXYZ = new double[nTargetCoords];
1906 arraycopy(targetXYZ, 0, baseXYZ, 0, nTargetCoords);
1907 targetXYZ = new double[nBaseCoords];
1908 arraycopy(tempXYZ, 0, targetXYZ, 0, nBaseCoords);
1909 arraycopy(baseXYZoriginal, 0, tempXYZ, 0, nBaseCoords);
1910 baseXYZoriginal = new double[nTargetCoords];
1911 arraycopy(targetXYZoriginal, 0, baseXYZoriginal, 0, nTargetCoords);
1912 targetXYZoriginal = new double[nBaseCoords];
1913 arraycopy(tempXYZ, 0, targetXYZoriginal, 0, nBaseCoords);
1914 tempXYZ = new double[nCoords];
1915 arraycopy(reducedBaseCoords, 0, tempXYZ, 0, nCoords);
1916 arraycopy(reducedTargetCoords, 0, reducedBaseCoords, 0, nCoords);
1917 arraycopy(tempXYZ, 0, reducedTargetCoords, 0, nCoords);
1918 ArrayList<SymOp> also = new ArrayList<>(baseSymOps);
1919 baseSymOps = new ArrayList<>(targetSymOps);
1920 targetSymOps = new ArrayList<>(also);
1921 ArrayList<Integer> ali = new ArrayList<>(baseDistMap);
1922 baseDistMap = new ArrayList<>(targetDistMap);
1923 targetDistMap = new ArrayList<>(ali);
1924 DoubleIndexPair[] tempDIP = new DoubleIndexPair[nBaseMols];
1925 arraycopy(baseAUDist, 0, tempDIP, 0, nBaseMols);
1926 baseAUDist = new DoubleIndexPair[nTargetMols];
1927 arraycopy(targetAUDist, 0, baseAUDist, 0, nTargetMols);
1928 targetAUDist = new DoubleIndexPair[nBaseMols];
1929 arraycopy(tempDIP, 0, targetAUDist, 0, nBaseMols);
1930 double[][] tempDD = new double[nBaseMols][3];
1931 arraycopy(baseCoM[0], 0, tempDD, 0, nBaseMols);
1932 baseCoM = new double[3][nTargetMols][3];
1933 arraycopy(targetCoM, 0, baseCoM[0], 0, nTargetMols);
1934 targetCoM = new double[nBaseMols][3];
1935 arraycopy(tempDD, 0, targetCoM, 0, nBaseMols);
1936 atoms2 = baseAssembly.getAtomArray();
1937 file2 = baseAssembly.getFile();
1938 name2 = baseAssembly.getName();
1939 bondList2 = baseAssembly.getBondList();
1940 forceField2 = baseAssembly.getForceField();
1941 int[] tempAtoms = comparisonAtoms.clone();
1942 comparisonAtoms = comparisonAtoms2.clone();
1943 comparisonAtoms2 = tempAtoms.clone();
1944 int temp = z1;
1945 z1 = z2;
1946 z2 = temp;
1947 temp = baseSearchValue;
1948 baseSearchValue = targetSearchValue;
1949 targetSearchValue = temp;
1950 temp = nBaseCoords;
1951 nBaseCoords = nTargetCoords;
1952 nTargetCoords = temp;
1953 temp = nBaseMols;
1954 nBaseMols = nTargetMols;
1955 nTargetMols = temp;
1956 if (!lowMemory) {
1957 atoms1 = atomCache[assemblyNum];
1958 file1 = fileCache[assemblyNum];
1959 name1 = nameCache[assemblyNum];
1960 bondList1 = bondCache.get(assemblyNum);
1961 forceField1 = forceFieldCache[assemblyNum];
1962 } else {
1963 MolecularAssembly staticAssembly = targetFilter.getActiveMolecularSystem();
1964 atoms1 = staticAssembly.getAtomArray();
1965 file1 = staticAssembly.getFile();
1966 name1 = staticAssembly.getName();
1967 bondList1 = staticAssembly.getBondList();
1968 forceField1 = staticAssembly.getForceField();
1969 }
1970 }
1971
1972 allocateVariables(nAU, nCoords, z1, z2, useSym);
1973
1974 if (useSym) {
1975 for (int i = 0; i < z1; i++) {
1976 arraycopy(reducedBaseCoords, i * compareAtomsSize * 3, baseAUoriginal[i], 0,
1977 compareAtomsSize * 3);
1978 }
1979 for (int i = 0; i < z2; i++) {
1980 arraycopy(reducedTargetCoords, i * compareAtomsSize2 * 3, targetAUoriginal[i], 0,
1981 compareAtomsSize2 * 3);
1982 }
1983 }
1984
1985 if (logger.isLoggable(Level.FINER)) {
1986 logger.finer(
1987 format(" %3d Molecules in Base Crystal \t %3d Molecules in Target Crystal",
1988 (nBaseCoords / 3) / compareAtomsSize, (nTargetCoords / 3) / compareAtomsSize));
1989 }
1990
1991 if (logger.isLoggable(Level.FINEST) && saveClusters > 0) {
1992 saveAssembly(file1, name1, bondList1, atoms1, forceField1, baseXYZoriginal,
1993 comparisonAtoms, nBaseCoords / 3, "_c1_rep", -1, saveClusters);
1994 saveAssembly(file2, name2, bondList2, atoms2, forceField2, targetXYZoriginal,
1995 comparisonAtoms2, nTargetCoords / 3, "_c2_rep", -1, saveClusters);
1996 }
1997
1998 rmsd = compare(file1, name1, bondList1, atoms1, forceField1, file2, name2, bondList2,
1999 atoms2, forceField2, z1, z2, compareAtomsSize, nAU, baseSearchValue,
2000 targetSearchValue, matchTol, row * targetSize + column, strict, saveClusters,
2001 machineLearning, linkage, inertia, gyrationComponents);
2002 time += System.nanoTime();
2003 final double timeSec = time * 1.0e-9;
2004
2005 if (timeSec < minTime) {
2006 minTime = timeSec;
2007 }
2008 if (timeSec > maxTime) {
2009 maxTime = timeSec;
2010 }
2011
2012 final double avgGyration = (gyrations[0] + gyrations[1]) / 2;
2013 final double diff = max(gyrations[0], gyrations[1]) - avgGyration;
2014 stringBuilder.append(
2015 format("\n PAC %7s: %12s %7.4f A (%5.3f sec) G(r) %7.4f A +/- %7.4f\n", rmsdLabel,
2016 "", rmsd, timeSec, avgGyration, diff));
2017 if (inertia) {
2018 stringBuilder.append("""
2019
2020 Moments of Inertia and Principle Axes for first crystal:
2021 Moments (amu Ang^2): \t X-, Y-, and Z-Components of Axes:
2022 """);
2023 for (int i = 1; i < 4; i++) {
2024 stringBuilder.append(
2025 format(" %16.3f %12.6f %12.6f %12.6f\n", bestBaseMandV[i - 1][0],
2026 bestBaseMandV[0][i], bestBaseMandV[1][i], bestBaseMandV[2][i]));
2027 }
2028 stringBuilder.append("""
2029 Moments of Inertia and Principle Axes for second crystal:
2030 Moments (amu Ang^2): \t X-, Y-, and Z-Components of Axes:
2031 """);
2032 for (int i = 1; i < 4; i++) {
2033 stringBuilder.append(
2034 format(" %16.3f %12.6f %12.6f %12.6f\n", bestTargetMandV[i - 1][0],
2035 bestTargetMandV[0][i], bestTargetMandV[1][i], bestTargetMandV[2][i]));
2036 }
2037 }
2038 if (gyrationComponents) {
2039
2040 double Rmax = max(max(bestBaseRg[0], bestBaseRg[1]), bestBaseRg[2]);
2041 Rmax *= Rmax;
2042 double Rmed = max(min(bestBaseRg[0], bestBaseRg[1]), bestBaseRg[2]);
2043 Rmed *= Rmed;
2044 double Rmin = min(min(bestBaseRg[0], bestBaseRg[1]), bestBaseRg[2]);
2045 Rmin *= Rmin;
2046 double Rg = Rmax + Rmed + Rmin;
2047
2048 double asphericity = Rmax - 0.5 * (Rmin + Rmed);
2049 double acylindricity = Rmed - Rmin;
2050 double anisotropy = (asphericity * asphericity + 0.75 * acylindricity * acylindricity);
2051 double totalMass = Arrays.stream(mass).sum();
2052
2053 double volume = totalMass / max(baseCrystal.getDensity(massSum),
2054 targetCrystal.getDensity(massSum));
2055 double targetRadius = cbrt(0.75 / PI * volume);
2056 stringBuilder.append(format(" Base Radius Metric: %7.4f", avgGyration / targetRadius));
2057 if (logger.isLoggable(Level.FINER)) {
2058 stringBuilder.append(
2059 format("\n Target Base Radius: %9.3f Å\n", targetRadius));
2060 stringBuilder.append(
2061 format(" Asphericity: (%6.3f Å^2) %9.3f\n", asphericity, asphericity / Rmax));
2062 stringBuilder.append(format(" Acylindricity: (%6.3f Å^2) %9.3f\n", acylindricity,
2063 acylindricity / Rmax));
2064 stringBuilder.append(
2065 format(" Anisotropy: (%6.3f Å) %9.3f\n", sqrt(sqrt(anisotropy)),
2066 anisotropy / (Rg * Rg)));
2067 stringBuilder.append(
2068 format(" Ryz %9.3fÅ Rxz %9.3fÅ Rxy %9.3fÅ\n", bestBaseRg[0], bestBaseRg[1],
2069 bestBaseRg[2]));
2070 }
2071
2072 Rmax = max(max(bestTargetRg[0], bestTargetRg[1]), bestTargetRg[2]);
2073 Rmax *= Rmax;
2074 Rmed = max(min(bestTargetRg[0], bestTargetRg[1]), bestTargetRg[2]);
2075 Rmed *= Rmed;
2076 Rmin = min(min(bestTargetRg[0], bestTargetRg[1]), bestTargetRg[2]);
2077 Rmin *= Rmin;
2078 Rg = Rmax + Rmed + Rmin;
2079
2080 asphericity = Rmax - 0.5 * (Rmin + Rmed);
2081 acylindricity = Rmed - Rmin;
2082 anisotropy = (asphericity * asphericity + 0.75 * acylindricity * acylindricity);
2083 totalMass = Arrays.stream(mass).sum();
2084 volume = totalMass / targetDensity;
2085 targetRadius = cbrt(0.75 / PI * volume);
2086 if (logger.isLoggable(Level.FINER)) {
2087 stringBuilder.append(
2088 format("\n Target Target Radius: %9.3f Å\n", targetRadius));
2089 stringBuilder.append(
2090 format(" Asphericity: (%6.3f Å) %9.3f\n", asphericity, asphericity / Rmax));
2091 stringBuilder.append(format(" Acylindricity: (%6.3f Å) %9.3f\n", acylindricity,
2092 acylindricity / Rmax));
2093 stringBuilder.append(
2094 format(" Anisotropy: (%6.3f Å) %9.3f\n", sqrt(sqrt(anisotropy)),
2095 anisotropy / (Rg * Rg)));
2096 stringBuilder.append(
2097 format(" Ryz %9.3fÅ Rxz %9.3fÅ Rxy %9.3fÅ\n", bestTargetRg[0],
2098 bestTargetRg[1], bestTargetRg[2]));
2099 }
2100 }
2101 if (logger.isLoggable(Level.FINER)) {
2102 stringBuilder.append(
2103 format("\n Gyration Crystal 1 (%s): %7.4f Crystal 2 (%s): %7.4f.\n", name1,
2104 gyrations[0], name2, gyrations[1]));
2105 }
2106 if (useSym) {
2107 StringBuilder sbSO = new StringBuilder(
2108 format("\n Sym Op to move %s onto %s:\nsymop ", name2, name1));
2109 StringBuilder sbInv = new StringBuilder(
2110 format("\n Inverted Sym Op to move %s onto %s:\nsymop ", name1, name2));
2111 final int[] mol1 = baseAssembly.getMoleculeNumbers();
2112 final int[] mol2 = targetAssembly.getMoleculeNumbers();
2113 for (int i = 0; i < z1; i++) {
2114 for (int j = 0; j < z2; j++) {
2115 if (bestTargetTransformSymOp[i][j] != null) {
2116 bestTargetTransformSymOp[i][j] = bestTargetTransformSymOp[i][j].prepend(
2117 bestTargetSymOp[i][j]);
2118
2119
2120
2121 bestBaseTransformSymOp[i][j] = bestBaseTransformSymOp[i][j].prepend(
2122 bestBaseSymOp[i][j]);
2123 bestTargetTransformSymOp[i][j] = bestTargetTransformSymOp[i][j].append(
2124 invertSymOp(bestBaseTransformSymOp[i][j]));
2125
2126 if (strict || logger.isLoggable(Level.FINEST) || z1 != z2 || i == j) {
2127 SymOp inverted = invertSymOp(bestTargetTransformSymOp[i][j]);
2128 if (logger.isLoggable(Level.FINEST)) {
2129 stringBuilder.append(
2130 format("\n Sym Op to move %s (%2d) onto %s (%2d):\n", name2, j, name1,
2131 i)).append(bestTargetTransformSymOp[i][j]).append("\n");
2132 stringBuilder.append(
2133 format("\n\n Inverted Sym Op to move %s (%2d) onto %s (%2d):\n", name1,
2134 i, name2, j)).append(inverted).append("\n");
2135 }
2136 ArrayList<Integer> mol1list = new ArrayList<>();
2137 ArrayList<Integer> mol2list = new ArrayList<>();
2138 for (int k = 0; k < nAtoms; k++) {
2139 if ((z1 == 1 || i == mol1[k]) && ArrayUtils.contains(comparisonAtoms, k)) {
2140 mol1list.add(k);
2141 }
2142 if ((z2 == 1 || j == mol2[k]) && ArrayUtils.contains(comparisonAtoms2, k)) {
2143 mol2list.add(k);
2144 }
2145 }
2146 final int[] mol1arr = mol1list.stream().mapToInt(Integer::intValue).toArray();
2147 final int[] mol2arr = mol2list.stream().mapToInt(Integer::intValue).toArray();
2148 if(mol1arr.length < 3 || mol2arr.length < 3){
2149 logger.warning(" Less than 3 atoms were included for this sym op which likely leads to poor multipole overlap.");
2150 }
2151 sbSO.append(format(" %s %s", writeAtomRanges(mol1arr), writeAtomRanges(mol2arr)))
2152 .append(asMatrixString(bestTargetTransformSymOp[i][j]));
2153 sbInv.append(format(" %s %s", writeAtomRanges(mol2arr), writeAtomRanges(mol1arr))).append(asMatrixString(inverted));
2154 if (i + 1 < z1 || j + 1 < z2) {
2155 sbSO.append("\\\n");
2156 sbInv.append("\\\n");
2157 }
2158 if(createFE){
2159
2160 StringBuilder fName1 = new StringBuilder();
2161 fName1.append(targetFE.getAbsolutePath()).append(File.separator).append(removeExtension(file1.getName())).append("_base");
2162 File newPropertyFile = new File( fName1.toString() + ".properties");
2163 if(!newPropertyFile.exists()){
2164 StringBuilder propertyName = new StringBuilder();
2165 propertyName.append(removeExtension(file1.getName())).append(".properties");
2166 if(!copyFile(new File(propertyName.toString()), newPropertyFile)){
2167 logger.warning(" Error copying PROPERTIES file to FE directory (currently not implemented for KEY/PRM).");
2168 }
2169 }
2170
2171 File newCoordFile = new File(fName1.toString() + ".xyz");
2172 if(!newCoordFile.exists()){
2173 MolecularAssembly assembly = ((densityCheck || crystalPriority == 2)? baseFilter.getActiveMolecularSystem() : targetFilter.getActiveMolecularSystem());
2174
2175 File originalFile = assembly.getFile();
2176 String originalName = assembly.getName();
2177 XYZFilter xyzFilter = new XYZFilter(newCoordFile, assembly, assembly.getForceField(), assembly.getProperties());
2178 xyzFilter.writeFile(newCoordFile,false);
2179 assembly.setFile(originalFile);
2180 assembly.setName(originalName);
2181 }
2182 StringBuilder fName2 = new StringBuilder();
2183 fName2.append(targetFE.getAbsolutePath()).append(File.separator).append(getBaseName(file2.getName())).append("_target");
2184 File newPropertyFile2 = new File(fName2.toString() + ".properties");
2185 if(!newPropertyFile2.exists()){
2186 StringBuilder propertyName = new StringBuilder();
2187 propertyName.append(getBaseName(file2.getName())).append(".properties");
2188 if(!copyFile(new File(propertyName.toString()), newPropertyFile2)){
2189 logger.warning(" Error copying PROPERTIES file to FE directory (currently not implemented for KEY/PRM).");
2190 }
2191 }
2192 File newCoordFile2 = new File(fName2.toString() + ".xyz");
2193 if(!newCoordFile2.exists()){
2194 MolecularAssembly assembly = (densityCheck || crystalPriority == 2)? targetFilter.getActiveMolecularSystem() : baseFilter.getActiveMolecularSystem();
2195
2196 File originalFile = assembly.getFile();
2197 String originalName = assembly.getName();
2198 XYZFilter xyzFilter = new XYZFilter(newCoordFile2, assembly, assembly.getForceField(), assembly.getProperties());
2199 xyzFilter.writeFile(newCoordFile2,false);
2200 assembly.setFile(originalFile);
2201 assembly.setName(originalName);
2202 }
2203 if(writeSym) {
2204 try (BufferedWriter bw = new BufferedWriter(new FileWriter(newPropertyFile,
2205 newPropertyFile.exists()))) {
2206 String label = fileContainsString(newPropertyFile, "symop");
2207 bw.write(format("%s %s %s", label, writeAtomRanges(mol1arr), writeAtomRanges(mol2arr)) + asMatrixString(bestTargetTransformSymOp[i][j]) + "\\\n");
2208 } catch (Exception ex) {
2209 logger.info(" Failed to append symmetry operator to target properties file.");
2210 logger.warning(ex + Utilities.stackTraceToString(ex));
2211 }
2212 try (BufferedWriter bw = new BufferedWriter(new FileWriter(newPropertyFile2,
2213 newPropertyFile2.exists()))) {
2214 String label = fileContainsString(newPropertyFile2, "symop");
2215 bw.write(format("%s %s %s", label, writeAtomRanges(mol2arr), writeAtomRanges(mol1arr)) + asMatrixString(inverted) + "\\\n");
2216 } catch (Exception ex) {
2217 logger.info(" Failed to append symmetry operator to base properties file.");
2218 logger.warning(ex + Utilities.stackTraceToString(ex));
2219 }
2220 }
2221 }
2222 if(!createFE && writeSym){
2223 File newFile = new File(removeExtension(file1.getAbsolutePath()) + ".properties");
2224 try (BufferedWriter bw = new BufferedWriter(new FileWriter(newFile,
2225 newFile.exists()))) {
2226 String label = fileContainsString(newFile, "symop");
2227 bw.write(format("%s %s %s", label, writeAtomRanges(mol1arr), writeAtomRanges(mol2arr)) + asMatrixString(bestTargetTransformSymOp[i][j]) + "\\\n");
2228 }catch(Exception ex){
2229 logger.info(" Failed to append symmetry operator to target properties file.");
2230 logger.warning(ex + Utilities.stackTraceToString(ex));
2231 }
2232 File newFile2 = new File(removeExtension(file2.getAbsolutePath()) + ".properties");
2233 try (BufferedWriter bw = new BufferedWriter(new FileWriter(newFile2,
2234 newFile2.exists()))) {
2235 String label = fileContainsString(newFile2, "symop");
2236 bw.write(format("%s %s %s", label, writeAtomRanges(mol2arr), writeAtomRanges(mol1arr)) + asMatrixString(inverted) + "\\\n");
2237 }catch(Exception ex){
2238 logger.info(" Failed to append symmetry operator to base properties file.");
2239 logger.warning(ex + Utilities.stackTraceToString(ex));
2240 }
2241 }
2242
2243 if(densityCheck || crystalPriority == 2){
2244 symOpsA.append(format(" %s %s", writeAtomRanges(mol1arr), writeAtomRanges(mol2arr))).append(asMatrixString(bestTargetTransformSymOp[i][j])).append("\\\n");
2245 symOpsB.append(format(" %s %s", writeAtomRanges(mol2arr), writeAtomRanges(mol1arr))).append(asMatrixString(inverted)).append("\\\n");
2246 }else{
2247 symOpsA.append(format(" %s %s", writeAtomRanges(mol2arr), writeAtomRanges(mol1arr))).append(asMatrixString(inverted)).append("\\\n");
2248 symOpsB.append(format(" %s %s", writeAtomRanges(mol1arr), writeAtomRanges(mol2arr))).append(asMatrixString(bestTargetTransformSymOp[i][j])).append("\\\n");
2249 }
2250
2251 if (logger.isLoggable(Level.FINER)) {
2252 stringBuilder.append(format(
2253 "\n Sym Op Application from Scratch: Target: %s (%2d) onto Base: %s (%2d)",
2254 name2, j, name1, i));
2255 final double[] symMol = new double[nCoords];
2256 for (int k = 0; k < compareAtomsSize; k++) {
2257 final int l = k * 3;
2258 final double[] xyz = new double[]{targetAUoriginal[j][l],
2259 targetAUoriginal[j][l + 1], targetAUoriginal[j][l + 2]};
2260 applyCartesianSymOp(xyz, xyz, bestTargetTransformSymOp[i][j]);
2261 symMol[l] = xyz[0];
2262 symMol[l + 1] = xyz[1];
2263 symMol[l + 2] = xyz[2];
2264 stringBuilder.append(format(
2265 "\n %8.3f %8.3f %8.3f moved to %8.3f %8.3f %8.3f compared to %8.3f %8.3f %8.3f",
2266 targetAUoriginal[j][l], targetAUoriginal[j][l + 1],
2267 targetAUoriginal[j][l + 2], symMol[l], symMol[l + 1], symMol[l + 2],
2268 baseAUoriginal[i][l], baseAUoriginal[i][l + 1],
2269 baseAUoriginal[i][l + 2]));
2270 }
2271 if (saveClusters > 0) {
2272 saveAssembly(file2, name2, bondList2, atoms2, forceField2, symMol,
2273 comparisonAtoms, 1, "_moved", 0, saveClusters);
2274 }
2275
2276 final double value = rmsd(baseAUoriginal[i], symMol, massN);
2277 final double[] symMol2 = new double[nCoords];
2278 stringBuilder.append("\n\n Sym Op Inverse Application:");
2279 for (int k = 0; k < compareAtomsSize; k++) {
2280 final int l = k * 3;
2281 final double[] xyz = new double[]{symMol[l], symMol[l + 1], symMol[l + 2]};
2282 applyCartesianSymOp(xyz, xyz, inverted);
2283 symMol2[l] = xyz[0];
2284 symMol2[l + 1] = xyz[1];
2285 symMol2[l + 2] = xyz[2];
2286 stringBuilder.append(format(
2287 "\n %8.3f %8.3f %8.3f moved to %8.3f %8.3f %8.3f compared to %8.3f %8.3f %8.3f",
2288 symMol[l], symMol[l + 1], symMol[l + 2], symMol2[l], symMol2[l + 1],
2289 symMol2[l + 2], targetAUoriginal[j][l], targetAUoriginal[j][l + 1],
2290 targetAUoriginal[j][l + 2]));
2291 }
2292 stringBuilder.append(format("\n\n Sym Op RMSD: %8.8f \n\n", value));
2293 }
2294 }
2295 } else {
2296 if (logger.isLoggable(Level.INFO)) {
2297 logger.info(format(" Symmetry Operator %2d %2d was filtered out. "
2298 + "Try using a single asymmetric unit (--na 1).", i, j));
2299 }
2300 }
2301 }
2302 }
2303 stringBuilder.append(sbSO.toString()).append("\n").append(sbInv.toString())
2304 .append("\n");
2305 }
2306
2307 if (!densityCheck && crystalPriority != 2) {
2308 baseXYZ = new double[nTargetCoords];
2309 arraycopy(targetXYZ, 0, baseXYZ, 0, nTargetCoords);
2310 baseXYZoriginal = new double[nTargetCoords];
2311 arraycopy(targetXYZoriginal, 0, baseXYZoriginal, 0, nTargetCoords);
2312 arraycopy(reducedTargetCoords, 0, reducedBaseCoords, 0, nCoords);
2313 baseSymOps = new ArrayList<>(targetSymOps);
2314 baseDistMap = new ArrayList<>(targetDistMap);
2315 int[] tempAtoms = comparisonAtoms.clone();
2316 comparisonAtoms = comparisonAtoms2.clone();
2317 comparisonAtoms2 = tempAtoms.clone();
2318 DoubleIndexPair[] tempDIP = new DoubleIndexPair[nBaseMols];
2319 arraycopy(baseAUDist, 0, tempDIP, 0, nBaseMols);
2320 baseAUDist = new DoubleIndexPair[nTargetMols];
2321 arraycopy(targetAUDist, 0, baseAUDist, 0, nTargetMols);
2322 targetAUDist = new DoubleIndexPair[nBaseMols];
2323 arraycopy(tempDIP, 0, targetAUDist, 0, nBaseMols);
2324 final double[][] tempDD = new double[nBaseMols][3];
2325 arraycopy(baseCoM[0], 0, tempDD, 0, nBaseMols);
2326 baseCoM = new double[3][nTargetMols][3];
2327 arraycopy(targetCoM, 0, baseCoM[0], 0, nTargetMols);
2328 targetCoM = new double[nBaseMols][3];
2329 arraycopy(tempDD, 0, targetCoM, 0, nBaseMols);
2330 int temp = z1;
2331 z1 = z2;
2332 z2 = temp;
2333 temp = baseSearchValue;
2334 baseSearchValue = targetSearchValue;
2335 targetSearchValue = temp;
2336 nBaseCoords = nTargetCoords;
2337 nBaseMols = nTargetMols;
2338 }
2339 if (hitTol >= 0.0 && hitTol > rmsd) {
2340 numberOfHits++;
2341 }
2342 }
2343 myDistances[myIndex] = rmsd;
2344 myIndex++;
2345 if (!stringBuilder.isEmpty()) {
2346 logger.info(stringBuilder.toString());
2347 }
2348 if (save >= 0.0 && rmsd < save) {
2349 XYZFilter xyzFilter = new XYZFilter(saveLocation1.getAbsoluteFile(), baseAssembly, baseAssembly.getForceField(),
2350 baseAssembly.getProperties());
2351 xyzFilter.writeFile(saveLocation1.getAbsoluteFile(), true);
2352 XYZFilter xyzFilter2 = new XYZFilter(saveLocation2.getAbsoluteFile(), targetAssembly, targetAssembly.getForceField(),
2353 targetAssembly.getProperties());
2354 xyzFilter2.writeFile(saveLocation2.getAbsoluteFile(), true);
2355 }
2356 }
2357 if (lowMemory) {
2358 targetFilter.readNext(false, false, (column + 1) % numProc == rank);
2359 }
2360 }
2361 restartColumn = 0;
2362 if (lowMemory) {
2363 targetFilter.readNext(true, false, 0 == rank);
2364 }
2365 baseFilter.readNext(false, false);
2366
2367
2368 gatherRMSDs(row, runningStatistics);
2369
2370
2371 if (rank == 0 && write) {
2372 final int firstColumn = (isSymmetric) ? row : 0;
2373 writeDistanceMatrixRow(pacFileName, distRow, firstColumn);
2374 }
2375 }
2376
2377 if (minTime < Double.MAX_VALUE) {
2378 logger.info(format("\n Minimum PAC Time: %7.4f", minTime));
2379 }
2380 if (logger.isLoggable(Level.FINE) && maxTime > Double.MIN_VALUE && maxTime != minTime) {
2381 logger.info(format(" Maximum PAC Time: %7.4f", maxTime));
2382 }
2383
2384 if (rank == 0 || logger.isLoggable(Level.FINER)) {
2385 final double min = runningStatistics.getMin();
2386 if (Double.MAX_VALUE - min < matchTol) {
2387 logger.warning(" SuperposeCrystals was not able to compare the provided structures.");
2388 if (logger.isLoggable(Level.FINE)) {
2389 logger.info(format(" RMSD Minimum: %8.6f", min));
2390 }
2391 } else {
2392 if(hitTol >= 0.0){
2393 logger.info(format(" Number of \"Hits\": %8d", numberOfHits));
2394 }
2395 logger.info(format(" RMSD Minimum: %8.6f", min));
2396 logger.info(format(" RMSD Maximum: %8.6f", runningStatistics.getMax()));
2397 logger.info(format(" RMSD Mean: %8.6f", runningStatistics.getMean()));
2398 final double variance = runningStatistics.getVariance();
2399 if (!Double.isNaN(variance)) {
2400 logger.info(format(" RMSD Variance: %8.6f", variance));
2401 }
2402 }
2403 }
2404
2405
2406 return runningStatistics;
2407 }
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417 private static boolean addLooseUnequal(List<Double> values, final double value, final double tol) {
2418 boolean found = false;
2419 for (Double dbl : values) {
2420 if (abs(dbl - value) < tol) {
2421 found = true;
2422 break;
2423 }
2424 }
2425 if (!found) {
2426 values.add(value);
2427 }
2428
2429 return !found;
2430 }
2431
2432
2433
2434
2435
2436
2437
2438
2439 public void addRotation(final double[][] rot, final double[][] totalTransform, final boolean prepend) {
2440 double[][] transform = new double[][]{{rot[0][0], rot[0][1], rot[0][2], 0.0},
2441 {rot[1][0], rot[1][1], rot[1][2], 0.0}, {rot[2][0], rot[2][1], rot[2][2], 0.0},
2442 {0.0, 0.0, 0.0, 1.0}};
2443 transform =
2444 (prepend) ? mat4Mat4(totalTransform, transform) : mat4Mat4(transform, totalTransform);
2445 final int length = totalTransform.length;
2446 for (int i = 0; i < length; i++) {
2447 arraycopy(transform[i], 0, totalTransform[i], 0, totalTransform[i].length);
2448 }
2449 }
2450
2451
2452
2453
2454
2455
2456
2457
2458 public static void addTranslation(final double[] translation, final double[][] totalTransform,
2459 final boolean prepend) {
2460 double[][] transform = new double[][]{{1.0, 0.0, 0.0, translation[0]},
2461 {0.0, 1.0, 0.0, translation[1]}, {0.0, 0.0, 1.0, translation[2]}, {0.0, 0.0, 0.0, 1.0}};
2462 transform =
2463 (prepend) ? mat4Mat4(totalTransform, transform) : mat4Mat4(transform, totalTransform);
2464 final int length = totalTransform.length;
2465 for (int i = 0; i < length; i++) {
2466 arraycopy(transform[i], 0, totalTransform[i], 0, totalTransform[i].length);
2467 }
2468 }
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478 private void allocateVariables(int nAU, int nCoords, int z1, int z2, boolean useSym){
2479 if (baseAU == null || baseAU.length != nCoords) {
2480 baseAU = new double[nCoords];
2481 }
2482 if (targetAU == null || targetAU.length != nCoords) {
2483 targetAU = new double[nCoords];
2484 }
2485 if (pairedAUs == null || pairedAUs.length != max(3, nAU)) {
2486 pairedAUs = new DoubleIndexPair[max(3, nAU)];
2487 }
2488 if (base3AUs == null || base3AUs.length != nCoords * 3) {
2489 base3AUs = new double[nCoords * 3];
2490 }
2491 if (target3AUs == null || target3AUs.length != nCoords * 3) {
2492 target3AUs = new double[nCoords * 3];
2493 }
2494 if (baseNAUs == null || baseNAUs[0].length != nAU * nCoords) {
2495 baseNAUs = new double[4][nAU * nCoords];
2496 }
2497 if (targetNAUs == null || targetNAUs.length != nAU * nCoords) {
2498 targetNAUs = new double[nAU * nCoords];
2499 }
2500 if (bestBaseNAUs == null || bestBaseNAUs.length != z1 || bestBaseNAUs[0].length != z2 || bestBaseNAUs[0][0].length != nAU * nCoords) {
2501 bestBaseNAUs = new double[z1][z2][nAU * nCoords];
2502 }
2503 if (bestTargetNAUs == null|| bestTargetNAUs.length != z1 || bestTargetNAUs[0].length != z2 || bestTargetNAUs[0][0].length != nAU * nCoords) {
2504 bestTargetNAUs = new double[z1][z2][nAU * nCoords];
2505 }
2506 if (useSym) {
2507 if (baseSymOps == null) {
2508 baseSymOps = new ArrayList<>();
2509 }
2510 if (targetSymOps == null) {
2511 targetSymOps = new ArrayList<>();
2512 }
2513 if (baseAUoriginal == null || baseAUoriginal.length != z1 || baseAUoriginal[0].length !=nCoords) {
2514 baseAUoriginal = new double[z1][nCoords];
2515 }
2516 if (targetAUoriginal == null || targetAUoriginal.length != z2 || targetAUoriginal[0].length !=nCoords) {
2517 targetAUoriginal = new double[z2][nCoords];
2518 }
2519 }
2520 }
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532 private static void centerOfMass(final double[][] centersOfMass, final double[] coords, final double[] mass,
2533 final double massSum, final int nAtoms) {
2534 final int nAU = coords.length / (nAtoms * 3);
2535 for (int i = 0; i < nAU; i++) {
2536 final int auIndex = i * nAtoms * 3;
2537 final double[] comI = new double[3];
2538 for (int j = 0; j < nAtoms; j++) {
2539 final int atomIndex = auIndex + j * 3;
2540 final double m = mass[j];
2541 for (int k = 0; k < 3; k++) {
2542 comI[k] += coords[atomIndex + k] * m;
2543 }
2544 }
2545 for (int j = 0; j < 3; j++) {
2546 comI[j] /= massSum;
2547 }
2548 centersOfMass[i] = comI;
2549 }
2550 }
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565 private static boolean checkInflatedSphere(final double[] xyz, final double[] massN, final double massSum,
2566 final int compareAtomsSize, final int nAU, final int linkage, final int[] startLMN, final ArrayList<SymOp> symOps,
2567 final ArrayList<Integer> indexOrder) {
2568
2569 final double[][] centerOfMass = new double[xyz.length / 3][3];
2570 centerOfMass(centerOfMass, xyz, massN, massSum, compareAtomsSize);
2571 DoubleIndexPair[] auDist = new DoubleIndexPair[xyz.length / 3 / compareAtomsSize];
2572 prioritizeReplicates(xyz, centerOfMass, compareAtomsSize, auDist, 0, linkage);
2573
2574 boolean redo = false;
2575 for (int i = 0; i < nAU; i++) {
2576 if (logger.isLoggable(Level.FINE)) {
2577 logger.fine(format(
2578 " i: %3d\t auDist Index: %3d \t indexOrder: %3d\t indget(au): %3d\t ReplicatesVector: %2d (%2d) %2d (%2d) %2d (%2d)",
2579 i, auDist[i].index(), indexOrder.get(i), indexOrder.get(auDist[i].index()),
2580 symOps.get(indexOrder.get(auDist[i].index())).replicatesVector[0] + 1, startLMN[0],
2581 symOps.get(indexOrder.get(auDist[i].index())).replicatesVector[1] + 1, startLMN[1],
2582 symOps.get(indexOrder.get(auDist[i].index())).replicatesVector[2] + 1, startLMN[2]));
2583 }
2584 final int[] lmn = symOps.get(indexOrder.get(auDist[i].index())).replicatesVector;
2585 if (lmn[0] == 0 || lmn[0] == startLMN[0] - 1) {
2586 redo = true;
2587 }
2588 if (lmn[1] == 0 || lmn[1] == startLMN[1] - 1) {
2589 redo = true;
2590 }
2591 if (lmn[2] == 0 || lmn[2] == startLMN[2] - 1) {
2592 redo = true;
2593 }
2594 if (redo) {
2595 break;
2596 }
2597 }
2598 if (redo && logger.isLoggable(Level.FINE)) {
2599 logger.fine(" REDO EXPANSION.");
2600 }
2601 return redo;
2602 }
2603
2604
2605
2606
2607
2608
2609
2610 private static void copyArrayValues(double[][] newArray, double[][] oldArray) {
2611 final int arrayLength = newArray.length;
2612 for (int i = 0; i < arrayLength; i++) {
2613 arraycopy(oldArray[i], 0, newArray[i], 0, newArray[i].length);
2614 }
2615 }
2616
2617
2618
2619
2620
2621
2622
2623 private static boolean copyFile(File input, File output){
2624 try(InputStream is = new FileInputStream(input); OutputStream os = new FileOutputStream(output)) {
2625 byte[] buffer = new byte[1024];
2626 int length;
2627 while((length = is.read(buffer)) > 0){
2628 os.write(buffer, 0, length);
2629 }
2630 }catch(Exception ex){
2631 logger.info(ex + Utilities.stackTraceToString(ex));
2632 return false;
2633 }
2634 return true;
2635 }
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647 private static void determineComparableAtoms(final Atom[] atoms, ArrayList<Integer> indices,
2648 final ArrayList<Integer> unique, final boolean alphaCarbons, final boolean includeHydrogen) {
2649 final int nAtoms = atoms.length;
2650 for (int i = 0; i < nAtoms; i++) {
2651 if (!unique.contains(i)) {
2652 final Atom atom = atoms[i];
2653 if (alphaCarbons) {
2654 final String atomName = atom.getName();
2655 final int atomAtNum = atom.getAtomicNumber();
2656 final boolean proteinCheck = atomName.equals("CA") && atomAtNum == 6;
2657 final boolean aminoCheck = (atomName.equals("N1") || atomName.equals("N9")) && atomAtNum == 7;
2658 if (proteinCheck || aminoCheck) {
2659 indices.add(i);
2660 }
2661 } else if (includeHydrogen || !atom.isHydrogen()) {
2662 indices.add(i);
2663 }
2664
2665 atom.setActive(true);
2666 }
2667 }
2668 }
2669
2670
2671
2672
2673
2674
2675
2676 private static String fileContainsString(File file, String string) {
2677 try (BufferedReader br = new BufferedReader(new FileReader(file))) {
2678 String line = br.readLine();
2679 while (line != null) {
2680 if (line.toUpperCase().contains(string.toUpperCase())) {
2681 return "";
2682 }
2683 line = br.readLine();
2684 }
2685 return string + " ";
2686 } catch (Exception ex) {
2687 logger.warning(ex + Utilities.stackTraceToString(ex));
2688 return "";
2689 }
2690 }
2691
2692
2693
2694
2695
2696
2697
2698 private void gatherRMSDs(int row, RunningStatistics runningStatistics) {
2699 if (useMPI) {
2700 try {
2701 if (logger.isLoggable(Level.FINER)) {
2702 logger.finer(" Receiving MPI results.");
2703 }
2704 world.gather(0, myBuffer, buffers);
2705 if (rank == 0) {
2706 for (int workItem = 0; workItem < numWorkItems; workItem++) {
2707 for (int proc = 0; proc < numProc; proc++) {
2708 final int column = numProc * workItem + proc;
2709
2710 if (column < targetSize) {
2711 distRow[column] = distances[proc][workItem];
2712 if (!isSymmetric) {
2713 runningStatistics.addValue(distRow[column]);
2714 } else if (column >= row) {
2715
2716 runningStatistics.addValue(distRow[column]);
2717 }
2718 if (logger.isLoggable(Level.FINER)) {
2719 logger.finer(format(" %d %d %14.8f", row, column, distances[proc][workItem]));
2720 }
2721 }
2722 }
2723 }
2724 } else {
2725 for (int workItem = 0; workItem < numWorkItems; workItem++) {
2726 final int column = numProc * workItem + rank;
2727
2728 if (column < targetSize) {
2729 distRow[column] = distances[rank][workItem];
2730 if (!isSymmetric) {
2731 runningStatistics.addValue(distRow[column]);
2732 } else if (column >= row) {
2733
2734 runningStatistics.addValue(distRow[column]);
2735 }
2736 if (logger.isLoggable(Level.FINER)) {
2737 logger.finer(format(" %d %d %14.8f", row, column, distances[rank][workItem]));
2738 }
2739 }
2740 }
2741 }
2742 } catch (Exception ex) {
2743 logger.severe(" Exception collecting distance values." + ex);
2744 logger.warning(ex + Utilities.stackTraceToString(ex));
2745 }
2746 } else {
2747 for (int i = 0; i < targetSize; i++) {
2748 distRow[i] = myDistances[i];
2749 if (!isSymmetric) {
2750 runningStatistics.addValue(distRow[i]);
2751 } else if (i >= row) {
2752
2753 runningStatistics.addValue(distRow[i]);
2754 }
2755 }
2756 }
2757 }
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778 private static double[] generateInflatedSphere(final Crystal unitCell,
2779 final double[] reducedCoords, final int zPrime, final int nAU, final int linkage,
2780 final double[] mass, int[] lmn, final boolean strict, ArrayList<SymOp> symOps,
2781 ArrayList<Integer> indexOrder, double inflationFactor) {
2782 symOps.clear();
2783 indexOrder.clear();
2784
2785 final int nCoords = reducedCoords.length;
2786
2787 final int nAtoms = nCoords / 3;
2788
2789 final int zAtoms = nAtoms / zPrime;
2790
2791 final double[] x = new double[nAtoms];
2792 final double[] y = new double[nAtoms];
2793 final double[] z = new double[nAtoms];
2794 for (int i = 0; i < nAtoms; i++) {
2795 final int atomIndex = i * 3;
2796 x[i] = reducedCoords[atomIndex];
2797 y[i] = reducedCoords[atomIndex + 1];
2798 z[i] = reducedCoords[atomIndex + 2];
2799 }
2800
2801
2802 final double volume = unitCell.volume / unitCell.getNumSymOps() / zPrime;
2803
2804 final double radius = cbrt(0.75 / PI * volume * max(3, nAU) * inflationFactor);
2805 Crystal replicatesCrystal = ReplicatesCrystal.replicatesCrystalFactory(unitCell, radius * 2.0,
2806 lmn);
2807
2808
2809
2810
2811 final int nSymm = replicatesCrystal.getNumSymOps();
2812 final double[][] xS = new double[nSymm][nAtoms];
2813 final double[][] yS = new double[nSymm][nAtoms];
2814 final double[][] zS = new double[nSymm][nAtoms];
2815
2816 final int numEntities = nSymm * zPrime;
2817
2818 final double[][] cartCenterOfMass = new double[numEntities][3];
2819
2820
2821 List<SymOp> inflatedSymOps = replicatesCrystal.spaceGroup.symOps;
2822 for (int iSym = 0; iSym < nSymm; iSym++) {
2823 final SymOp symOp = inflatedSymOps.get(iSym);
2824
2825 final double[][] rot = new double[3][3];
2826 replicatesCrystal.getTransformationOperator(symOp, rot);
2827
2828 replicatesCrystal.applySymOp(nAtoms, x, y, z, xS[iSym], yS[iSym], zS[iSym], symOp);
2829 for (int zp = 0; zp < zPrime; zp++) {
2830 final int symIndex = zp * zAtoms;
2831
2832 final double[] centerOfMass = new double[3];
2833 double zMass = 0.0;
2834 for (int i = 0; i < zAtoms; i++) {
2835 final double m = mass[i];
2836 final int coordIndex = symIndex + i;
2837 centerOfMass[0] += xS[iSym][coordIndex] * m;
2838 centerOfMass[1] += yS[iSym][coordIndex] * m;
2839 centerOfMass[2] += zS[iSym][coordIndex] * m;
2840 zMass += m;
2841 }
2842 centerOfMass[0] /= zMass;
2843 centerOfMass[1] /= zMass;
2844 centerOfMass[2] /= zMass;
2845
2846 final double[] translate = moveIntoCrystal(replicatesCrystal, centerOfMass);
2847
2848 final double[] trans = symOp.tr.clone();
2849 replicatesCrystal.toCartesianCoordinates(trans, trans);
2850 final int translateLength = translate.length;
2851 for (int i = 0; i < translateLength; i++) {
2852 trans[i] += translate[i];
2853 }
2854 symOps.add(new SymOp(rot, trans, symOp.replicatesVector));
2855 for (int i = 0; i < zAtoms; i++) {
2856 final int coordIndex = symIndex + i;
2857 xS[iSym][coordIndex] += translate[0];
2858 centerOfMass[0] += translate[0];
2859 yS[iSym][coordIndex] += translate[1];
2860 centerOfMass[1] += translate[1];
2861 zS[iSym][coordIndex] += translate[2];
2862 centerOfMass[2] += translate[2];
2863 }
2864
2865
2866 cartCenterOfMass[iSym * zPrime + zp] = centerOfMass;
2867 }
2868 }
2869
2870
2871
2872
2873 final DoubleIndexPair[] auDist = new DoubleIndexPair[numEntities];
2874 final double[] cartCenter = new double[3];
2875
2876
2877
2878 final double[] fracCenter = {0.5, 0.5, 0.5};
2879 replicatesCrystal.toCartesianCoordinates(fracCenter, cartCenter);
2880
2881 if (logger.isLoggable(Level.FINER)) {
2882 if (logger.isLoggable(Level.FINEST)) {
2883 logger.finer(" Replicates crystal " + replicatesCrystal);
2884 }
2885 logger.finer(format(" Replicates Volume: %8.4f", replicatesCrystal.volume));
2886 logger.finer(
2887 format(" Expanded Crystal Center: %14.8f %14.8f %14.8f", cartCenter[0], cartCenter[1],
2888 cartCenter[2]));
2889 }
2890 logger.fine(
2891 format(" Expanded Crystal Center: %14.8f %14.8f %14.8f", cartCenter[0], cartCenter[1],
2892 cartCenter[2]));
2893
2894 for (int i = 0; i < numEntities; i++) {
2895
2896 auDist[i] = new DoubleIndexPair(i, dist2(cartCenter, cartCenterOfMass[i]));
2897 }
2898
2899
2900
2901 sort(auDist);
2902 for (DoubleIndexPair molsDist : auDist) {
2903 indexOrder.add(molsDist.index());
2904 }
2905
2906 if (logger.isLoggable(Level.FINEST)) {
2907 logger.finest("\n Copy SymOp Distance");
2908 }
2909 double[] systemCoords = new double[numEntities * zAtoms * 3];
2910 for (int n = 0; n < nSymm; n++) {
2911 for (int zp = 0; zp < zPrime; zp++) {
2912 final int index = n * zPrime + zp;
2913
2914 final int iSym = auDist[index].index();
2915 final double distance = auDist[index].doubleValue();
2916 if (logger.isLoggable(Level.FINEST) && n < 30) {
2917 logger.finest(format(" %4d %5d %14.8f", index, iSym, sqrt(distance)));
2918 }
2919
2920
2921 for (int i = 0; i < zAtoms; i++) {
2922 final int symIndex = index * zAtoms * 3;
2923 final int atomIndex = symIndex + i * 3;
2924 final int conversion = iSym / zPrime;
2925 final int shift = i + (iSym % zPrime) * zAtoms;
2926 systemCoords[atomIndex] = xS[conversion][shift];
2927 systemCoords[atomIndex + 1] = yS[conversion][shift];
2928 systemCoords[atomIndex + 2] = zS[conversion][shift];
2929 }
2930 }
2931 }
2932 final double massSum = Arrays.stream(mass).sum();
2933 if (logger.isLoggable(Level.FINE)) {
2934 logger.fine(format(
2935 " Checking replicates crystal.\n SysCoords: %3d (%3d)(%3d), reducedCoords Size: %3d (%3d)\n mass Size %3d, massSum: %6.3f, nAU: %2d, nCoords: %3d\n "
2936 + "auDist Size: %3d, l: %2d, m: %2d, n: %2d, numEntities: %3d, nAtoms: %3d, zPrime: %3d, zAtoms: %3d",
2937 systemCoords.length, systemCoords.length / 3, systemCoords.length / 3 / zAtoms,
2938 reducedCoords.length, reducedCoords.length / 3, mass.length, massSum, nAU, nCoords,
2939 auDist.length, lmn[0], lmn[1], lmn[2], numEntities, nAtoms, zPrime, zAtoms));
2940 }
2941 if (strict && checkInflatedSphere(systemCoords, mass, massSum, zAtoms, nAU, linkage, lmn, symOps,
2942 indexOrder)) {
2943
2944
2945 inflationFactor += 5;
2946 systemCoords = generateInflatedSphere(unitCell, reducedCoords, zPrime, nAU, linkage, mass, lmn,
2947 true, symOps, indexOrder, inflationFactor);
2948 }
2949 return systemCoords;
2950 }
2951
2952
2953
2954
2955
2956
2957
2958
2959 private static int guessZPrime(final ArrayList<Integer> unique, final int[] molNum, final int numEntities) {
2960 final int[] molSize = new int[numEntities];
2961 final int nAtoms = molNum.length;
2962 for(int i = 0; i < nAtoms; i++){
2963 if(unique.contains(i)){
2964 molSize[molNum[i]]++;
2965 }
2966 }
2967 int z;
2968 switch (numEntities) {
2969 case 2 -> {
2970 if (molSize[0] == molSize[1]) {
2971 z = 2;
2972 } else {
2973 z = 1;
2974 }
2975 }
2976 case 3 -> {
2977 if (molSize[0] == molSize[1] && molSize[1] == molSize[2]) {
2978 z = 3;
2979 } else {
2980 z = 1;
2981 }
2982 }
2983 case 4 -> {
2984 if (molSize[0] == molSize[1] && molSize[1] == molSize[2] && molSize[2] == molSize[3]) {
2985 z = 4;
2986 } else if (molSize[0] == molSize[2] && molSize[1] == molSize[3]) {
2987 z = 2;
2988 } else {
2989 z = 1;
2990 }
2991 }
2992 case 5 -> {
2993 if (molSize[0] == molSize[1] && molSize[1] == molSize[2] && molSize[2] == molSize[3] && molSize[3] == molSize[4]) {
2994 z = 5;
2995 } else {
2996 z = 1;
2997 }
2998 }
2999 case 6 -> {
3000 if (molSize[0] == molSize[1] && molSize[1] == molSize[2] && molSize[2] == molSize[3] && molSize[3] == molSize[4] && molSize[4] == molSize[5]) {
3001 z = 6;
3002 } else if (molSize[0] == molSize[2] && molSize[2] == molSize[4] && molSize[1] == molSize[3] && molSize[3] == molSize[5]) {
3003 z = 3;
3004 } else if (molSize[0] == molSize[3] && molSize[1] == molSize[4] && molSize[2] == molSize[5]) {
3005 z = 2;
3006 } else {
3007 z = 1;
3008 }
3009 }
3010 default -> z = 1;
3011 }
3012 if (logger.isLoggable(Level.FINER)) {
3013 logger.finer(format(" Number of species in asymmetric unit (Z Prime): %d", z));
3014 }
3015 return z;
3016 }
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027 private static boolean invalidAtomSelection(final ArrayList<Integer> indices, final Atom[] atoms,
3028 final boolean alphaCarbons, final boolean includeHydrogen) {
3029 final ArrayList<Integer> unique = new ArrayList<>();
3030 for (Integer value : indices) {
3031 if (!unique.contains(value)) {
3032 unique.add(value);
3033 }
3034 }
3035 indices.clear();
3036 determineComparableAtoms(atoms, indices, unique, alphaCarbons, includeHydrogen);
3037 return indices.isEmpty();
3038 }
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048 public static String matrixToString(final double[][] matrix, final int index, String description) {
3049 StringBuilder value = new StringBuilder(format("\n %s %d: ", description, index));
3050 for (double[] doubles : matrix) {
3051 value.append(format("\n\t%s", Arrays.toString(doubles)));
3052 }
3053 value.append("\n");
3054 return value.toString();
3055 }
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065 private static double[] moveIntoCrystal(final Crystal crystal, final double[] com) {
3066
3067 final double[] translate = new double[3];
3068 final double[] currentCoM = new double[3];
3069 currentCoM[0] = com[0];
3070 currentCoM[1] = com[1];
3071 currentCoM[2] = com[2];
3072
3073
3074 crystal.toFractionalCoordinates(com, translate);
3075 translate[0] = mod(translate[0], 1.0);
3076 translate[1] = mod(translate[1], 1.0);
3077 translate[2] = mod(translate[2], 1.0);
3078 crystal.toCartesianCoordinates(translate, translate);
3079
3080
3081 com[0] = translate[0];
3082 com[1] = translate[1];
3083 com[2] = translate[2];
3084
3085 translate[0] -= currentCoM[0];
3086 translate[1] -= currentCoM[1];
3087 translate[2] -= currentCoM[2];
3088
3089 return translate;
3090 }
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106 private void numberUniqueAUs(double[] allCoords, DoubleIndexPair[] auDist, double[] auCoords,
3107 int nCoords, int upperLimit, boolean strict, int nAUinReplicates, double[] massN,
3108 double matchTol) {
3109
3110
3111 final List<Double> uniqueDiffs = new ArrayList<>();
3112 final ArrayList<double[]> tempListXYZ = new ArrayList<>();
3113 tempListIndices.add(0);
3114 uniqueDiffs.add(rmsd(auCoords, auCoords, massN));
3115 tempListXYZ.add(auCoords);
3116
3117 int index = 1;
3118
3119 final boolean useSym = printSym >= 0.0;
3120 final int numConfCheck = (strict || upperLimit <= 0 || useSym) ? nAUinReplicates : upperLimit;
3121 if (logger.isLoggable(Level.FINEST)) {
3122 logger.finest("RMSD Differences in Replicates Crystal:");
3123 }
3124 final double[] target = new double[nCoords];
3125 arraycopy(tempListXYZ.get(0), 0, target, 0, nCoords);
3126 translate(target, massN);
3127 while ((uniqueDiffs.size() < numConfCheck)) {
3128 final double[] baseCheckMol = new double[nCoords];
3129 final int auIndex = auDist[index].index();
3130 arraycopy(allCoords, auIndex * nCoords, baseCheckMol, 0, nCoords);
3131 translate(baseCheckMol, massN);
3132 rotate(target, baseCheckMol, massN);
3133 final double value = rmsd(target, baseCheckMol, massN);
3134 if (addLooseUnequal(uniqueDiffs, value, matchTol) || useSym) {
3135 if (logger.isLoggable(Level.FINEST)) {
3136 logger.finest(format(" Sorted Index: %4d Dist: %18.16f", auIndex, value));
3137 }
3138 tempListIndices.add(index);
3139 tempListXYZ.add(baseCheckMol);
3140 }
3141 index++;
3142 if (index >= auDist.length) {
3143 break;
3144 }
3145 }
3146 if (logger.isLoggable(Level.FINER)) {
3147 logger.finer(" RMSD_1 from 1st AU:\n i RMSD AU Index");
3148 final int numUnique = uniqueDiffs.size();
3149 for (int i = 0; i < numUnique; i++) {
3150 logger.finer(format(" %d %4.4f %4d", i, uniqueDiffs.get(i),
3151 auDist[tempListIndices.get(i)].index()));
3152 }
3153 }
3154 }
3155
3156
3157
3158
3159
3160
3161
3162 private void pairEntities(int desiredAUs, int comparisonNum) {
3163 tempListIndices.clear();
3164
3165 for (DoubleIndexPair doubleIndexPair : targetAUDist_2) {
3166
3167
3168 tempListIndices.add(doubleIndexPair.index());
3169 }
3170
3171
3172 for (int i = 0; i < desiredAUs; i++) {
3173 double minDist = Double.MAX_VALUE;
3174 Integer minIndex = -1;
3175 final double[] baseCoMCurr = baseCoM[comparisonNum][baseAUDist_2[i].index()];
3176 for (Integer target : tempListIndices) {
3177 final double dist = dist2(baseCoMCurr, targetCoM[target]);
3178 if (dist < minDist) {
3179 minIndex = target;
3180 minDist = dist;
3181 }
3182 if (abs(minDist) < MATCH_TOLERANCE) {
3183
3184 if (logger.isLoggable(Level.FINEST)) {
3185 stringBuilder.append("\n \tExit out of match loop.\n");
3186 }
3187 break;
3188 }
3189 }
3190 pairedAUs[i] = new DoubleIndexPair(minIndex, minDist);
3191 if (!tempListIndices.remove(minIndex)) {
3192 logger.warning(format(" Index value of %d was not found (%4.4f).", minIndex, minDist));
3193 }
3194 if (logger.isLoggable(Level.FINEST)) {
3195 stringBuilder.append(format("\n Base position: %d: %8.4f %8.4f %8.4f\n", i,
3196 baseCoM[comparisonNum][baseAUDist_2[i].index()][0],
3197 baseCoM[comparisonNum][baseAUDist_2[i].index()][1],
3198 baseCoM[comparisonNum][baseAUDist_2[i].index()][2]));
3199 stringBuilder.append(
3200 format(" Match Distance: %d: %8.4f\n", i, sqrt(pairedAUs[i].doubleValue())));
3201 int pairedIndex = pairedAUs[i].index();
3202 stringBuilder.append(
3203 format(" Target position: %d: %8.4f %8.4f %8.4f\n", i, targetCoM[pairedIndex][0],
3204 targetCoM[pairedIndex][1], targetCoM[pairedIndex][2]));
3205 }
3206 }
3207 if (logger.isLoggable(Level.FINER)) {
3208 stringBuilder.append(" Distance between pairs:\n"
3209 + " Index Base Index Target Index Match Index Distance\n");
3210 for (int i = 0; i < desiredAUs; i++) {
3211 stringBuilder.append(format(" %5d %10d %14d %14d %10.4f\n", i, baseAUDist_2[i].index(),
3212 targetAUDist_2[i].index(), pairedAUs[i].index(), sqrt(pairedAUs[i].doubleValue())));
3213 }
3214 }
3215 }
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229 private void printSym(int compareAtomsSize, File file, String name, List<Bond> bondList,
3230 Atom[] atoms, ForceField forceField, int saveClusters, int currZ2) {
3231
3232
3233
3234 final double[][] tempTransform = new double[4][4];
3235 copyArrayValues(tempTransform, targetTransformSymOp.asMatrix());
3236 addTranslation(targetSymOp.tr, tempTransform, true);
3237 addRotation(targetSymOp.rot, tempTransform, true);
3238 final double[] bestTranslation = new double[]{tempTransform[0][3] / tempTransform[3][3],
3239 tempTransform[1][3] / tempTransform[3][3], tempTransform[2][3] / tempTransform[3][3]};
3240 final SymOp symOp = new SymOp(copyOf(tempTransform, 3), bestTranslation);
3241 final double[] newMol = new double[compareAtomsSize * 3];
3242 StringBuilder printString = new StringBuilder();
3243 String title = format("\n Print Current Symmetry Operator (Z'=%2d):\n"
3244 + " \t Original Coords \t\t Current Coords \t==\t Applied Sym Op Coords", currZ2);
3245 final double[] originalAU = targetAUoriginal[currZ2];
3246 for (int i = 0; i < compareAtomsSize; i++) {
3247 final int k = i * 3;
3248 final double[] xyz = new double[]{originalAU[k], originalAU[k + 1], originalAU[k + 2]};
3249 applyCartesianSymOp(xyz, xyz, symOp);
3250 newMol[k] = xyz[0];
3251 newMol[k + 1] = xyz[1];
3252 newMol[k + 2] = xyz[2];
3253 printString.append(
3254 format("\n %9.3f %9.3f %9.3f to %9.3f %9.3f %9.3f to %9.3f %9.3f %9.3f", originalAU[k],
3255 originalAU[k + 1], originalAU[k + 2], targetAU[k], targetAU[k + 1], targetAU[k + 2],
3256 newMol[k], newMol[k + 1], newMol[k + 2]));
3257 }
3258 title += format(" RMSD: %9.3f (should be 0.000)", rmsd(newMol, targetAU, massN));
3259 stringBuilder.append(title).append(printString);
3260 saveAssembly(file, name, bondList, atoms, forceField, newMol, comparisonAtoms, 1, "_moveMethod",
3261 0, saveClusters);
3262 stringBuilder.append("\n").append(symOp).append("\n");
3263 }
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276 private static void prioritizeReplicates(final double[] coordsXYZ, final double[][] centerOfMasses, final int nAtoms,
3277 final DoubleIndexPair[] auDist, final int index, final int linkage) {
3278
3279
3280 final int nCoords = nAtoms * 3;
3281 final int length = coordsXYZ.length;
3282 final int nMols = length / nCoords;
3283 if (auDist.length != nMols) {
3284 logger.warning(" Number of molecules does not match distance sort array length.");
3285 }
3286 if (linkage == 0) {
3287
3288 final int centerIndex = index * nCoords;
3289 for (int i = 0; i < nMols; i++) {
3290 double tempDist = Double.MAX_VALUE;
3291 final int molIndex = i * nCoords;
3292 for (int j = 0; j < nAtoms; j++) {
3293 final int centerAtomIndex = centerIndex + j * 3;
3294 final double[] centerXYZ = {coordsXYZ[centerAtomIndex], coordsXYZ[centerAtomIndex + 1],
3295 coordsXYZ[centerAtomIndex + 2]};
3296 for (int k = 0; k < nAtoms; k++) {
3297 final int atomIndex = molIndex + k * 3;
3298 final double[] xyz = {coordsXYZ[atomIndex], coordsXYZ[atomIndex + 1],
3299 coordsXYZ[atomIndex + 2]};
3300 final double currDist = dist2(centerXYZ, xyz);
3301 if (currDist < tempDist) {
3302 tempDist = currDist;
3303 }
3304 if (abs(tempDist) < MATCH_TOLERANCE) {
3305 break;
3306 }
3307 }
3308 if (abs(tempDist) < MATCH_TOLERANCE) {
3309 break;
3310 }
3311 }
3312 auDist[i] = new DoubleIndexPair(i, tempDist);
3313 }
3314
3315 sort(auDist);
3316
3317 } else if (linkage == 1) {
3318
3319 final double[] coordCenter = centerOfMasses[index];
3320 for (int i = 0; i < nMols; i++) {
3321 final double[] moleculeCenter = centerOfMasses[i];
3322 auDist[i] = new DoubleIndexPair(i, dist2(coordCenter, moleculeCenter));
3323 }
3324
3325 sort(auDist);
3326
3327
3328
3329
3330 final DoubleIndexPair[] molDists = new DoubleIndexPair[nMols];
3331 int auIndex0 = auDist[0].index();
3332 final int auIndex1 = auDist[1].index();
3333 molDists[0] = new DoubleIndexPair(auIndex0, auDist[0].doubleValue());
3334
3335 double[] avgCenter = new double[3];
3336 avgCenter[0] = (centerOfMasses[auIndex0][0] + centerOfMasses[auIndex1][0]) / 2;
3337 avgCenter[1] = (centerOfMasses[auIndex0][1] + centerOfMasses[auIndex1][1]) / 2;
3338 avgCenter[2] = (centerOfMasses[auIndex0][2] + centerOfMasses[auIndex1][2]) / 2;
3339
3340 for (int i = 1; i < nMols; i++) {
3341 auIndex0 = auDist[i].index();
3342 final double[] moleculeCenter = centerOfMasses[auIndex0];
3343 molDists[i] = new DoubleIndexPair(auIndex0, dist2(avgCenter, moleculeCenter));
3344 }
3345 sort(molDists);
3346
3347
3348
3349 final DoubleIndexPair[] molDists3 = new DoubleIndexPair[nMols];
3350 molDists3[0] = new DoubleIndexPair(molDists[0].index(), molDists[0].doubleValue());
3351 molDists3[1] = new DoubleIndexPair(molDists[1].index(), molDists[1].doubleValue());
3352 avgCenter = new double[3];
3353 final int bound = min(3, molDists.length);
3354 for (int i = 0; i < bound; i++) {
3355 final int molIndex = molDists[i].index();
3356 avgCenter[0] += centerOfMasses[molIndex][0];
3357 avgCenter[1] += centerOfMasses[molIndex][1];
3358 avgCenter[2] += centerOfMasses[molIndex][2];
3359 }
3360 for (int i = 0; i < 3; i++) {
3361 avgCenter[i] /= 3;
3362 }
3363
3364 for (int i = 2; i < nMols; i++) {
3365 auIndex0 = molDists[i].index();
3366 final double[] moleculeCenter = centerOfMasses[auIndex0];
3367 molDists3[i] = new DoubleIndexPair(auIndex0, dist2(avgCenter, moleculeCenter));
3368 }
3369
3370 sort(molDists3);
3371 arraycopy(molDists3, 0, auDist, 0, nMols);
3372 } else if (linkage == 2) {
3373
3374 final int centerIndex = index * nCoords;
3375 for (int i = 0; i < nMols; i++) {
3376 double tempDist = 0.0;
3377 final int molIndex = i * nCoords;
3378 for (int j = 0; j < nAtoms; j++) {
3379 final int centerAtomIndex = centerIndex + j * 3;
3380 final double[] centerXYZ = {coordsXYZ[centerAtomIndex], coordsXYZ[centerAtomIndex + 1],
3381 coordsXYZ[centerAtomIndex + 2]};
3382 for (int k = 0; k < nAtoms; k++) {
3383 final int atomIndex = molIndex + k * 3;
3384 final double[] xyz = {coordsXYZ[atomIndex], coordsXYZ[atomIndex + 1],
3385 coordsXYZ[atomIndex + 2]};
3386 final double currDist = dist2(centerXYZ, xyz);
3387 if (currDist > tempDist) {
3388 tempDist = currDist;
3389 }
3390 if (abs(tempDist) < MATCH_TOLERANCE) {
3391 break;
3392 }
3393 }
3394 if (abs(tempDist) < MATCH_TOLERANCE) {
3395 break;
3396 }
3397 }
3398 auDist[i] = new DoubleIndexPair(i, tempDist);
3399 }
3400
3401 sort(auDist);
3402 } else {
3403 logger.severe(format(" Linkage value of %d was unrecognized. Try 0 (, 1, or 2.", linkage));
3404 }
3405 }
3406
3407
3408
3409
3410
3411
3412
3413 private RunningStatistics readMatrix(final String filename) {
3414 restartRow = 0;
3415 restartColumn = 0;
3416
3417 final DistanceMatrixFilter distanceMatrixFilter = new DistanceMatrixFilter();
3418 final RunningStatistics runningStatistics = distanceMatrixFilter.readDistanceMatrix(filename, baseSize,
3419 targetSize);
3420
3421 if (runningStatistics != null && runningStatistics.getCount() > 0) {
3422 restartRow = distanceMatrixFilter.getRestartRow();
3423 restartColumn = distanceMatrixFilter.getRestartColumn();
3424
3425 if (isSymmetric) {
3426
3427 if (restartRow == baseSize && restartColumn == 1) {
3428 logger.info(format(" Complete symmetric distance matrix found (%d x %d).", restartRow,
3429 restartRow));
3430 } else {
3431 restartColumn = 0;
3432 logger.info(format(
3433 " Incomplete symmetric distance matrix found.\n Restarting at row %d, column %d.",
3434 restartRow + 1, restartColumn + 1));
3435 }
3436 } else if (restartRow == baseSize && restartColumn == targetSize) {
3437 logger.info(format(" Complete distance matrix found (%d x %d).", restartRow, restartColumn));
3438 } else {
3439 restartColumn = 0;
3440 logger.info(format(" Incomplete distance matrix found.\n Restarting at row %d, column %d.",
3441 restartRow + 1, restartColumn + 1));
3442 }
3443 }
3444
3445 return runningStatistics;
3446 }
3447
3448
3449
3450
3451
3452
3453
3454
3455 private static double[] reduceSystem(final Atom[] atoms, final int[] comparisonAtoms) {
3456
3457 final double[] reducedCoords = new double[comparisonAtoms.length * 3];
3458 int coordIndex = 0;
3459 for (Integer value : comparisonAtoms) {
3460 final Atom atom = atoms[value];
3461 reducedCoords[coordIndex++] = atom.getX();
3462 reducedCoords[coordIndex++] = atom.getY();
3463 reducedCoords[coordIndex++] = atom.getZ();
3464 }
3465 return reducedCoords;
3466 }
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482 private void saveAssembly(final File file, String name, final List<Bond> bondList, final Atom[] atoms,
3483 final ForceField forceField0, final double[] coords, final int[] comparisonAtoms, final int nAU,
3484 final String description, final int compNum, final int saveClusters) {
3485
3486 final String fileName = removeExtension(file.getName());
3487 File saveLocation;
3488 if (saveClusters == 2) {
3489 saveLocation = new File(fileName + description + ".xyz");
3490 } else if (saveClusters == 1) {
3491 saveLocation = new File(fileName + description + ".pdb");
3492 } else {
3493 return;
3494 }
3495
3496 final MolecularAssembly currentAssembly = new MolecularAssembly(name);
3497 final ArrayList<Atom> newAtomList = new ArrayList<>();
3498 int atomIndex = 1;
3499 final int nCoords = coords.length / (nAU);
3500
3501 try {
3502 for (int n = 0; n < nAU; n++) {
3503
3504
3505
3506
3507
3508 final ArrayList<Atom> atomList = new ArrayList<>();
3509
3510 int atomValue = 0;
3511
3512
3513
3514 final int[] atomSelection = new int[nCoords / 3];
3515 arraycopy(comparisonAtoms, 0, atomSelection, 0, nCoords / 3);
3516 for (final Integer i : atomSelection) {
3517 final Atom a = atoms[i];
3518
3519 final double[] xyz = new double[3];
3520 final int coordIndex = n * nCoords + atomValue;
3521 xyz[0] = coords[coordIndex];
3522 xyz[1] = coords[coordIndex + 1];
3523 xyz[2] = coords[coordIndex + 2];
3524 final Atom atom = new Atom(atomIndex++, a.getName(), a.getAtomType(), xyz);
3525 atomList.add(atom);
3526 atomValue += 3;
3527 }
3528
3529 if (saveClusters == 2) {
3530
3531 for (final Bond bond : bondList) {
3532 final Atom a1 = bond.getAtom(0);
3533 final Atom a2 = bond.getAtom(1);
3534
3535 final int a1Ind = a1.getIndex() - 1;
3536 final int a2Ind = a2.getIndex() - 1;
3537 if (IntStream.of(comparisonAtoms).anyMatch(x -> x == a1Ind) && IntStream.of(
3538 comparisonAtoms).anyMatch(x -> x == a2Ind)) {
3539 final Atom newA1 = atomList.get(a1Ind);
3540 final Atom newA2 = atomList.get(a2Ind);
3541 if(!newA1.isBonded(newA2) || !newA2.isBonded(newA1)) {
3542 final Bond b = new Bond(newA1, newA2);
3543 b.setBondType(bond.getBondType());
3544 }
3545 }
3546 }
3547 }
3548 newAtomList.addAll(atomList);
3549 }
3550 } catch (Exception exception) {
3551 stringBuilder.append("\n Error saving moved coordinates to PDB.\n").append(exception).append("\n");
3552 logger.warning(exception + Utilities.stackTraceToString(exception));
3553 }
3554
3555 if (logger.isLoggable(Level.FINEST)) {
3556 final int newSize = newAtomList.size();
3557 stringBuilder.append(format("\n Save Num AU: %3d", nAU));
3558 stringBuilder.append(format("\n Save Num Active: %3d", nCoords));
3559 stringBuilder.append(format("\n Save Num Coords: %3d", coords.length));
3560 stringBuilder.append(format("\n Save Assembly Length: %3d", newSize));
3561 if (newSize > 0) {
3562 final Atom zero = newAtomList.get(0);
3563 stringBuilder.append(
3564 format("\n Save Assembly First Atom: %3s %9.3f %9.3f %9.3f\n", zero.getName(),
3565 zero.getX(), zero.getY(), zero.getZ()));
3566 } else {
3567 stringBuilder.append("\n WARNING: New list containing new atoms was empty.\n");
3568 }
3569 }
3570
3571
3572 final ForceField forceField = new ForceField(forceField0.getProperties());
3573
3574
3575 forceField.clearProperty("a-axis");
3576 forceField.clearProperty("b-axis");
3577 forceField.clearProperty("c-axis");
3578 forceField.clearProperty("alpha");
3579 forceField.clearProperty("beta");
3580 forceField.clearProperty("gamma");
3581 forceField.clearProperty("spacegroup");
3582 currentAssembly.setForceField(forceField);
3583
3584
3585
3586 Utilities.biochemistry(currentAssembly, newAtomList);
3587
3588 currentAssembly.setFile(file);
3589 if (saveClusters == 2) {
3590 final File key = new File(fileName + ".key");
3591 final File properties = new File(fileName + ".properties");
3592 if (key.exists()) {
3593 final File keyComparison = new File(fileName + description + ".key");
3594 try {
3595 if (keyComparison.createNewFile()) {
3596 FileUtils.copyFile(key, keyComparison);
3597 } else {
3598 stringBuilder.append("\n Could not create properties file.\n");
3599 }
3600 } catch (Exception ex) {
3601
3602 if (logger.isLoggable(Level.FINER)) {
3603 stringBuilder.append(ex);
3604 logger.warning(ex + Utilities.stackTraceToString(ex));
3605 }
3606 }
3607 } else if (properties.exists()) {
3608 final File propertiesComparison = new File(fileName + description + ".properties");
3609 try {
3610 if (propertiesComparison.createNewFile()) {
3611 FileUtils.copyFile(properties, propertiesComparison);
3612 } else {
3613 stringBuilder.append("\n Could not create properties file.\n");
3614 }
3615 } catch (Exception ex) {
3616
3617 stringBuilder.append(
3618 "\n Neither key nor properties file detected therefore only creating XYZ.\n");
3619 if (logger.isLoggable(Level.FINER)) {
3620 stringBuilder.append(ex);
3621 logger.warning(ex + Utilities.stackTraceToString(ex));
3622 }
3623 }
3624 }
3625 final XYZFilter xyzFilter = new XYZFilter(saveLocation, currentAssembly, forceField,
3626 currentAssembly.getProperties());
3627 xyzFilter.writeFile(saveLocation, true);
3628 } else {
3629 final PDBFilter pdbfilter = new PDBFilter(saveLocation, currentAssembly, forceField,
3630 currentAssembly.getProperties());
3631 pdbfilter.setModelNumbering(compNum);
3632 pdbfilter.writeFile(saveLocation, true, false, false);
3633 }
3634 currentAssembly.destroy();
3635 }
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647 private void saveAssembly(final File file, final String name, final List<Bond> bondList, final Atom[] atoms,
3648 final ForceField forceField, double[] coords, final int[] comparisonAtoms, final int nAU, final String description,
3649 final double finalRMSD, final int compNum, final int saveClusters) {
3650 saveAssembly(file, name, bondList, atoms, forceField, coords, comparisonAtoms, nAU, description,
3651 compNum, saveClusters);
3652 final String fileName = removeExtension(file.getName());
3653 try {
3654
3655 File csv = PDBFilter.version(new File(fileName + description + ".csv"));
3656 if (csv.createNewFile()) {
3657 BufferedWriter bw = new BufferedWriter(new FileWriter(csv, false));
3658 bw.append("rms\n");
3659 bw.append(format("%4.4f\n", finalRMSD));
3660 bw.close();
3661 } else {
3662 logger.warning(" Could not create CSV file \"" + csv.getName() + "\"");
3663 }
3664 } catch (Exception ex) {
3665 logger.info(ex + Utilities.stackTraceToString(ex));
3666 }
3667 }
3668 }
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093