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}