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.core;
022
023import org.biojava.nbio.structure.Atom;
024
025import javax.vecmath.Point3d;
026import java.util.*;
027
028/**
029 * Wraps a sequence clustering with structural information
030 */
031public class ChainClusterer  {
032        private List<SequenceAlignmentCluster> seqClusters = new ArrayList<SequenceAlignmentCluster>();
033        private boolean modified = true;
034
035        private List<Atom[]> caAligned = new ArrayList<Atom[]>();
036        private List<Point3d[]> caCoords = new ArrayList<Point3d[]>();
037
038        public ChainClusterer(List<SequenceAlignmentCluster> seqClusters) {
039                this.seqClusters = seqClusters;
040                this.modified = true;
041        }
042
043        public List<Point3d[]> getCalphaCoordinates() {
044                run();
045                return caCoords;
046        }
047
048        public List<Atom[]> getCalphaTraces() {
049                run();
050                return caAligned;
051        }
052
053        public List<String> getChainIds() {
054                run();
055                List<String> chainIdList = new ArrayList<String>();
056
057                for (int i = 0; i < seqClusters.size(); i++) {
058                        SequenceAlignmentCluster cluster = seqClusters.get(i);
059                        for (String chainId: cluster.getChainIds()) {
060                                chainIdList.add(chainId);
061                        }
062                }
063                return chainIdList;
064        }
065
066
067        public List<Integer> getModelNumbers() {
068                run();
069                List<Integer> modNumbers = new ArrayList<Integer>();
070
071                for (int i = 0; i < seqClusters.size(); i++) {
072                        SequenceAlignmentCluster cluster = seqClusters.get(i);
073                        for (Integer number: cluster.getModelNumbers()) {
074                                modNumbers.add(number);
075                        }
076                }
077                return modNumbers;
078        }
079
080        public String getStoichiometry() {
081                run();
082                StringBuilder formula = new StringBuilder();
083                String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
084
085                for (int i = 0; i < seqClusters.size(); i++) {
086                        String c = "?";
087                        if (i < alpha.length()) {
088                                c = alpha.substring(i, i+1);
089                        }
090                        formula.append(c);
091                        int multiplier = seqClusters.get(i).getSequenceCount();
092                        if (multiplier > 1) {
093                                formula.append(multiplier);
094                        }
095                }
096                return formula.toString();
097        }
098
099        /**
100         * Get valid symmetry order for this stoichiometry.
101         * @return
102         */
103        public List<Integer> getFolds() {
104                run();
105                List<Integer> stoichiometry = new ArrayList<Integer>(seqClusters.size());
106                for (int id = 0; id < seqClusters.size(); id++) {
107                        int seqCount = seqClusters.get(id).getSequenceCount();
108                        stoichiometry.add(seqCount);
109                }
110                return getValidFolds(stoichiometry);
111        }
112        /**
113         * Find valid symmetry orders for a given stoichiometry. For instance,
114         * an A6B4 protein would give [1,2] because (A6B4)1 and (A3B2)2 are valid
115         * decompositions.
116         * @param stoichiometry List giving the number of copies in each chain cluster
117         * @return The common factors of the stoichiometry
118         */
119        public static List<Integer> getValidFolds(List<Integer> stoichiometry){
120                List<Integer> denominators = new ArrayList<Integer>();
121
122                int nChains = Collections.max(stoichiometry);
123
124                // Remove duplicate stoichiometries
125                Set<Integer> nominators = new TreeSet<Integer>(stoichiometry);
126
127                // find common denominators
128                for (int d = 1; d <= nChains; d++) {
129                        boolean isDivisable=true;
130                        for (Integer n : nominators) {
131                                if (n % d != 0) {
132                                        isDivisable = false;
133                                        break;
134                                }
135                        }
136                        if(isDivisable) {
137                                denominators.add(d);
138                        }
139                }
140                return denominators;
141        }
142
143        public List<Integer> getSequenceClusterIds() {
144                run();
145                List<Integer> list = new ArrayList<Integer>();
146
147                for (int id = 0; id < seqClusters.size(); id++) {
148                        int seqCount = seqClusters.get(id).getSequenceCount();
149                        for (int i = 0; i < seqCount; i++) {
150                                list.add(id);
151                        }
152                }
153                return list;
154        }
155
156
157        public int getSequenceClusterCount() {
158                run();
159                return seqClusters.size();
160        }
161
162        public List<SequenceAlignmentCluster> getSequenceAlignmentClusters() {
163                return seqClusters;
164        }
165
166        public List<Boolean> getPseudoStoichiometry() {
167                run();
168                List<Boolean> list = new ArrayList<Boolean>();
169
170                for (int id = 0; id < seqClusters.size(); id++) {
171                        int seqCount = seqClusters.get(id).getSequenceCount();
172                        Boolean pseudo = seqClusters.get(id).isPseudoStoichiometric();
173                        for (int i = 0; i < seqCount; i++) {
174                                list.add(pseudo);
175                        }
176                }
177                return list;
178        }
179
180        public List<Double> getMinSequenceIdentity() {
181                run();
182                List<Double> list = new ArrayList<Double>();
183
184                for (int id = 0; id < seqClusters.size(); id++) {
185                        int seqCount = seqClusters.get(id).getSequenceCount();
186                        double minSequenceIdentity = seqClusters.get(id).getMinSequenceIdentity();
187                        for (int i = 0; i < seqCount; i++) {
188                                list.add(minSequenceIdentity);
189                        }
190                }
191                return list;
192        }
193
194        public List<Double> getMaxSequenceIdentity() {
195                run();
196                List<Double> list = new ArrayList<Double>();
197
198                for (int id = 0; id < seqClusters.size(); id++) {
199                        int seqCount = seqClusters.get(id).getSequenceCount();
200                        double maxSequenceIdentity = seqClusters.get(id).getMaxSequenceIdentity();
201                        for (int i = 0; i < seqCount; i++) {
202                                list.add(maxSequenceIdentity);
203                        }
204                }
205                return list;
206        }
207
208        @Override
209        public String toString() {
210                run();
211                StringBuilder builder = new StringBuilder();
212                builder.append("Sequence alignment clusters: " + seqClusters.size());
213                builder.append("\n");
214                for (SequenceAlignmentCluster s: seqClusters) {
215                        builder.append("# seq: ");
216                        builder.append(s.getSequenceCount());
217                        builder.append(" alignment length: ");
218                        builder.append(s.getSequenceAlignmentLength());
219                        builder.append("\n");
220                }
221                return builder.toString();
222        }
223
224        private void run() {
225                if (modified) {
226                        modified = false;
227                        calcAlignedSequences();
228                        createCalphaTraces();
229                }
230        }
231
232
233        private void calcAlignedSequences() {
234                caAligned = new ArrayList<Atom[]>();
235                for (SequenceAlignmentCluster cluster: seqClusters) {
236                        caAligned.addAll(cluster.getAlignedCalphaAtoms());
237                }
238        }
239
240        private void createCalphaTraces() {
241                for (Atom[] atoms: caAligned) {
242                        Point3d[] trace = new Point3d[atoms.length];
243                        for (int j = 0; j < atoms.length; j++) {
244                                trace[j] = new Point3d(atoms[j].getCoords());
245                        }
246                        caCoords.add(trace);
247                }
248        }
249}