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 */ 021 022package org.biojava.nbio.structure.symmetry.core; 023 024import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia; 025import org.biojava.nbio.structure.symmetry.geometry.SuperPosition; 026 027import javax.vecmath.Point3d; 028import javax.vecmath.Vector3d; 029import java.util.*; 030import java.util.Map.Entry; 031 032 033/** 034 * A bean to represent info about the set of subunits being considered for a 035 * QuatSymmetryDetector alignment. 036 * @author Peter Rose 037 */ 038public class Subunits { 039 private List<Point3d[]> caCoords = Collections.emptyList(); 040 private List<Integer> sequenceClusterIds = Collections.emptyList(); 041 private List<Boolean> pseudoStoichiometry = Collections.emptyList(); 042 private List<Double> minSequenceIdentity = Collections.emptyList(); 043 private List<Double> maxSequenceIdentity = Collections.emptyList(); 044 private List<String> chainIds = Collections.emptyList(); 045 private List<Integer> modelNumbers = Collections.emptyList(); 046 private List<Integer> folds = Collections.emptyList(); 047 private List<Point3d> originalCenters = new ArrayList<Point3d>(); 048 private List<Point3d> centers = new ArrayList<Point3d>(); 049 private List<Vector3d> unitVectors = new ArrayList<Vector3d>(); 050 private int nucleicAcidChainCount = 0; 051 private boolean pseudoSymmetric = false; 052 053 private Point3d centroid; 054 private MomentsOfInertia momentsOfInertia = new MomentsOfInertia(); 055 056 /** 057 * All inputs should contain one element per subunit. 058 * @param caCoords CA coordinates of all subunits 059 * @param sequenceClusterIds ID of the cluster that each subunit belongs to 060 * @param pseudoStoichiometry Whether pseudosymmetry was used when clustering the subunit 061 * @param minSequenceIdentity Minimum sequence identity to other cluster members 062 * @param maxSequenceIdentity Maximum sequence identity to other cluster members 063 * @param folds Valid symmetry orders for this stoichiometry 064 * @param chainIds Chain ID for the subunit 065 * @param modelNumbers Model number for the subunit 066 */ 067 public Subunits(List<Point3d[]> caCoords, List<Integer> sequenceClusterIds, List<Boolean> pseudoStoichiometry, List<Double> minSequenceIdentity, List<Double> maxSequenceIdentity, List<Integer> folds, List<String> chainIds, List<Integer> modelNumbers) { 068 069 for (int i=0; i<caCoords.size(); i++) { 070 if (caCoords.get(i).length==0) 071 throw new IllegalArgumentException("0-length coordinate array in subunit coordinates with index " +i+". Can't calculate quaternary symmetry for empty coordinates."); 072 } 073 074 this.caCoords = caCoords; 075 this.sequenceClusterIds = sequenceClusterIds; 076 this.pseudoStoichiometry = pseudoStoichiometry; 077 this.minSequenceIdentity = minSequenceIdentity; 078 this.maxSequenceIdentity = maxSequenceIdentity; 079 this.folds = folds; 080 this.chainIds = chainIds; 081 this.modelNumbers = modelNumbers; 082 } 083 084 public List<Point3d[]> getTraces() { 085 return caCoords; 086 } 087 088 public int getSubunitCount() { 089 run(); 090 if (centers == null) { 091 return 0; 092 } 093 return centers.size(); 094 } 095 096 public List<Integer> getSequenceClusterIds() { 097 return sequenceClusterIds; 098 } 099 100 public boolean isPseudoStoichiometric() { 101 for (Boolean b: pseudoStoichiometry) { 102 if (b) { 103 return true; 104 } 105 } 106 return false; 107 } 108 109 public boolean isPseudoSymmetric() { 110 return pseudoSymmetric; 111 } 112 113 public void setPseudoSymmetric(boolean pseudoSymmetric) { 114 this.pseudoSymmetric = pseudoSymmetric; 115 } 116 117 public double getMinSequenceIdentity() { 118 double minId = 1.0; 119 for (double seqId: minSequenceIdentity) { 120 minId = Math.min(seqId, minId); 121 } 122 return minId; 123 } 124 125 public double getMaxSequenceIdentity() { 126 double maxId = 1.0; 127 for (double seqId: maxSequenceIdentity) { 128 maxId = Math.min(seqId, maxId); 129 } 130 return maxId; 131 } 132 133 public List<String> getChainIds() { 134 return chainIds; 135 } 136 137 public List<Integer> getModelNumbers() { 138 return modelNumbers; 139 } 140 141 public List<Integer>getFolds() { 142 return folds; 143 } 144 145 public String getStoichiometry() { 146 // count number of members in each cluster 147 Map<Integer, Integer> map = new TreeMap<Integer, Integer>(); 148 for (Integer id: sequenceClusterIds) { 149 Integer value = map.get(id); 150 if (value == null) { 151 value = new Integer(1); 152 } else { 153 value++; 154 } 155 map.put(id, value); 156 } 157 158 // build formula string 159 String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 160 StringBuilder formula = new StringBuilder(); 161 for (Entry<Integer, Integer> entry: map.entrySet()) { 162 String key = "?"; 163 int id = entry.getKey(); 164 if (id < alpha.length()) { 165 key = alpha.substring(id, id+1); 166 } 167 formula.append(key); 168 if (entry.getValue() > 1) { 169 formula.append(entry.getValue()); 170 } 171 } 172 173 return formula.toString(); 174 } 175 176 public int getCalphaCount() { 177 int count = 0; 178 for (Point3d[] trace: caCoords) { 179 count += trace.length; 180 } 181 return count; 182 } 183 184 public int getLargestSubunit() { 185 int index = -1; 186 int maxLength = 0; 187 for (int i = 0; i < caCoords.size(); i++) { 188 int length = caCoords.get(i).length; 189 if (length > maxLength) { 190 index = i; 191 } 192 } 193 return index; 194 } 195 196 public List<Point3d> getCenters() { 197 run(); 198 return centers; 199 } 200 201 public List<Vector3d> getUnitVectors() { 202 run(); 203 return unitVectors; 204 } 205 206 public List<Point3d> getOriginalCenters() { 207 run(); 208 return originalCenters; 209 } 210 211 public Point3d getCentroid() { 212 run(); 213 return centroid; 214 } 215 216 public MomentsOfInertia getMomentsOfInertia() { 217 run(); 218 return momentsOfInertia; 219 } 220 221 /** 222 * @return the nucleicAcidChainCount 223 */ 224 public int getNucleicAcidChainCount() { 225 run(); 226 return nucleicAcidChainCount; 227 } 228 229 /** 230 * @param nucleicAcidChainCount the nucleicAcidChainCount to set 231 */ 232 public void setNucleicAcidChainCount(int nucleicAcidChainCount) { 233 this.nucleicAcidChainCount = nucleicAcidChainCount; 234 } 235 236 public boolean overlaps(Subunits subunits) { 237 Set<String> set1 = getSignatures(this); 238 Set<String> set2 = getSignatures(subunits); 239 set1.retainAll(set2); 240 return set1.size() > 0; 241 } 242 243 public boolean contains(Subunits subunits) { 244 Set<String> set1 = getSignatures(this); 245 Set<String> set2 = getSignatures(subunits); 246 return set1.containsAll(set2); 247 } 248 249 private static Set<String> getSignatures(Subunits subunits) { 250 Set<String> set = new HashSet<String>(subunits.getSubunitCount()); 251 for (int i = 0; i < subunits.getSubunitCount(); i++) { 252 set.add(subunits.getChainIds().get(i) + "_" + subunits.getModelNumbers().get(i)); 253 } 254 return set; 255 } 256 257 private void run() { 258 if (centers.size() > 0) { 259 return; 260 } 261 calcOriginalCenters(); 262 calcCentroid(); 263 calcCenters(); 264 calcMomentsOfIntertia(); 265 } 266 267 private void calcOriginalCenters() { 268 for (Point3d[] trace: caCoords) { 269 Point3d com = SuperPosition.centroid(trace); 270 originalCenters.add(com); 271 } 272 } 273 274 private void calcCentroid() { 275 Point3d[] orig = originalCenters.toArray(new Point3d[originalCenters.size()]); 276 centroid = SuperPosition.centroid(orig); 277 } 278 279 private void calcCenters() { 280 for (Point3d p: originalCenters) { 281 Point3d c = new Point3d(p); 282 c.sub(centroid); 283 centers.add(c); 284 Vector3d v = new Vector3d(c); 285 v.normalize(); 286 unitVectors.add(v); 287 } 288 } 289 290 public Point3d getLowerBound() { 291 Point3d lower = new Point3d(); 292 for (Point3d p: centers) { 293 if (p.x < lower.x) { 294 lower.x = p.x; 295 } 296 if (p.y < lower.y) { 297 lower.y = p.y; 298 } 299 if (p.z < lower.z) { 300 lower.z = p.z; 301 } 302 } 303 return lower; 304 } 305 306 public Point3d getUpperBound() { 307 Point3d upper = new Point3d(); 308 for (Point3d p: centers) { 309 if (p.x > upper.x) { 310 upper.x = p.x; 311 } 312 if (p.y > upper.y) { 313 upper.y = p.y; 314 } 315 if (p.z > upper.z) { 316 upper.z = p.z; 317 } 318 } 319 return upper; 320 } 321 322 private void calcMomentsOfIntertia() { 323 for (Point3d[] trace: caCoords) { 324 for (Point3d p: trace) { 325 momentsOfInertia.addPoint(p, 1.0f); 326 } 327 } 328 } 329}