001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.symmetry.internal; 022 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.List; 026 027import org.biojava.nbio.structure.Atom; 028import org.biojava.nbio.structure.PdbId; 029import org.biojava.nbio.structure.ResidueNumber; 030import org.biojava.nbio.structure.ResidueRange; 031import org.biojava.nbio.structure.StructureException; 032import org.biojava.nbio.structure.StructureIdentifier; 033import org.biojava.nbio.structure.SubstructureIdentifier; 034import org.biojava.nbio.structure.align.model.AFPChain; 035import org.biojava.nbio.structure.align.multiple.Block; 036import org.biojava.nbio.structure.align.multiple.MultipleAlignment; 037import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentScorer; 038import org.biojava.nbio.structure.symmetry.internal.CESymmParameters.RefineMethod; 039import org.biojava.nbio.structure.symmetry.internal.CESymmParameters.SymmetryType; 040import org.biojava.nbio.structure.symmetry.utils.SymmetryTools; 041 042/** 043 * This Class stores all the relevant information of an internal symmetry result 044 * obtained with CeSymm. The purpose is to carry all the information packed 045 * during the calculations and return a single object. 046 * 047 * @author Aleix Lafita 048 * @since 4.2.0 049 * 050 */ 051public class CeSymmResult { 052 053 private MultipleAlignment multipleAlignment; 054 private AFPChain selfAlignment; 055 056 private StructureIdentifier structureId; 057 private Atom[] atoms; 058 059 private CESymmParameters params; 060 private SymmetryAxes axes = new SymmetryAxes(); 061 private String symmGroup; 062 063 private int numRepeats; 064 private boolean refined; 065 066 /** 067 * Conditions checked are: score above the threshold, number of repeats 068 * higher than 1 and refinement succeeded. 069 * 070 * @return true if significant, false otherwise 071 */ 072 public boolean isSignificant() { 073 074 // In any case if the order is 1 it is asymmetric 075 if (numRepeats < 2) 076 return false; 077 078 // If the TM-Score before refinement is below the threshold 079 if (selfAlignment.getTMScore() < params.getUnrefinedScoreThreshold()) 080 return false; 081 082 // If the refinement was attempted 083 if (params.getRefineMethod() != RefineMethod.NOT_REFINED) { 084 // Check for minimum length 085 if (multipleAlignment == null || multipleAlignment.getCoreLength() < params.getMinCoreLength()) 086 return false; 087 // Allow 90% of original TM-Score theshold 088 if (multipleAlignment.getScore(MultipleAlignmentScorer.AVGTM_SCORE) < params 089 .getRefinedScoreThreshold()) 090 return false; 091 return true; 092 } 093 094 return true; 095 } 096 097 /** 098 * Return the symmetric repeats as structure identifiers, if the result is 099 * symmetric and it was refined, return null otherwise. 100 * 101 * @return List of StructureIdentifiers or null if not defined 102 * @throws StructureException 103 */ 104 public List<StructureIdentifier> getRepeatsID() throws StructureException { 105 106 if (!isRefined()) 107 return null; 108 109 List<StructureIdentifier> repeats = new ArrayList<StructureIdentifier>( 110 numRepeats); 111 112 PdbId pdbId = structureId.toCanonical().getPdbId(); 113 Block align = multipleAlignment.getBlocks().get(0); 114 115 for (int su = 0; su < numRepeats; su++) { 116 // Get the start and end residues of the repeat 117 ResidueNumber res1 = atoms[align.getStartResidue(su)].getGroup() 118 .getResidueNumber(); 119 ResidueNumber res2 = atoms[align.getFinalResidue(su)].getGroup() 120 .getResidueNumber(); 121 ResidueRange range = new ResidueRange(res1.getChainName(), res1, res2); 122 123 StructureIdentifier id = new SubstructureIdentifier(pdbId, 124 Arrays.asList(range)); 125 126 repeats.add(id); 127 } 128 return repeats; 129 } 130 131 @Override 132 public String toString() { 133 return structureId + ", symmGroup=" + getSymmGroup() + ", numRepeats=" 134 + numRepeats + ", symmLevels=" + axes.getNumLevels() 135 + ", refined=" + refined + " | " + params; 136 } 137 138 public MultipleAlignment getMultipleAlignment() { 139 return multipleAlignment; 140 } 141 142 public void setMultipleAlignment(MultipleAlignment multipleAlignment) { 143 this.multipleAlignment = multipleAlignment; 144 } 145 146 public AFPChain getSelfAlignment() { 147 return selfAlignment; 148 } 149 150 public void setSelfAlignment(AFPChain selfAlignment) { 151 this.selfAlignment = selfAlignment; 152 } 153 154 public CESymmParameters getParams() { 155 return params; 156 } 157 158 public void setParams(CESymmParameters params) { 159 this.params = params.clone(); 160 } 161 162 public SymmetryAxes getAxes() { 163 return axes; 164 } 165 166 public void setAxes(SymmetryAxes axes) { 167 this.axes = axes; 168 } 169 170 /** 171 * Return the symmetry order determined by the order detector if the 172 * symmetry is significant. Return 1 otherwise. 173 * 174 * @return the order of symmetry if the result is significant 175 */ 176 public int getNumRepeats() { 177 if (isSignificant()) 178 return numRepeats; 179 else 180 return 1; 181 } 182 183 public void setNumRepeats(int symmOrder) { 184 this.numRepeats = symmOrder; 185 } 186 187 public boolean isRefined() { 188 return refined; 189 } 190 191 public void setRefined(boolean refined) { 192 this.refined = refined; 193 } 194 195 public String getSymmGroup() { 196 // Lazily calculate the symmetry group if significant 197 if (symmGroup == null) { 198 if (isSignificant()) { 199 if (isRefined()) { 200 try { 201 symmGroup = SymmetryTools.getQuaternarySymmetry(this) 202 .getSymmetry(); 203 } catch (StructureException e) { 204 symmGroup = "C1"; 205 } 206 if (symmGroup.equals("C1")) 207 symmGroup = "R"; // could not find group 208 } else { 209 // in case significant but not refined 210 if (axes.getElementaryAxis(0).getSymmType() 211 .equals(SymmetryType.CLOSED)) 212 symmGroup = "C" + numRepeats; 213 else 214 symmGroup = "R"; 215 } 216 } else 217 // case asymmetric 218 symmGroup = "C1"; 219 } 220 return symmGroup; 221 } 222 223 public void setSymmGroup(String symmGroup) { 224 this.symmGroup = symmGroup; 225 } 226 227 public Atom[] getAtoms() { 228 return atoms; 229 } 230 231 public void setAtoms(Atom[] atoms) { 232 this.atoms = atoms; 233 } 234 235 public int getSymmLevels() { 236 return axes.getNumLevels(); 237 } 238 239 public StructureIdentifier getStructureId() { 240 return structureId; 241 } 242 243 public void setStructureId(StructureIdentifier structureId) { 244 this.structureId = structureId; 245 } 246 247 /** 248 * Return a String describing the reasons for the CE-Symm final decision in 249 * this particular result. 250 * 251 * @return String decision reason 252 */ 253 public String getReason() { 254 // Cases: 255 // 1. Asymmetric because insignificant self-alignment (1itb.A_1-100) 256 double tm = selfAlignment.getTMScore(); 257 if (tm < params.getUnrefinedScoreThreshold()) { 258 return String.format("Insignificant self-alignment (TM=%.2f)", tm); 259 } 260 // 2. Asymmetric because order detector returned 1 261 if (numRepeats == 1) { 262 return String.format( 263 "Order detector found asymmetric alignment (TM=%.2f)", tm); 264 } 265 266 // Check that the user requested refinement 267 if (params.getRefineMethod() != RefineMethod.NOT_REFINED) { 268 // 3. Asymmetric because refinement failed 269 if (!refined) { 270 return "Refinement failed"; 271 } 272 tm = multipleAlignment 273 .getScore(MultipleAlignmentScorer.AVGTM_SCORE); 274 // 4. Asymmetric because refinement & optimization were not 275 // significant 276 if (!isSignificant()) { 277 return String.format( 278 "Refinement was not significant (TM=%.2f)", tm); 279 } 280 } else { 281 // 4. Not refined, but result was not significant 282 if (!isSignificant()) { 283 return String 284 .format("Result was not significant (TM=%.2f)", tm); 285 } 286 } 287 288 String hierarchical = ""; 289 if (axes.getNumLevels() > 1) { 290 hierarchical = String.format("; Contains %d levels of symmetry", 291 axes.getNumLevels()); 292 } 293 // 5. Symmetric. 294 // a. Open. Give # repeats (1n0r.A) 295 if (axes.getElementaryAxis(0).getSymmType() == SymmetryType.OPEN) { 296 return String.format("Contains %d open repeats (TM=%.2f)%s", 297 getNumRepeats(), tm, hierarchical); 298 } 299 // b. Closed, non-hierarchical (1itb.A) 300 // c. Closed, heirarchical (4gcr) 301 return String.format("Significant (TM=%.2f)%s", tm, hierarchical); 302 } 303 304}