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