/** * After the alignment changes (optAln, optLen, blockNum, at a minimum), many other properties * which depend on the superposition will be invalid. * * <p>This method re-runs a rigid superposition over the whole alignment and repopulates the * required properties, including RMSD (TotalRMSD) and TM-Score. * * @param afpChain * @param ca1 * @param ca2 Second set of ca atoms. Will be modified based on the superposition * @throws StructureException * @see {@link CECalculator#calc_rmsd(Atom[], Atom[], int, boolean)} contains much of the same * code, but stores results in a CECalculator instance rather than an AFPChain */ public static void updateSuperposition(AFPChain afpChain, Atom[] ca1, Atom[] ca2) throws StructureException { // Update ca information, because the atom array might also be changed afpChain.setCa1Length(ca1.length); afpChain.setCa2Length(ca2.length); // We need this to get the correct superposition int[] focusRes1 = afpChain.getFocusRes1(); int[] focusRes2 = afpChain.getFocusRes2(); if (focusRes1 == null) { focusRes1 = new int[afpChain.getCa1Length()]; afpChain.setFocusRes1(focusRes1); } if (focusRes2 == null) { focusRes2 = new int[afpChain.getCa2Length()]; afpChain.setFocusRes2(focusRes2); } if (afpChain.getNrEQR() == 0) return; // create new arrays for the subset of atoms in the alignment. Atom[] ca1aligned = new Atom[afpChain.getOptLength()]; Atom[] ca2aligned = new Atom[afpChain.getOptLength()]; int pos = 0; int[] blockLens = afpChain.getOptLen(); int[][][] optAln = afpChain.getOptAln(); assert (afpChain.getBlockNum() <= optAln.length); for (int block = 0; block < afpChain.getBlockNum(); block++) { for (int i = 0; i < blockLens[block]; i++) { int pos1 = optAln[block][0][i]; int pos2 = optAln[block][1][i]; Atom a1 = ca1[pos1]; Atom a2 = (Atom) ca2[pos2].clone(); ca1aligned[pos] = a1; ca2aligned[pos] = a2; pos++; } } // this can happen when we load an old XML serialization which did not support modern ChemComp // representation of modified residues. if (pos != afpChain.getOptLength()) { logger.warn( "AFPChainScorer getTMScore: Problems reconstructing alignment! nr of loaded atoms is " + pos + " but should be " + afpChain.getOptLength()); // we need to resize the array, because we allocated too many atoms earlier on. ca1aligned = (Atom[]) resizeArray(ca1aligned, pos); ca2aligned = (Atom[]) resizeArray(ca2aligned, pos); } // Superimpose the two structures in correspondance to the new alignment SVDSuperimposer svd = new SVDSuperimposer(ca1aligned, ca2aligned); Matrix matrix = svd.getRotation(); Atom shift = svd.getTranslation(); Matrix[] blockMxs = new Matrix[afpChain.getBlockNum()]; Arrays.fill(blockMxs, matrix); afpChain.setBlockRotationMatrix(blockMxs); Atom[] blockShifts = new Atom[afpChain.getBlockNum()]; Arrays.fill(blockShifts, shift); afpChain.setBlockShiftVector(blockShifts); for (Atom a : ca2aligned) { Calc.rotate(a, matrix); Calc.shift(a, shift); } // Calculate the RMSD and TM score for the new alignment double rmsd = SVDSuperimposer.getRMS(ca1aligned, ca2aligned); double tmScore = SVDSuperimposer.getTMScore(ca1aligned, ca2aligned, ca1.length, ca2.length); afpChain.setTotalRmsdOpt(rmsd); afpChain.setTMScore(tmScore); // Calculate the RMSD and TM score for every block of the new alignment double[] blockRMSD = new double[afpChain.getBlockNum()]; double[] blockScore = new double[afpChain.getBlockNum()]; for (int k = 0; k < afpChain.getBlockNum(); k++) { // Create the atom arrays corresponding to the aligned residues in the block Atom[] ca1block = new Atom[afpChain.getOptLen()[k]]; Atom[] ca2block = new Atom[afpChain.getOptLen()[k]]; int position = 0; for (int i = 0; i < blockLens[k]; i++) { int pos1 = optAln[k][0][i]; int pos2 = optAln[k][1][i]; Atom a1 = ca1[pos1]; Atom a2 = (Atom) ca2[pos2].clone(); ca1block[position] = a1; ca2block[position] = a2; position++; } if (position != afpChain.getOptLen()[k]) { logger.warn( "AFPChainScorer getTMScore: Problems reconstructing block alignment! nr of loaded atoms is " + pos + " but should be " + afpChain.getOptLen()[k]); // we need to resize the array, because we allocated too many atoms earlier on. ca1block = (Atom[]) resizeArray(ca1block, position); ca2block = (Atom[]) resizeArray(ca2block, position); } // Superimpose the two block structures SVDSuperimposer svdb = new SVDSuperimposer(ca1block, ca2block); Matrix matrixb = svdb.getRotation(); Atom shiftb = svdb.getTranslation(); for (Atom a : ca2block) { Calc.rotate(a, matrixb); Calc.shift(a, shiftb); } // Calculate the RMSD and TM score for the block double rmsdb = SVDSuperimposer.getRMS(ca1block, ca2block); double tmScoreb = SVDSuperimposer.getTMScore(ca1block, ca2block, ca1.length, ca2.length); blockRMSD[k] = rmsdb; blockScore[k] = tmScoreb; } afpChain.setOptRmsd(blockRMSD); afpChain.setBlockRmsd(blockRMSD); afpChain.setBlockScore(blockScore); }