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

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

/*
 *                    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 java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import javax.vecmath.Matrix4d;

import org.biojava.nbio.structure.Atom;
import org.biojava.nbio.structure.Calc;
import org.biojava.nbio.structure.Chain;
import org.biojava.nbio.structure.Group;
import org.biojava.nbio.structure.GroupType;
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.AlignUtils;
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.MultipleSuperimposer;
import org.biojava.nbio.structure.cluster.Subunit;
import org.biojava.nbio.structure.cluster.SubunitClustererMethod;
import org.biojava.nbio.structure.cluster.SubunitClustererParameters;
import org.biojava.nbio.structure.geometry.SuperPositions;
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.internal.CeSymmResult;
import org.biojava.nbio.structure.symmetry.internal.SymmetryAxes;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility methods for symmetry (quaternary and internal) detection and result
 * manipulation.
 *
 * @author Spencer Bliven
 * @author Aleix Lafita
 * @author Peter Rose
 *
 */
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 (Double.isNaN(unpenalizedScore)) 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 = AlignUtils.getDiagonalAtK(ca1, k); double[] dist2 = AlignUtils.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 Graph buildSymmetryGraph( AFPChain selfAlignment) { Graph 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 * structures, so that they can be individually visualized. * * @param symmetry * CeSymmResult * @throws StructureException * @result List of structures, by repeat index sequentially * */ public static List divideStructure(CeSymmResult symmetry) throws StructureException { if (!symmetry.isRefined()) throw new IllegalArgumentException("The symmetry result " + "is not refined, repeats cannot be defined"); int order = symmetry.getMultipleAlignment().size(); Atom[] atoms = symmetry.getAtoms(); Set allGroups = StructureTools.getAllGroupsFromSubset(atoms, GroupType.HETATM); List repeatsId = symmetry.getRepeatsID(); List repeats = new ArrayList(order); // Create new structure containing the repeat atoms for (int i = 0; i < order; i++) { Structure s = new StructureImpl(); s.addModel(new ArrayList(1)); s.setStructureIdentifier(repeatsId.get(i)); Block align = symmetry.getMultipleAlignment().getBlock(0); // Get the start and end of the repeat // Repeats are always sequential blocks int res1 = align.getStartResidue(i); int res2 = align.getFinalResidue(i); // All atoms from the repeat, used for ligand search // AA have an average of 8.45 atoms, so guess capacity with that List repeat = new ArrayList<>(Math.max(9*(res2-res1+1),9)); // speedy chain lookup Chain prevChain = null; for(int k=res1;k<=res2; k++) { Group g = atoms[k].getGroup(); prevChain = StructureTools.addGroupToStructure(s, g, 0, prevChain,true); repeat.addAll(g.getAtoms()); } List ligands = StructureTools.getLigandsByProximity( allGroups, repeat.toArray(new Atom[repeat.size()]), StructureTools.DEFAULT_LIGAND_PROXIMITY_CUTOFF); logger.warn("Adding {} ligands to {}",ligands.size(), symmetry.getMultipleAlignment().getStructureIdentifier(i)); for( Group ligand : ligands) { prevChain = StructureTools.addGroupToStructure(s, ligand, 0, prevChain,true); } repeats.add(s); } return repeats; } /** * 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(); List repSt = SymmetryTools.divideStructure(result); MultipleAlignment repeats = newEnsemble.getMultipleAlignment(0); Block block = repeats.getBlock(0); List atomArrays = new ArrayList(); for (Structure s : repSt) atomArrays.add(StructureTools.getRepresentativeAtomArray(s)); 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; } /** * 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 subunits of the repeats List atoms = toRepeatsAlignment(result).getAtomArrays(); List subunits = atoms.stream() .map(a -> new Subunit(a, null, null, null)) .collect(Collectors.toList()); // The clustering thresholds are set to 0 so that all always merged SubunitClustererParameters cp = new SubunitClustererParameters(); cp.setClustererMethod(SubunitClustererMethod.STRUCTURE); cp.setRMSDThreshold(10.0); cp.setStructureCoverageThreshold(0.0); QuatSymmetryParameters sp = new QuatSymmetryParameters(); QuatSymmetryResults gSymmetry = QuatSymmetryDetector .calcGlobalSymmetry(subunits, sp, cp); return gSymmetry; } /** * 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. */ public static void updateSymmetryTransformation(SymmetryAxes axes, MultipleAlignment msa) throws StructureException { List> block = msa.getBlocks().get(0).getAlignRes(); int length = block.get(0).size(); if (axes != null) { for (int level = 0; level < axes.getNumLevels(); level++) { // Calculate the aligned atom arrays to superimpose List list1 = new ArrayList(); List list2 = new ArrayList(); for (int firstRepeat : axes.getFirstRepeats(level)) { Matrix4d transform = axes.getRepeatTransform(firstRepeat); List> relation = axes.getRepeatRelation( level, firstRepeat); for (int index = 0; index < relation.get(0).size(); index++) { int p1 = relation.get(0).get(index); int p2 = relation.get(1).get(index); 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) { Atom a = (Atom) msa.getAtomArrays().get(p1)[pos1] .clone(); Atom b = (Atom) msa.getAtomArrays().get(p2)[pos2] .clone(); Calc.transform(a, transform); Calc.transform(b, transform); list1.add(a); list2.add(b); } } } } 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) { Matrix4d axis = SuperPositions.superpose( Calc.atomsToPoints(arr1), Calc.atomsToPoints(arr2)); axes.updateAxis(level, 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); } /** * Returns the representative Atom Array of the first model, if the * structure is NMR, or the Array for each model, if it is a biological * assembly with multiple models. * * @param structure * @return representative Atom[] */ public static Atom[] getRepresentativeAtoms(Structure structure) { if (structure.isNmr()) return StructureTools.getRepresentativeAtomArray(structure); else { // Get Atoms of all models List atomList = new ArrayList(); for (int m = 0; m < structure.nrModels(); m++) { for (Chain c : structure.getModel(m)) atomList.addAll(Arrays.asList(StructureTools .getRepresentativeAtomArray(c))); } return atomList.toArray(new Atom[0]); } } /** * Find valid symmetry orders for a given stoichiometry. For instance, an * A6B4 protein would give [1,2] because (A6B4)1 and (A3B2)2 are valid * decompositions. * * @param stoichiometry * List giving the number of copies in each Subunit cluster * @return The common factors of the stoichiometry */ public static List getValidFolds(List stoichiometry) { List denominators = new ArrayList(); if (stoichiometry.isEmpty()) return denominators; int nChains = Collections.max(stoichiometry); // Remove duplicate stoichiometries Set nominators = new TreeSet(stoichiometry); // find common denominators for (int d = 1; d <= nChains; d++) { boolean isDivisable = true; for (Integer n : nominators) { if (n % d != 0) { isDivisable = false; break; } } if (isDivisable) { denominators.add(d); } } return denominators; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy