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.symmetry.geometry.SuperPosition;
024
025import javax.vecmath.Point3d;
026import java.util.*;
027import java.util.Map.Entry;
028
029public class HelicalRepeatUnit {
030        private Subunits subunits = null;
031        private List<Point3d> repeatUnitCenters = new ArrayList<Point3d>();
032        private List<Point3d[]> repeatUnits = new ArrayList<Point3d[]>();
033        private List<List<Integer>> repeatUnitIndices = new ArrayList<List<Integer>>();
034        private Map<Integer[], Integer> interactingNeighbors = Collections.emptyMap();
035
036public HelicalRepeatUnit(Subunits subunits) {
037        this.subunits = subunits;
038}
039
040public List<Point3d> getRepeatUnitCenters() {
041        if (repeatUnitCenters.isEmpty()) {
042                run();
043        }
044        return repeatUnitCenters;
045}
046
047public List<Point3d[]> getRepeatUnits() {
048        if (repeatUnits.isEmpty()) {
049                run();
050        }
051        return repeatUnits;
052}
053
054public List<List<Integer>> getRepeatUnitIndices() {
055        return repeatUnitIndices;
056}
057
058public Map<Integer[], Integer> getInteractingRepeatUnits() {
059        if (interactingNeighbors.isEmpty()) {
060                run();
061        }
062        return interactingNeighbors;
063}
064
065private void run() {
066        this.repeatUnitCenters = calcRepeatUnitCenters();
067        if (this.repeatUnitCenters.size() == 0) {
068                return;
069        }
070        this.repeatUnits = calcRepeatUnits();
071        this.interactingNeighbors = findInteractingNeigbors();
072}
073
074private List<Point3d> calcRepeatUnitCenters() {
075        Set<Integer> uniqueModels = new HashSet<Integer>(subunits.getModelNumbers());
076        int modelCount = uniqueModels.size();
077        List<Integer> folds = this.subunits.getFolds();
078        int maxFold = folds.get(folds.size()-1);
079
080        List<Point3d> repeatCenters = new ArrayList<Point3d>();
081        List<Point3d> centers = subunits.getCenters();
082        List<Integer> models = subunits.getModelNumbers();
083
084//      if (modelCount == maxFold && subunits.getSubunitCount() > 3) {
085        if (maxFold%modelCount == 0 && modelCount > 1 && subunits.getSubunitCount() > 3) {
086//              System.out.println("calcRepeatUnitCenters case 1");
087                for (int i = 0; i < modelCount; i++) {
088                        List<Integer> subunitIndices = new ArrayList<Integer>();
089                        Point3d p = new Point3d();
090                        int count = 0;
091//                      System.out.println("Models: " + models.size());
092                        for (int j = 0; j < models.size(); j++) {
093                                if (models.get(j) == i) {
094                                        p.add(centers.get(j));
095                                        subunitIndices.add(j);
096                                        count++;
097                                }
098                        }
099//                      System.out.println("count: " + count);
100                        p.scale(1.0/count);
101//                      System.out.println("Orig Repeat unit: " + p);
102                        repeatCenters.add(p);
103                        repeatUnitIndices.add(subunitIndices);
104                }
105        } else {
106//              System.out.println("calcRepeatUnitCenters case21");
107                // TODO need to group subunits into repeating groups
108                // Case of 3B5U: A14, but seems to form (A2)*7 and symmetry related subunits don't have direct contact
109                List<Integer> sequenceClusterIds = subunits.getSequenceClusterIds();
110                for (int i = 0; i < subunits.getSubunitCount(); i++) {
111                        List<Integer> subunitIndices = new ArrayList<Integer>(1);
112                        if (sequenceClusterIds.get(i) == 0) {
113                                repeatCenters.add(new Point3d(centers.get(i)));
114//                              System.out.println("Orig Repeat unit: " + centers.get(i));
115                                subunitIndices.add(i);
116                                repeatUnitIndices.add(subunitIndices);
117                        }
118                }
119        }
120
121        // helixes should have at least 3 repeat centers
122//      System.out.println("Number of repeat centers: " + repeatCenters.size());
123        if (repeatCenters.size() < 3) {
124                repeatCenters.clear();
125        }
126
127        return repeatCenters;
128}
129
130private List<Point3d[]> calcRepeatUnits() {
131        Set<Integer> uniqueModels = new HashSet<Integer>(subunits.getModelNumbers());
132        int modelCount = uniqueModels.size();
133        List<Integer> folds = this.subunits.getFolds();
134        int maxFold = folds.get(folds.size()-1);
135
136        List<Point3d[]> repeatTraces = new ArrayList<Point3d[]>();
137        List<Integer> models = subunits.getModelNumbers();
138        List<Point3d[]> traces = subunits.getTraces();
139
140//      if (modelCount == maxFold && subunitCount > 3) {
141        if (maxFold%modelCount == 0 && modelCount > 1 && subunits.getSubunitCount() > 3) {
142                for (int i = 0; i < modelCount; i++) {
143                        List<Point3d> coords = new ArrayList<Point3d>();
144                        for (int j = 0; j < models.size(); j++) {
145                                if (models.get(j) == i) {
146                                        coords.addAll(Arrays.asList(traces.get(j)));
147                                }
148                        }
149                        Point3d[] x = new Point3d[coords.size()];
150                        coords.toArray(x);
151//                      repeatTraces.add(x); // make sure we don't accidently change the original coordinates
152                        repeatTraces.add(SuperPosition.clonePoint3dArray(x));
153                }
154        } else {
155                List<Integer> sequenceClusterIds = subunits.getSequenceClusterIds();
156                for (int i = 0; i < subunits.getSubunitCount(); i++) {
157                        if (sequenceClusterIds.get(i) == 0) {
158                                Point3d[] x = subunits.getTraces().get(i);
159                                repeatTraces.add(SuperPosition.clonePoint3dArray(x));
160                        }
161                }
162        }
163
164//      for (int i = 0; i < repeatTraces.size(); i++) {
165//              System.out.println("Repeat " + i);
166//              System.out.println(Arrays.toString(repeatTraces.get(i)));
167//      }
168        return repeatTraces;
169}
170
171private Map<Integer[], Integer> findInteractingNeigbors() {
172        Map<Integer[], Integer>  contactMap = new HashMap<Integer[], Integer>();
173
174        Map<Integer, List<Integer[]>> distanceMap = findClosestPairs(8);
175        for (List<Integer[]> pairs: distanceMap.values())
176        for (Integer[] pair: pairs) {
177                Integer contacts = calcContactNumber(repeatUnits.get(pair[0]), repeatUnits.get(pair[1]));
178//              System.out.println("contacts: " + pair[0] + "-" + pair[1] + ": " + contacts);
179                if (contacts > 0) {
180                        contactMap.put(pair, contacts);
181                }
182        }
183
184        return contactMap;
185}
186
187private Map<Integer, List<Integer[]>> findClosestPairs(int maxNeighbors) {
188        Map<Integer, List<Integer[]>>  reducedMap = new TreeMap<Integer, List<Integer[]>>();
189
190        Map<Integer, List<Integer[]>>  distanceMap = new TreeMap<Integer, List<Integer[]>>();
191        int nCenters = repeatUnitCenters.size();
192//      System.out.println("repeatUnitCenters: " + repeatUnitCenters);
193
194        for (int i = 0; i < nCenters-1; i++) {
195                for (int j = i+1; j < nCenters; j++) {
196                        float dist = (float)repeatUnitCenters.get(i).distance(repeatUnitCenters.get(j));
197                        // round to 2 digits precision
198//                      System.out.println("dist pair: " + i + "-" + j + ": " + dist);
199                        dist *= 100;
200                        int intDist = Math.round(dist);
201                        List<Integer[]> pairs = distanceMap.get(intDist);
202                        // save only one representative pair for each distance
203                        if (pairs == null) {
204                                pairs = new ArrayList<Integer[]>();
205                        }
206                        Integer[] pair = new Integer[2];
207                        pair[0] = i;
208                        pair[1] = j;
209                        pairs.add(pair);
210                        distanceMap.put(intDist, pairs);
211                }
212        }
213
214        int count = 0;
215        for (Entry<Integer, List<Integer[]>> entry: distanceMap.entrySet()) {
216                if (! (reducedMap.containsKey(entry.getKey())) ) {
217                        reducedMap.put(entry.getKey(), entry.getValue());
218//                      System.out.println("dist pair: " + entry.getValue() + ": " + entry.getKey());
219                        count++;
220                        if (count == maxNeighbors) {
221                                break;
222                        }
223                }
224        }
225        distanceMap.clear();
226
227        return reducedMap;
228}
229
230private static int calcContactNumber(Point3d[] a, Point3d[] b) {
231        int contacts = 0;
232        for (Point3d pa : a) {
233                for (Point3d pb : b) {
234                        if (pa.distance(pb) < 10) {
235                                contacts++;
236                        }
237                }
238        }
239        return contacts;
240}
241}