All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.biojava.nbio.structure.symmetry.utils.SymmetryTools Maven / Gradle / Ivy

There is a newer version: 7.2.2
Show newest version
/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */
package org.biojava.nbio.structure.symmetry.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.vecmath.AxisAngle4d;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;

import org.biojava.nbio.structure.Atom;
import org.biojava.nbio.structure.Chain;
import org.biojava.nbio.structure.ChainImpl;
import org.biojava.nbio.structure.Group;
import org.biojava.nbio.structure.SVDSuperimposer;
import org.biojava.nbio.structure.Structure;
import org.biojava.nbio.structure.StructureException;
import org.biojava.nbio.structure.StructureIdentifier;
import org.biojava.nbio.structure.StructureImpl;
import org.biojava.nbio.structure.StructureTools;
import org.biojava.nbio.structure.align.ce.CECalculator;
import org.biojava.nbio.structure.align.helper.AlignTools;
import org.biojava.nbio.structure.align.model.AFPChain;
import org.biojava.nbio.structure.align.multiple.Block;
import org.biojava.nbio.structure.align.multiple.BlockImpl;
import org.biojava.nbio.structure.align.multiple.BlockSet;
import org.biojava.nbio.structure.align.multiple.BlockSetImpl;
import org.biojava.nbio.structure.align.multiple.MultipleAlignment;
import org.biojava.nbio.structure.align.multiple.MultipleAlignmentEnsemble;
import org.biojava.nbio.structure.align.multiple.MultipleAlignmentEnsembleImpl;
import org.biojava.nbio.structure.align.multiple.MultipleAlignmentImpl;
import org.biojava.nbio.structure.align.multiple.util.CoreSuperimposer;
import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentScorer;
import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentTools;
import org.biojava.nbio.structure.align.multiple.util.MultipleSuperimposer;
import org.biojava.nbio.structure.jama.Matrix;
import org.biojava.nbio.structure.symmetry.core.QuatSymmetryDetector;
import org.biojava.nbio.structure.symmetry.core.QuatSymmetryParameters;
import org.biojava.nbio.structure.symmetry.core.QuatSymmetryResults;
import org.biojava.nbio.structure.symmetry.core.Subunits;
import org.biojava.nbio.structure.symmetry.internal.CeSymmResult;
import org.biojava.nbio.structure.symmetry.internal.SymmetryAxes;
import org.jgrapht.UndirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility methods for the internal symmetry identification and manipulation.
 * 

