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