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.io.mmtf; 022 023import java.text.DateFormat; 024import java.text.SimpleDateFormat; 025import java.util.ArrayList; 026import java.util.Date; 027import java.util.HashSet; 028import java.util.LinkedHashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.Set; 033 034import javax.vecmath.Matrix4d; 035 036import org.biojava.nbio.structure.AminoAcid; 037import org.biojava.nbio.structure.AminoAcidImpl; 038import org.biojava.nbio.structure.Atom; 039import org.biojava.nbio.structure.Bond; 040import org.biojava.nbio.structure.Chain; 041import org.biojava.nbio.structure.ExperimentalTechnique; 042import org.biojava.nbio.structure.Group; 043import org.biojava.nbio.structure.GroupType; 044import org.biojava.nbio.structure.NucleotideImpl; 045import org.biojava.nbio.structure.PDBCrystallographicInfo; 046import org.biojava.nbio.structure.Structure; 047import org.biojava.nbio.structure.StructureException; 048import org.biojava.nbio.structure.chem.ChemComp; 049import org.biojava.nbio.structure.chem.ChemCompGroupFactory; 050import org.biojava.nbio.structure.chem.ChemCompTools; 051import org.biojava.nbio.structure.quaternary.BioAssemblyInfo; 052import org.biojava.nbio.structure.quaternary.BiologicalAssemblyTransformation; 053import org.biojava.nbio.structure.secstruc.SecStrucCalc; 054import org.biojava.nbio.structure.secstruc.SecStrucState; 055import org.biojava.nbio.structure.secstruc.SecStrucType; 056import org.biojava.nbio.structure.xtal.CrystalCell; 057import org.biojava.nbio.structure.xtal.SpaceGroup; 058import org.rcsb.mmtf.dataholders.DsspType; 059import org.rcsb.mmtf.utils.CodecUtils; 060import org.slf4j.Logger; 061import org.slf4j.LoggerFactory; 062 063/** 064 * A utils class of functions needed for Biojava to read and write to mmtf. 065 * @author Anthony Bradley 066 * 067 */ 068public class MmtfUtils { 069 070 private static final Logger LOGGER = LoggerFactory.getLogger(MmtfUtils.class); 071 072 /** 073 * This sets all microheterogeneous groups 074 * (previously alternate location groups) as separate groups. 075 * This is required because mmtf groups cannot have multiple HET codes. 076 * @param bioJavaStruct 077 */ 078 public static void fixMicroheterogenity(Structure bioJavaStruct) { 079 // Loop through the models 080 for (int i=0; i<bioJavaStruct.nrModels(); i++){ 081 // Then the chains 082 List<Chain> chains = bioJavaStruct.getModel(i); 083 for (Chain c : chains) { 084 // Build a new list of groups 085 List<Group> outGroups = new ArrayList<>(); 086 for (Group g : c.getAtomGroups()) { 087 List<Group> removeList = new ArrayList<>(); 088 for (Group altLoc : g.getAltLocs()) { 089 // Check if they are not equal -> microheterogenity 090 if(! altLoc.getPDBName().equals(g.getPDBName())) { 091 // Now add this group to the main list 092 removeList.add(altLoc); 093 } 094 } 095 // Add this group 096 outGroups.add(g); 097 // Remove any microhet alt locs 098 g.getAltLocs().removeAll(removeList); 099 // Add these microhet alt locs 100 outGroups.addAll(removeList); 101 } 102 c.setAtomGroups(outGroups); 103 } 104 } 105 } 106 107 108 /** 109 * Generate the secondary structure for a Biojava structure object. 110 * @param bioJavaStruct the Biojava structure for which it is to be calculate. 111 */ 112 public static void calculateDsspSecondaryStructure(Structure bioJavaStruct) { 113 SecStrucCalc ssp = new SecStrucCalc(); 114 115 try{ 116 ssp.calculate(bioJavaStruct, true); 117 } 118 catch(StructureException e) { 119 LOGGER.warn("Could not calculate secondary structure (error {}). Secondary structure annotation will be missing.", e.getMessage()); 120 } 121 } 122 123 /** 124 * Get the string representation of a space group. 125 * @param spaceGroup the input SpaceGroup object 126 * @return the space group as a string. 127 */ 128 public static String getSpaceGroupAsString(SpaceGroup spaceGroup) { 129 if(spaceGroup==null){ 130 return "NA"; 131 } 132 else{ 133 return spaceGroup.getShortSymbol(); 134 } 135 } 136 137 /** 138 * Get the length six array of the unit cell information. 139 * @param xtalInfo the input PDBCrystallographicInfo object 140 * @return the length six float array 141 */ 142 public static float[] getUnitCellAsArray(PDBCrystallographicInfo xtalInfo) { 143 CrystalCell xtalCell = xtalInfo.getCrystalCell(); 144 if(xtalCell==null){ 145 return null; 146 }else{ 147 float[] inputUnitCell = new float[6]; 148 inputUnitCell[0] = (float) xtalCell.getA(); 149 inputUnitCell[1] = (float) xtalCell.getB(); 150 inputUnitCell[2] = (float) xtalCell.getC(); 151 inputUnitCell[3] = (float) xtalCell.getAlpha(); 152 inputUnitCell[4] = (float) xtalCell.getBeta(); 153 inputUnitCell[5] = (float) xtalCell.getGamma(); 154 return inputUnitCell; 155 } 156 } 157 158 /** 159 * Converts the set of experimental techniques to an array of strings. 160 * @param experimentalTechniques the input set of experimental techniques 161 * @return the array of strings describing the methods used. 162 */ 163 public static String[] techniquesToStringArray(Set<ExperimentalTechnique> experimentalTechniques) { 164 if(experimentalTechniques==null){ 165 return new String[0]; 166 } 167 String[] outArray = new String[experimentalTechniques.size()]; 168 int index = 0; 169 for (ExperimentalTechnique experimentalTechnique : experimentalTechniques) { 170 outArray[index] = experimentalTechnique.getName(); 171 index++; 172 } 173 return outArray; 174 } 175 176 /** 177 * Covert a Date object to ISO time format. 178 * @param inputDate The input date object 179 * @return the time in ISO time format 180 */ 181 public static String dateToIsoString(Date inputDate) { 182 DateFormat dateStringFormat = new SimpleDateFormat("yyyy-MM-dd"); 183 return dateStringFormat.format(inputDate); 184 } 185 186 /** 187 * Convert a bioassembly information into a map of transform, chainindices it relates to. 188 * @param bioassemblyInfo the bioassembly info object for this structure 189 * @param chainIdToIndexMap the map of chain ids to the index that chain corresponds to. 190 * @return the bioassembly information (as primitive types). 191 */ 192 public static Map<double[], int[]> getTransformMap(BioAssemblyInfo bioassemblyInfo, Map<String, Integer> chainIdToIndexMap) { 193 Map<Matrix4d, List<Integer>> matMap = new LinkedHashMap<>(); 194 List<BiologicalAssemblyTransformation> transforms = bioassemblyInfo.getTransforms(); 195 for (BiologicalAssemblyTransformation transformation : transforms) { 196 Matrix4d transMatrix = transformation.getTransformationMatrix(); 197 String transChainId = transformation.getChainId(); 198 if (!chainIdToIndexMap.containsKey(transChainId)){ 199 continue; 200 } 201 int chainIndex = chainIdToIndexMap.get(transformation.getChainId()); 202 if(matMap.containsKey(transMatrix)){ 203 matMap.get(transMatrix).add(chainIndex); 204 } 205 else{ 206 List<Integer> chainIdList = new ArrayList<>(); 207 chainIdList.add(chainIndex); 208 matMap.put(transMatrix, chainIdList); 209 } 210 } 211 212 Map<double[], int[]> outMap = new LinkedHashMap<>(); 213 for (Entry<Matrix4d, List<Integer>> entry : matMap.entrySet()) { 214 outMap.put(convertToDoubleArray(entry.getKey()), CodecUtils.convertToIntArray(entry.getValue())); 215 } 216 return outMap; 217 } 218 219 /** 220 * Convert a four-d matrix to a double array. Row-packed. 221 * @param transformationMatrix the input matrix4d object 222 * @return the double array (16 long). 223 */ 224 public static double[] convertToDoubleArray(Matrix4d transformationMatrix) { 225 // Initialise the output array 226 double[] outArray = new double[16]; 227 // Iterate over the matrix 228 for(int i=0; i<4; i++){ 229 for(int j=0; j<4; j++){ 230 // Now set this element 231 outArray[i*4+j] = transformationMatrix.getElement(i,j); 232 } 233 } 234 return outArray; 235 } 236 237 /** 238 * Count the total number of groups in the structure 239 * @param structure the input structure 240 * @return the total number of groups 241 */ 242 public static int getNumGroups(Structure structure) { 243 int count = 0; 244 for(int i=0; i<structure.nrModels(); i++) { 245 for(Chain chain : structure.getChains(i)){ 246 count+= chain.getAtomGroups().size(); 247 } 248 } 249 return count; 250 } 251 252 253 /** 254 * Get a list of atoms for a group. Only add each atom once. 255 * @param inputGroup the Biojava Group to consider 256 * @return the atoms for the input Biojava Group 257 */ 258 public static List<Atom> getAtomsForGroup(Group inputGroup) { 259 Set<Atom> uniqueAtoms = new HashSet<Atom>(); 260 List<Atom> theseAtoms = new ArrayList<Atom>(); 261 for(Atom a: inputGroup.getAtoms()){ 262 theseAtoms.add(a); 263 uniqueAtoms.add(a); 264 } 265 List<Group> altLocs = inputGroup.getAltLocs(); 266 for(Group thisG: altLocs){ 267 for(Atom a: thisG.getAtoms()){ 268 if(uniqueAtoms.contains(a)){ 269 continue; 270 } 271 theseAtoms.add(a); 272 } 273 } 274 return theseAtoms; 275 } 276 277 /** 278 * Find the number of bonds in a group 279 * @param atomsInGroup the list of atoms in the group 280 * @return the number of atoms in the group 281 */ 282 public static int getNumBondsInGroup(List<Atom> atomsInGroup) { 283 int bondCounter = 0; 284 for(Atom atom : atomsInGroup) { 285 if(atom.getBonds()==null){ 286 continue; 287 } 288 for(Bond bond : atom.getBonds()) { 289 // Now set the bonding information. 290 Atom other = bond.getOther(atom); 291 // If both atoms are in the group 292 if (atomsInGroup.indexOf(other)!=-1){ 293 Integer firstBondIndex = atomsInGroup.indexOf(atom); 294 Integer secondBondIndex = atomsInGroup.indexOf(other); 295 // Don't add the same bond twice 296 if (firstBondIndex<secondBondIndex){ 297 bondCounter++; 298 } 299 } 300 } 301 } 302 return bondCounter; 303 } 304 305 /** 306 * Get the secondary structure as defined by DSSP. 307 * @param group the input group to be calculated 308 * @return the integer index of the group type. 309 */ 310 public static int getSecStructType(Group group) { 311 SecStrucState props = (SecStrucState) group.getProperty("secstruc"); 312 if(props==null){ 313 return DsspType.NULL_ENTRY.getDsspIndex(); 314 } 315 return DsspType.dsspTypeFromString(props.getType().name).getDsspIndex(); 316 } 317 318 /** 319 * Get the secondary structure as defined by DSSP. 320 * @param group the input group to be calculated 321 * @param dsspIndex integer index of the group type. 322 */ 323 public static void setSecStructType(Group group, int dsspIndex) { 324 SecStrucType secStrucType = getSecStructTypeFromDsspIndex(dsspIndex); 325 SecStrucState secStrucState = new SecStrucState(group, "MMTF_ASSIGNED", secStrucType); 326 if(secStrucType!=null){ 327 group.setProperty("secstruc", secStrucState); 328 } 329 } 330 331 332 /** 333 * Set the DSSP type based on a numerical index. 334 * @param dsspIndex the integer index of the type to set 335 * @return the instance of the SecStrucType object holding this secondary 336 * structure type. 337 */ 338 public static SecStrucType getSecStructTypeFromDsspIndex(int dsspIndex) { 339 String dsspType = DsspType.dsspTypeFromInt(dsspIndex).getDsspType(); 340 for(SecStrucType secStrucType : SecStrucType.values()) 341 { 342 if(dsspType.equals(secStrucType.name)) 343 { 344 return secStrucType; 345 } 346 } 347 // Return a null entry. 348 return null; 349 } 350 351 /** 352 * Get summary information for the structure. 353 * @param structure the structure for which to get the information. 354 */ 355 public static MmtfSummaryDataBean getStructureInfo(Structure structure) { 356 MmtfSummaryDataBean mmtfSummaryDataBean = new MmtfSummaryDataBean(); 357 // Get all the atoms 358 List<Atom> theseAtoms = new ArrayList<>(); 359 List<Chain> allChains = new ArrayList<>(); 360 Map<String, Integer> chainIdToIndexMap = new LinkedHashMap<>(); 361 int chainCounter = 0; 362 int bondCount = 0; 363 mmtfSummaryDataBean.setAllAtoms(theseAtoms); 364 mmtfSummaryDataBean.setAllChains(allChains); 365 mmtfSummaryDataBean.setChainIdToIndexMap(chainIdToIndexMap); 366 for (int i=0; i<structure.nrModels(); i++){ 367 List<Chain> chains = structure.getModel(i); 368 allChains.addAll(chains); 369 for (Chain chain : chains) { 370 String idOne = chain.getId(); 371 if (!chainIdToIndexMap.containsKey(idOne)) { 372 chainIdToIndexMap.put(idOne, chainCounter); 373 } 374 chainCounter++; 375 for (Group g : chain.getAtomGroups()) { 376 for(Atom atom: getAtomsForGroup(g)){ 377 theseAtoms.add(atom); 378 // If both atoms are in the group 379 if (atom.getBonds()!=null){ 380 bondCount+=atom.getBonds().size(); 381 } 382 } 383 } 384 } 385 } 386 // Assumes all bonds are referenced twice 387 mmtfSummaryDataBean.setNumBonds(bondCount/2); 388 return mmtfSummaryDataBean; 389 390 } 391 392 /** 393 * Get a list of N 4*4 matrices from a single list of doubles of length 16*N. 394 * @param ncsOperMatrixList the input list of doubles 395 * @return the list of 4*4 matrics 396 */ 397 public static Matrix4d[] getNcsAsMatrix4d(double[][] ncsOperMatrixList) { 398 if(ncsOperMatrixList==null){ 399 return null; 400 } 401 int numMats = ncsOperMatrixList.length; 402 if(numMats==0){ 403 return null; 404 } 405 if(numMats==1 && ncsOperMatrixList[0].length==0){ 406 return null; 407 } 408 Matrix4d[] outList = new Matrix4d[numMats]; 409 for(int i=0; i<numMats; i++){ 410 outList[i] = new Matrix4d(ncsOperMatrixList[i]); 411 } 412 return outList; 413 } 414 415 /** 416 * Get a list of length N*16 of a list of Matrix4d*N. 417 * @param ncsOperators the {@link Matrix4d} list 418 * @return the list of length N*16 of the list of matrices 419 */ 420 public static double[][] getNcsAsArray(Matrix4d[] ncsOperators) { 421 if(ncsOperators==null){ 422 return new double[0][0]; 423 } 424 double[][] outList = new double[ncsOperators.length][16]; 425 for(int i=0; i<ncsOperators.length;i++){ 426 outList[i] = convertToDoubleArray(ncsOperators[i]); 427 } 428 return outList; 429 } 430 431 /** 432 * Insert the group in the given position in the sequence. 433 * @param chain the chain to add the seq res group to 434 * @param group the group to add 435 * @param sequenceIndexId the index to add it in 436 */ 437 public static void insertSeqResGroup(Chain chain, Group group, int sequenceIndexId) { 438 List<Group> seqResGroups = chain.getSeqResGroups(); 439 addGroupAtId(seqResGroups, group, sequenceIndexId); 440 } 441 442 /** 443 * Add the missing groups to the SeqResGroups. 444 * @param modelChain the chain to add the information for 445 * @param sequence the sequence of the construct 446 */ 447 public static void addSeqRes(Chain modelChain, String sequence) { 448 449 List<Group> seqResGroups = modelChain.getSeqResGroups(); 450 GroupType chainType = getChainType(modelChain.getAtomGroups()); 451 452 for(int i=0; i<sequence.length(); i++){ 453 454 char singleLetterCode = sequence.charAt(i); 455 Group group = null; 456 if (seqResGroups.size() > i) { 457 group=seqResGroups.get(i); 458 } 459 if(group!=null){ 460 continue; 461 } 462 463 group = getSeqResGroup(singleLetterCode, chainType); 464 addGroupAtId(seqResGroups, group, i); 465 } 466 } 467 468 private static GroupType getChainType(List<Group> groups) { 469 for(Group group : groups) { 470 if(group!=null && group.getType()!=GroupType.HETATM){ 471 return group.getType(); 472 } 473 } 474 return GroupType.HETATM; 475 } 476 477 private static <T> void addGroupAtId(List<T> seqResGroups, T group, int sequenceIndexId) { 478 while(seqResGroups.size()<=sequenceIndexId){ 479 seqResGroups.add(null); 480 } 481 if(sequenceIndexId>=0){ 482 seqResGroups.set(sequenceIndexId, group); 483 } 484 } 485 486 private static Group getSeqResGroup(char singleLetterCode, GroupType type) { 487 488 if(type==GroupType.AMINOACID){ 489 String threeLetter = ChemCompTools.getAminoThreeLetter(singleLetterCode); 490 if (threeLetter == null) return null; 491 ChemComp chemComp = ChemCompGroupFactory.getChemComp(threeLetter); 492 493 AminoAcidImpl a = new AminoAcidImpl(); 494 a.setRecordType(AminoAcid.SEQRESRECORD); 495 a.setAminoType(singleLetterCode); 496 a.setPDBName(threeLetter); 497 a.setChemComp(chemComp); 498 return a; 499 500 } else if (type==GroupType.NUCLEOTIDE) { 501 String twoLetter = ChemCompTools.getDNATwoLetter(singleLetterCode); 502 if (twoLetter == null) return null; 503 ChemComp chemComp = ChemCompGroupFactory.getChemComp(twoLetter); 504 505 NucleotideImpl n = new NucleotideImpl(); 506 n.setPDBName(twoLetter); 507 n.setChemComp(chemComp); 508 return n; 509 } 510 else{ 511 return null; 512 } 513 } 514}