* Methods include: blank out regions of DP Matrix, build symmetry graphs, get * rotation symmetry angles, split repeats in quaternary structure chains, * convert between symmetry formats (full, repeats, rotations), determine if two * symmetry axes are equivalent, get groups from representative Atoms. * * @author Spencer Bliven * @author Aleix Lafita * */ public class SymmetryTools { private static final Logger logger = LoggerFactory .getLogger(SymmetryTools.class); /** Prevent instantiation. */ private SymmetryTools() { } /** * Returns the "reset value" for graying out the main diagonal. If we're * blanking out the main diagonal, this value is always Integer.MIN_VALUE. *

* This is possible if {@code gradientPolyCoeff = Integer.MIN_VALUE} and * {@code gradientExpCoeff = 0}. * * @param unpenalizedScore * @param nResFromMainDiag * @param gradientPolyCoeff * @param gradientExpCoeff * @return */ private static double getResetVal(double unpenalizedScore, double nResFromMainDiag, double[] gradientPolyCoeff, double gradientExpCoeff) { if (unpenalizedScore == Double.NaN) return 0; // what else? // We can actually return a positive value if this is high enough double updateVal = unpenalizedScore; updateVal -= gradientExpCoeff * Math.pow(Math.E, -nResFromMainDiag); for (int p = 0; p < gradientPolyCoeff.length; p++) { updateVal -= gradientPolyCoeff[gradientPolyCoeff.length - 1 - p] * Math.pow(nResFromMainDiag, -p); } return updateVal; } /** * Grays out the main diagonal of a duplicated distance matrix. * * @param ca2 * @param rows * Number of rows * @param cols * Number of original columns * @param calculator * Used to get the matrix if origM is null * @param origM * starting matrix. If null, uses * {@link CECalculator#getMatMatrix()} * @param blankWindowSize * Width of section to gray out * @param gradientPolyCoeff * @param gradientExpCoeff * @return */ public static Matrix grayOutCEOrig(Atom[] ca2, int rows, int cols, CECalculator calculator, Matrix origM, int blankWindowSize, double[] gradientPolyCoeff, double gradientExpCoeff) { if (origM == null) { origM = new Matrix(calculator.getMatMatrix()); } // symmetry hack, disable main diagonal for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int diff = Math.abs(i - j); double resetVal = getResetVal(origM.get(i, j), diff, gradientPolyCoeff, gradientExpCoeff); if (diff < blankWindowSize) { origM.set(i, j, origM.get(i, j) + resetVal); } int diff2 = Math.abs(i - (j - ca2.length / 2)); // other side double resetVal2 = getResetVal(origM.get(i, j), diff2, gradientPolyCoeff, gradientExpCoeff); if (diff2 < blankWindowSize) { origM.set(i, j, origM.get(i, j) + resetVal2); } } } return origM; } public static Matrix grayOutPreviousAlignment(AFPChain afpChain, Atom[] ca2, int rows, int cols, CECalculator calculator, Matrix max, int blankWindowSize, double[] gradientPolyCoeff, double gradientExpCoeff) { max = grayOutCEOrig(ca2, rows, cols, calculator, max, blankWindowSize, gradientPolyCoeff, gradientExpCoeff); double[][] dist1 = calculator.getDist1(); double[][] dist2 = calculator.getDist2(); int[][][] optAln = afpChain.getOptAln(); int blockNum = afpChain.getBlockNum(); int[] optLen = afpChain.getOptLen(); // ca2 is circularly permutated int breakPoint = ca2.length / 2; for (int bk = 0; bk < blockNum; bk++) { for (int i = 0; i < optLen[bk]; i++) { int pos1 = optAln[bk][0][i]; int pos2 = optAln[bk][1][i]; int dist = blankWindowSize / 2; int start1 = Math.max(pos1 - dist, 0); int start2 = Math.max(pos2 - dist, 0); int end1 = Math.min(pos1 + dist, rows - 1); int end2 = Math.min(pos2 + dist, cols - 1); for (int i1 = start1; i1 < end1; i1++) { // blank diagonal of dist1 for (int k = 0; k < blankWindowSize / 2; k++) { if (i1 - k >= 0) { double resetVal = getResetVal( max.get(i1 - k, i1 - k), 0, gradientPolyCoeff, gradientExpCoeff); dist1[i1 - k][i1 - k] = resetVal; } else if (i1 + k < rows) { double resetVal = getResetVal( max.get(i1 + k, i1 + k), 0, gradientPolyCoeff, gradientExpCoeff); dist1[i1 + k][i1 + k] = resetVal; } } for (int j2 = start2; j2 < end2; j2++) { double resetVal = getResetVal(max.get(i1, j2), Math.abs(i1 - j2), gradientPolyCoeff, gradientExpCoeff); max.set(i1, j2, resetVal); if (j2 < breakPoint) { double resetVal2 = getResetVal( max.get(i1, j2 + breakPoint), Math.abs(i1 - (j2 + breakPoint)), gradientPolyCoeff, gradientExpCoeff); max.set(i1, j2 + breakPoint, resetVal2); } else { double resetVal2 = getResetVal( max.get(i1, j2 - breakPoint), Math.abs(i1 - (j2 - breakPoint)), gradientPolyCoeff, gradientExpCoeff); max.set(i1, j2 - breakPoint, resetVal2); } for (int k = 0; k < blankWindowSize / 2; k++) { if (j2 - k >= 0) { if (j2 - k < breakPoint) { double resetVal2 = getResetVal( max.get(j2 - k, j2 - k), 0, gradientPolyCoeff, gradientExpCoeff); dist2[j2 - k][j2 - k] = resetVal2; } else { double resetVal2 = getResetVal(max.get(j2 - k - breakPoint, j2 - k), 0, gradientPolyCoeff, gradientExpCoeff); dist2[j2 - k - breakPoint][j2 - k - breakPoint] = resetVal2; } } else if (j2 + k < cols) { if (j2 + k < breakPoint) { double resetVal2 = getResetVal( max.get(j2 + k, j2 + k), 0, gradientPolyCoeff, gradientExpCoeff); dist2[j2 + k][j2 + k] = resetVal2; } else { double resetVal2 = getResetVal(max.get(j2 + k - breakPoint, j2 + k), 0, gradientPolyCoeff, gradientExpCoeff); dist2[j2 + k - breakPoint][j2 + k - breakPoint] = resetVal2; } } } } } } } calculator.setDist1(dist1); calculator.setDist2(dist2); return max; } public Matrix getDkMatrix(Atom[] ca1, Atom[] ca2, int fragmentLength, double[] dist1, double[] dist2, int rows, int cols) { Matrix diffDistMax = Matrix.identity(ca1.length, ca2.length); for (int i = 0; i < rows; i++) { double score1 = 0; for (int x = 0; x < fragmentLength; x++) { score1 += dist1[i + x]; } for (int j = 0; j < cols; j++) { double score2 = 0; for (int y = 0; y < fragmentLength; y++) { score2 += dist2[j + y]; } // if the intramolecular distances are very similar // the two scores should be similar, // i.e. the difference is close to 0 diffDistMax.set(i, j, Math.abs(score1 - score2)); } } // symmetry hack, disable main diagonal for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int diff = Math.abs(i - j); if (diff < 15) { diffDistMax.set(i, j, 99); } int diff2 = Math.abs(i - (j - ca2.length / 2)); if (diff2 < 15) { diffDistMax.set(i, j, 99); } } } return diffDistMax; } public static Matrix blankOutPreviousAlignment(AFPChain afpChain, Atom[] ca2, int rows, int cols, CECalculator calculator, Matrix max, int blankWindowSize) { return grayOutPreviousAlignment(afpChain, ca2, rows, cols, calculator, max, blankWindowSize, new double[] { Integer.MIN_VALUE }, 0.0); } public static Matrix blankOutCEOrig(Atom[] ca2, int rows, int cols, CECalculator calculator, Matrix origM, int blankWindowSize) { return grayOutCEOrig(ca2, rows, cols, calculator, origM, blankWindowSize, new double[] { Integer.MIN_VALUE }, 0.0); } public static Matrix getDkMatrix(Atom[] ca1, Atom[] ca2, int k, int fragmentLength) { double[] dist1 = AlignTools.getDiagonalAtK(ca1, k); double[] dist2 = AlignTools.getDiagonalAtK(ca2, k); int rows = ca1.length - fragmentLength - k + 1; int cols = ca2.length - fragmentLength - k + 1; // Matrix that tracks similarity of a fragment of length fragmentLength // starting a position i,j. Matrix m2 = new Matrix(rows, cols); for (int i = 0; i < rows; i++) { double score1 = 0; for (int x = 0; x < fragmentLength; x++) { score1 += dist1[i + x]; } for (int j = 0; j < cols; j++) { double score2 = 0; for (int y = 0; y < fragmentLength; y++) { score2 += dist2[j + y]; } // if the intramolecular distances are very similar // the two scores should be similar, // i.e. the difference is close to 0 m2.set(i, j, Math.abs(score1 - score2)); } } return m2; } public static boolean[][] blankOutBreakFlag(AFPChain afpChain, Atom[] ca2, int rows, int cols, CECalculator calculator, boolean[][] breakFlag, int blankWindowSize) { int[][][] optAln = afpChain.getOptAln(); int blockNum = afpChain.getBlockNum(); int[] optLen = afpChain.getOptLen(); // ca2 is circularly permutated at this point. int breakPoint = ca2.length / 2; for (int bk = 0; bk < blockNum; bk++) { // Matrix m= afpChain.getBlockRotationMatrix()[bk]; // Atom shift = afpChain.getBlockShiftVector()[bk]; for (int i = 0; i < optLen[bk]; i++) { int pos1 = optAln[bk][0][i]; int pos2 = optAln[bk][1][i]; // blank out area around these positions... int dist = blankWindowSize; int start1 = Math.max(pos1 - dist, 0); int start2 = Math.max(pos2 - dist, 0); int end1 = Math.min(pos1 + dist, rows - 1); int end2 = Math.min(pos2 + dist, cols - 1); for (int i1 = start1; i1 < end1; i1++) { for (int j2 = start2; j2 < end2; j2++) { // System.out.println(i1 + " " + j2 + " (***)"); breakFlag[i1][j2] = true; if (j2 < breakPoint) { breakFlag[i1][j2 + breakPoint] = true; } } } } } return breakFlag; } /** * Returns the magnitude of the angle between the first and second * blocks of {@code afpChain}, measured in degrees. This is always a * positive value (unsigned). * * @param afpChain * @param ca1 * @param ca2 * @return */ public static double getAngle(AFPChain afpChain, Atom[] ca1, Atom[] ca2) { Matrix rotation = afpChain.getBlockRotationMatrix()[0]; return Math.acos(rotation.trace() - 1) * 180 / Math.PI; } /** * Converts a set of AFP alignments into a Graph of aligned residues, where * each vertex is a residue and each edge means the connection between the * two residues in one of the alignments. * * @param afps * List of AFPChains * @param atoms * Atom array of the symmetric structure * @param undirected * if true, the graph is undirected * * @return adjacency List of aligned residues */ public static List> buildSymmetryGraph(List afps, Atom[] atoms, boolean undirected) { List> graph = new ArrayList>(); for (int n = 0; n < atoms.length; n++) { graph.add(new ArrayList()); } for (int k = 0; k < afps.size(); k++) { for (int i = 0; i < afps.get(k).getOptAln().length; i++) { for (int j = 0; j < afps.get(k).getOptAln()[i][0].length; j++) { Integer res1 = afps.get(k).getOptAln()[i][0][j]; Integer res2 = afps.get(k).getOptAln()[i][1][j]; graph.get(res1).add(res2); if (undirected) graph.get(res2).add(res1); } } } return graph; } /** * Converts a self alignment into a directed jGraphT of aligned residues, * where each vertex is a residue and each edge means the equivalence * between the two residues in the self-alignment. * * @param selfAlignment * AFPChain * * @return alignment Graph */ public static UndirectedGraph buildSymmetryGraph( AFPChain selfAlignment) { UndirectedGraph graph = new SimpleGraph( DefaultEdge.class); for (int i = 0; i < selfAlignment.getOptAln().length; i++) { for (int j = 0; j < selfAlignment.getOptAln()[i][0].length; j++) { Integer res1 = selfAlignment.getOptAln()[i][0][j]; Integer res2 = selfAlignment.getOptAln()[i][1][j]; graph.addVertex(res1); graph.addVertex(res2); graph.addEdge(res1, res2); } } return graph; } /** * Method that converts the symmetric units of a structure into different * chains, so that internal symmetry can be translated into quaternary. *

* Application: obtain the internal symmetry axis with the quaternary * symmetry code in biojava or calculate independent repeat properties. * * @param symmetry * CeSymmResult * @return Structure with different chains for every symmetric unit */ public static Structure getQuaternaryStructure(CeSymmResult symmetry) { if (!symmetry.isRefined()) throw new IllegalArgumentException("The symmetry result " + "is not refined, repeats cannot be defined"); Atom[] atoms = symmetry.getAtoms(); Structure symm = new StructureImpl(); symm.setStructureIdentifier(symmetry.getStructureId()); symm.setChains(new ArrayList()); char chainID = 'A'; // Create new structure containing the repeat atoms for (int i = 0; i < symmetry.getMultipleAlignment().size(); i++) { Chain newCh = new ChainImpl(); Block align = symmetry.getMultipleAlignment().getBlock(0); // Get the start and end of the repeat int res1 = align.getStartResidue(i); int res2 = align.getFinalResidue(i); Atom[] repeat = Arrays.copyOfRange(atoms, res1, res2 + 1); for (int k = 0; k < repeat.length; k++) { Group g = (Group) repeat[k].getGroup().clone(); newCh.addGroup(g); } newCh.setChainID(chainID + ""); chainID++; symm.addChain(newCh); } return symm; } /** * Method that converts a repeats symmetric alignment into an alignment of * whole structures. *

* Example: if the structure has repeats A,B and C, the original alignment * is A-B-C, and the returned alignment is ABC-BCA-CAB. * * @param symm * CeSymmResult * @return MultipleAlignment of the full structure superpositions */ public static MultipleAlignment toFullAlignment(CeSymmResult symm) { if (!symm.isRefined()) throw new IllegalArgumentException("The symmetry result " + "is not refined, repeats cannot be defined"); MultipleAlignment full = symm.getMultipleAlignment().clone(); for (int str = 1; str < full.size(); str++) { // Create a new Block with swapped AlignRes (move first to last) Block b = full.getBlock(full.getBlocks().size() - 1).clone(); b.getAlignRes().add(b.getAlignRes().get(0)); b.getAlignRes().remove(0); full.getBlockSet(0).getBlocks().add(b); } return full; } /** * Method that converts a symmetry alignment into an alignment of the * repeats only, as new independent structures. *

* This method changes the structure identifiers, the Atom arrays and * re-scles the aligned residues in the Blocks corresponding to those * changes. *

* Application: display superimposed repeats in Jmol. * * @param result * CeSymmResult of symmetry * @return MultipleAlignment of the repeats * @throws StructureException */ public static MultipleAlignment toRepeatsAlignment(CeSymmResult result) throws StructureException { if (!result.isRefined()) throw new IllegalArgumentException("The symmetry result " + "is not refined, repeats cannot be defined"); MultipleAlignment msa = result.getMultipleAlignment(); MultipleAlignmentEnsemble newEnsemble = msa.getEnsemble().clone(); newEnsemble.setStructureIdentifiers(result.getRepeatsID()); // Modify atom arrays to include the repeat atoms only List atomArrays = new ArrayList(); Structure divided = SymmetryTools.getQuaternaryStructure(result); MultipleAlignment repeats = newEnsemble.getMultipleAlignment(0); Block block = repeats.getBlock(0); for (int i = 0; i < result.getMultipleAlignment().size(); i++) { Structure newStr = new StructureImpl(); Chain newCh = divided.getChain(i); newStr.addChain(newCh); Atom[] repeat = StructureTools.getRepresentativeAtomArray(newCh); atomArrays.add(repeat); } newEnsemble.setAtomArrays(atomArrays); for (int su = 0; su < block.size(); su++) { Integer start = block.getStartResidue(su); // Normalize aligned residues for (int res = 0; res < block.length(); res++) { Integer residue = block.getAlignRes().get(su).get(res); if (residue != null) residue -= start; block.getAlignRes().get(su).set(res, residue); } } return repeats; } /** * Converts a refined symmetry AFPChain alignment into the standard * representation of symmetry in a MultipleAlignment, that contains the * entire Atom array of the strcuture and the symmetric repeats are orgaized * in different rows in a single Block. * * @param symm * AFPChain created with a symmetry algorithm and refined * @param atoms * Atom array of the entire structure * @return MultipleAlignment format of the symmetry * @throws StructureException */ public static MultipleAlignment fromAFP(AFPChain symm, Atom[] atoms) throws StructureException { if (!symm.getAlgorithmName().contains("symm")) { throw new IllegalArgumentException( "The input alignment is not a symmetry alignment."); } MultipleAlignmentEnsemble e = new MultipleAlignmentEnsembleImpl(symm, atoms, atoms, false); e.setAtomArrays(new ArrayList()); StructureIdentifier name = null; if (e.getStructureIdentifiers() != null) { if (!e.getStructureIdentifiers().isEmpty()) name = e.getStructureIdentifiers().get(0); } else name = atoms[0].getGroup().getChain().getStructure() .getStructureIdentifier(); e.setStructureIdentifiers(new ArrayList()); MultipleAlignment result = new MultipleAlignmentImpl(); BlockSet bs = new BlockSetImpl(result); Block b = new BlockImpl(bs); b.setAlignRes(new ArrayList>()); int order = symm.getBlockNum(); for (int su = 0; su < order; su++) { List residues = e.getMultipleAlignment(0).getBlock(su) .getAlignRes().get(0); b.getAlignRes().add(residues); e.getStructureIdentifiers().add(name); e.getAtomArrays().add(atoms); } e.getMultipleAlignments().set(0, result); result.setEnsemble(e); CoreSuperimposer imposer = new CoreSuperimposer(); imposer.superimpose(result); updateSymmetryScores(result); return result; } /** * Determines if two symmetry axis are equivalent inside the error * threshold. It only takes into account the direction of the vector where * the rotation is made: the angle and translation are not taken into * account. * * @param axis1 * @param axis2 * @param epsilon * error allowed in the axis comparison * @return true if equivalent, false otherwise */ public static boolean equivalentAxes(Matrix4d axis1, Matrix4d axis2, double epsilon) { AxisAngle4d rot1 = new AxisAngle4d(); rot1.set(axis1); AxisAngle4d rot2 = new AxisAngle4d(); rot2.set(axis2); // rot1.epsilonEquals(rot2, error); //that also compares angle // L-infinite distance without comparing the angle (epsilonEquals) List sameDir = new ArrayList(); sameDir.add(Math.abs(rot1.x - rot2.x)); sameDir.add(Math.abs(rot1.y - rot2.y)); sameDir.add(Math.abs(rot1.z - rot2.z)); List otherDir = new ArrayList(); otherDir.add(Math.abs(rot1.x + rot2.x)); otherDir.add(Math.abs(rot1.y + rot2.y)); otherDir.add(Math.abs(rot1.z + rot2.z)); Double error = Math.min(Collections.max(sameDir), Collections.max(otherDir)); return error < epsilon; } /** * Given a symmetry result, it calculates the overall global symmetry, * factoring out the alignment and detection steps of * {@link QuatSymmetryDetector} algorithm. * * @param result * symmetry result * @return global symmetry results * @throws StructureException */ public static QuatSymmetryResults getQuaternarySymmetry(CeSymmResult result) throws StructureException { // Obtain the clusters of aligned Atoms and repeat variables MultipleAlignment repeats = SymmetryTools.toRepeatsAlignment(result); List alignedCA = repeats.getAtomArrays(); List corePos = MultipleAlignmentTools.getCorePositions(repeats .getBlock(0)); List caCoords = new ArrayList(); List folds = new ArrayList(); List pseudo = new ArrayList(); List chainIds = new ArrayList(); List models = new ArrayList(); List seqIDmin = new ArrayList(); List seqIDmax = new ArrayList(); List clusterIDs = new ArrayList(); int fold = 1; for (int str = 0; str < alignedCA.size(); str++) { Atom[] array = alignedCA.get(str); List points = new ArrayList(); List alignedRes = repeats.getBlock(0).getAlignRes() .get(str); for (int pos = 0; pos < alignedRes.size(); pos++) { Integer residue = alignedRes.get(pos); if (residue == null) continue; else if (!corePos.contains(pos)) continue; Atom a = array[residue]; points.add(new Point3d(a.getCoords())); } caCoords.add(points.toArray(new Point3d[points.size()])); if (alignedCA.size() % fold == 0) { folds.add(fold); // the folds are the common denominators } fold++; pseudo.add(false); chainIds.add(alignedCA.get(str)[0].getGroup().getChainId()); models.add(0); seqIDmax.add(1.0); seqIDmin.add(1.0); clusterIDs.add(0); } // Create directly the repeats, because we know the aligned CA Subunits globalSubunits = new Subunits(caCoords, clusterIDs, pseudo, seqIDmin, seqIDmax, folds, chainIds, models); // Quaternary Symmetry Detection QuatSymmetryParameters param = new QuatSymmetryParameters(); QuatSymmetryResults gSymmetry = QuatSymmetryDetector.calcQuatSymmetry( globalSubunits, param); return gSymmetry; } /** * Returns true a symmetry multiple alignment has been refined, false * otherwise. *

* For a refined alignment only one Block with no repeated residues is * necessary. Sufficient condition is not tested (only known from the * algorithm or CeSymmResult). * * @param symm * the symmetry alignment * @return true if the alignment is refined */ public static boolean isRefined(MultipleAlignment symm) { if (symm.getBlocks().size() > 1) { return false; } else if (symm.size() < 2) return false; else { List alreadySeen = new ArrayList(); List> align = symm.getBlock(0).getAlignRes(); for (int str = 0; str < symm.size(); str++) { for (int res = 0; res < align.get(str).size(); res++) { Integer residue = align.get(str).get(res); if (residue == null) continue; if (alreadySeen.contains(residue)) { return false; } else { alreadySeen.add(residue); } } } // end of all repeats return true; } } /** * Returns true if the symmetry alignment is significant, false otherwise. *

* For a symmetry alignment to be significant, the alignment has to be * refined and the TM-score has to be higher than the threshold. *

* It is recommended to use the {@link CeSymmResult#isSignificant()} method * instead. * * @param msa * @param symmetryThreshold * @return * @throws StructureException */ public static boolean isSignificant(MultipleAlignment msa, double symmetryThreshold) throws StructureException { // Order/refinement check if (!SymmetryTools.isRefined(msa)) return false; // TM-score cutoff double tm = 0.0; if (msa.getScore(MultipleAlignmentScorer.AVGTM_SCORE) == null) tm = MultipleAlignmentScorer.getAvgTMScore(msa); else tm = msa.getScore(MultipleAlignmentScorer.AVGTM_SCORE); if (tm < symmetryThreshold) return false; return true; } /** * Returns the List of Groups of the corresponding representative Atom * array. The representative Atom array needs to fulfill: no two Atoms are * from the same Group and Groups are sequential (connected in the original * Structure), except if they are from different Chains. * * @param rAtoms * array of representative Atoms (CA, P, etc). * @return List of Groups */ public static List getGroups(Atom[] rAtoms) { List groups = new ArrayList(rAtoms.length); for (Atom a : rAtoms) { Group g = a.getGroup(); if (g != null) groups.add(g); else logger.info("Group not found for representative Atom {}", a); } return groups; } /** * Calculates the set of symmetry operation Matrices (transformations) of * the new alignment, based on the symmetry relations in the SymmetryAxes * object. It sets the transformations to the input MultipleAlignment and * SymmetryAxes objects. If the SymmetryAxes object is null, the * superposition of the repeats is done without symmetry constraints. *

* This method also sets the scores (RMSD and TM-score) after the new * superposition has been updated. * * @param axes * SymmetryAxes object. It will be modified. * @param msa * MultipleAlignment. It will be modified. * @param atoms * Atom array of the structure */ public static void updateSymmetryTransformation(SymmetryAxes axes, MultipleAlignment msa, Atom[] atoms) throws StructureException { List> block = msa.getBlocks().get(0).getAlignRes(); int length = block.get(0).size(); if (axes != null) { for (int t = 0; t < axes.getElementaryAxes().size(); t++) { Matrix4d axis = axes.getElementaryAxes().get(t); List chain1 = axes.getRepeatRelation(t).get(0); List chain2 = axes.getRepeatRelation(t).get(1); // Calculate the aligned atom arrays List list1 = new ArrayList(); List list2 = new ArrayList(); for (int pair = 0; pair < chain1.size(); pair++) { int p1 = chain1.get(pair); int p2 = chain2.get(pair); for (int k = 0; k < length; k++) { Integer pos1 = block.get(p1).get(k); Integer pos2 = block.get(p2).get(k); if (pos1 != null && pos2 != null) { list1.add(atoms[pos1]); list2.add(atoms[pos2]); } } } Atom[] arr1 = list1.toArray(new Atom[list1.size()]); Atom[] arr2 = list2.toArray(new Atom[list2.size()]); // Calculate the new transformation information if (arr1.length > 0 && arr2.length > 0) { SVDSuperimposer svd = new SVDSuperimposer(arr1, arr2); axis = svd.getTransformation(); axes.updateAxis(t, axis); } // Get the transformations from the SymmetryAxes List transformations = new ArrayList(); for (int su = 0; su < msa.size(); su++) { transformations.add(axes.getRepeatTransform(su)); } msa.getBlockSet(0).setTransformations(transformations); } } else { MultipleSuperimposer imposer = new CoreSuperimposer(); imposer.superimpose(msa); } updateSymmetryScores(msa); } /** * Update the scores (TM-score and RMSD) of a symmetry multiple alignment. * This method does not redo the superposition of the alignment. * * @param symm * Symmetry Multiple Alignment of Repeats * @throws StructureException */ public static void updateSymmetryScores(MultipleAlignment symm) throws StructureException { // Multiply by the order of symmetry to normalize score double tmScore = MultipleAlignmentScorer.getAvgTMScore(symm) * symm.size(); double rmsd = MultipleAlignmentScorer.getRMSD(symm); symm.putScore(MultipleAlignmentScorer.AVGTM_SCORE, tmScore); symm.putScore(MultipleAlignmentScorer.RMSD, rmsd); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy