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.cluster;
022
023import org.biojava.nbio.structure.*;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.List;
030
031/**
032 * The SubunitExtractor extracts the information of each protein {@link Chain}
033 * in a {@link Structure} and converts them into a List of {@link Subunit}.
034 * 
035 * @author Peter Rose
036 * @author Aleix Lafita
037 * @since 5.0.0
038 * 
039 */
040public class SubunitExtractor {
041
042        private static final Logger logger = LoggerFactory
043                        .getLogger(SubunitExtractor.class);
044
045        /** Prevent instantiation **/
046        private SubunitExtractor() {
047        }
048
049        /**
050         * Extract the information of each protein Chain in a Structure and converts
051         * them into a List of Subunit. The name of the Subunits is set to the
052         * {@link Chain#getName()}.
053         * 
054         * 
055         * @param structure
056         *            Structure object with protein Chains
057         * @param absMinLen
058         *            {@link SubunitClustererParameters#getAbsoluteMinimumSequenceLength()}
059         * @param fraction
060         *            {@link SubunitClustererParameters#getMinimumSequenceLengthFraction()}
061         * @param minLen
062         *            {@link SubunitClustererParameters#getMinimumSequenceLength()}
063         * @return List of Subunits
064         */
065        public static List<Subunit> extractSubunits(Structure structure,
066                        int absMinLen, double fraction, int minLen) {
067
068                // The extracted subunit container
069                List<Subunit> subunits = new ArrayList<Subunit>();
070
071                for (Chain c : structure.getPolyChains()) {
072                        // Only take protein chains
073                        if (c.isProtein()) {
074                                Atom[] ca = StructureTools.getRepresentativeAtomArray(c);
075                                logger.debug("Chain " + c.getId() + "; CA Atoms: " + ca.length + "; SEQRES: " + c.getSeqResSequence());
076                                if (ca.length==0)
077                                        continue;
078                                subunits.add(new Subunit(ca, c.getName(), null, structure));
079                        }
080                }
081
082                // Calculate the minimum length of a Subunit
083                int adjustedMinLen = calcAdjustedMinimumSequenceLength(subunits,
084                                absMinLen, fraction, minLen);
085                logger.debug("Adjusted minimum sequence length: " + adjustedMinLen);
086
087                // Filter out short Subunits
088                for (int s = subunits.size() - 1; s >= 0; s--) {
089                        if (subunits.get(s).size() < adjustedMinLen)
090                                subunits.remove(s);
091                }
092
093                return subunits;
094        }
095
096        /**
097         * Returns an adapted minimum sequence length. This method ensure that
098         * structure that only have short chains are not excluded by the
099         * minimumSequenceLength cutoff value.
100         * 
101         * @return adjustedMinimumSequenceLength
102         */
103        private static int calcAdjustedMinimumSequenceLength(
104                        List<Subunit> subunits, int absMinLen, double fraction, int minLen) {
105
106                int maxLength = Integer.MIN_VALUE;
107                int minLength = Integer.MAX_VALUE;
108
109                // Extract the length List, the min and the max
110                List<Integer> lengths = new ArrayList<Integer>();
111                for (int i = 0; i < subunits.size(); i++) {
112                        if (subunits.get(i).size() >= absMinLen) {
113                                maxLength = Math.max(subunits.get(i).size(), maxLength);
114                                minLength = Math.min(subunits.get(i).size(), minLength);
115                                lengths.add(subunits.get(i).size());
116
117                        }
118                }
119
120                int adjustedMinimumSequenceLength = minLen;
121
122                if (lengths.size() < 2)
123                        return adjustedMinimumSequenceLength;
124
125                // Calculate the median of the lengths
126                double median = 0;
127                Collections.sort(lengths);
128                if (lengths.size() % 2 == 1) {
129                        int middle = (lengths.size() - 1) / 2;
130                        median = lengths.get(middle);
131                } else {
132                        int middle2 = lengths.size() / 2;
133                        int middle1 = middle2 - 1;
134                        median = 0.5 * (lengths.get(middle1) + lengths.get(middle2));
135                }
136
137                // If the median * fraction is lower than the minLength
138                if (minLength >= median * fraction) {
139                        adjustedMinimumSequenceLength = Math.min(minLength, minLen);
140                }
141
142                return adjustedMinimumSequenceLength;
143        }
144}