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.util.List; 024import java.util.Map; 025import java.util.Map.Entry; 026 027import org.biojava.nbio.structure.Atom; 028import org.biojava.nbio.structure.Bond; 029import org.biojava.nbio.structure.Chain; 030import org.biojava.nbio.structure.ChainImpl; 031import org.biojava.nbio.structure.EntityInfo; 032import org.biojava.nbio.structure.Group; 033import org.biojava.nbio.structure.PDBCrystallographicInfo; 034import org.biojava.nbio.structure.PDBHeader; 035import org.biojava.nbio.structure.Structure; 036import org.biojava.nbio.structure.chem.ChemComp; 037import org.biojava.nbio.structure.quaternary.BioAssemblyInfo; 038import org.rcsb.mmtf.api.StructureAdapterInterface; 039import org.rcsb.mmtf.dataholders.MmtfStructure; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * Class to take Biojava structure data and covert to the DataApi for encoding. 045 * Must implement all the functions in {@link StructureAdapterInterface}. 046 * 047 * @author Anthony Bradley 048 * @since 5.0 049 * 050 */ 051public class MmtfStructureWriter { 052 053 private static final Logger logger = LoggerFactory.getLogger(MmtfStructureWriter.class); 054 055 private final StructureAdapterInterface mmtfDecoderInterface; 056 057 /** 058 * Pass data from Biojava structure to another generic output type. Loops through the data 059 * structure and calls all the set functions. 060 * @param structure the input {@link Structure} to write 061 * @param dataTransferInterface the generic interface that 062 * implements all the set methods. 063 */ 064 public MmtfStructureWriter(Structure structure, StructureAdapterInterface dataTransferInterface) { 065 this.mmtfDecoderInterface = dataTransferInterface; 066 // Reset structure to consider altloc groups with the same residue number but different group names as seperate groups 067 MmtfUtils.fixMicroheterogenity(structure); 068 // Get the chain name to index map 069 MmtfSummaryDataBean mmtfSummaryDataBean = MmtfUtils.getStructureInfo(structure); 070 Map<String, Integer> chainIdToIndexMap = mmtfSummaryDataBean.getChainIdToIndexMap(); 071 List<Atom> allAtoms = mmtfSummaryDataBean.getAllAtoms(); 072 int numBonds = mmtfSummaryDataBean.getNumBonds(); 073 List<Chain> allChains = mmtfSummaryDataBean.getAllChains(); 074 mmtfDecoderInterface.initStructure(numBonds, allAtoms.size(), MmtfUtils.getNumGroups(structure), allChains.size(), structure.nrModels(), structure.getPDBCode()); 075 // Generate the secondary structure 076 MmtfUtils.calculateDsspSecondaryStructure(structure); 077 // Get the header and the xtal info. 078 PDBHeader pdbHeader = structure.getPDBHeader(); 079 PDBCrystallographicInfo xtalInfo = pdbHeader.getCrystallographicInfo(); 080 mmtfDecoderInterface.setHeaderInfo(pdbHeader.getRfree(), pdbHeader.getRwork(), pdbHeader.getResolution(), pdbHeader.getTitle(), MmtfUtils.dateToIsoString(pdbHeader.getDepDate()), 081 MmtfUtils.dateToIsoString(pdbHeader.getRelDate()), MmtfUtils.techniquesToStringArray(pdbHeader.getExperimentalTechniques())); 082 mmtfDecoderInterface.setXtalInfo(MmtfUtils.getSpaceGroupAsString(xtalInfo.getSpaceGroup()), MmtfUtils.getUnitCellAsArray(xtalInfo), MmtfUtils.getNcsAsArray(xtalInfo.getNcsOperators())); 083 // Store the bioassembly data 084 storeBioassemblyInformation(chainIdToIndexMap, pdbHeader.getBioAssemblies()); 085 // Store the entity data 086 storeEntityInformation(allChains, structure.getEntityInfos()); 087 // Now loop through the data structure 088 for (int modelIndex=0; modelIndex<structure.nrModels(); modelIndex++) { 089 List<Chain> modelChains = structure.getChains(modelIndex); 090 // Set this model 091 mmtfDecoderInterface.setModelInfo(modelIndex, modelChains.size()); 092 for(int chainInModelIndex=0; chainInModelIndex<modelChains.size(); chainInModelIndex++) { 093 Chain chain = modelChains.get(chainInModelIndex); 094 List<Group> groups = chain.getAtomGroups(); 095 List<Group> sequenceGroups = chain.getSeqResGroups(); 096 mmtfDecoderInterface.setChainInfo(chain.getId(), chain.getName(), groups.size()); 097 for(int groupInChainIndex=0; groupInChainIndex<groups.size(); groupInChainIndex++){ 098 Group group = groups.get(groupInChainIndex); 099 List<Atom> atomsInGroup = MmtfUtils.getAtomsForGroup(group); 100 ChemComp chemComp = group.getChemComp(); 101 Character insCode = group.getResidueNumber().getInsCode(); 102 if(insCode==null || insCode.equals(' ')){ 103 insCode=MmtfStructure.UNAVAILABLE_CHAR_VALUE; 104 } 105 char singleLetterCode = 'X'; 106 if (chemComp.getOneLetterCode().length()==1){ 107 singleLetterCode = chemComp.getOneLetterCode().charAt(0); 108 } 109 mmtfDecoderInterface.setGroupInfo(group.getPDBName(), group.getResidueNumber().getSeqNum(), insCode.charValue(), 110 chemComp.getType().toUpperCase(), atomsInGroup.size(), MmtfUtils.getNumBondsInGroup(atomsInGroup), singleLetterCode, 111 sequenceGroups.indexOf(group), MmtfUtils.getSecStructType(group)); 112 for (Atom atom : atomsInGroup){ 113 char altLoc = MmtfStructure.UNAVAILABLE_CHAR_VALUE; 114 if(atom.getAltLoc()!=null){ 115 if(atom.getAltLoc().charValue()!=' '){ 116 altLoc=atom.getAltLoc().charValue(); 117 } 118 } 119 mmtfDecoderInterface.setAtomInfo(atom.getName(), atom.getPDBserial(), altLoc, (float) atom.getX(), 120 (float) atom.getY(), (float) atom.getZ(), atom.getOccupancy(), 121 atom.getTempFactor(), atom.getElement().toString(), atom.getCharge()); 122 addBonds(atom, atomsInGroup, allAtoms); 123 } 124 } 125 } 126 } 127 mmtfDecoderInterface.finalizeStructure(); 128 129 } 130 131 /** 132 * Add the bonds for a given atom. 133 * @param atom the atom for which bonds are to be formed 134 * @param atomsInGroup the list of atoms in the group 135 * @param allAtoms the list of atoms in the whole structure 136 */ 137 private void addBonds(Atom atom, List<Atom> atomsInGroup, List<Atom> allAtoms) { 138 if(atom.getBonds()==null){ 139 return; 140 } 141 for(Bond bond : atom.getBonds()) { 142 // Now set the bonding information. 143 Atom other = bond.getOther(atom); 144 // If both atoms are in the group 145 if (atomsInGroup.indexOf(other)!=-1){ 146 Integer firstBondIndex = atomsInGroup.indexOf(atom); 147 Integer secondBondIndex = atomsInGroup.indexOf(other); 148 // Don't add the same bond twice 149 if(firstBondIndex>secondBondIndex){ 150 int bondOrder = bond.getBondOrder(); 151 mmtfDecoderInterface.setGroupBond(firstBondIndex, secondBondIndex, bondOrder); 152 } 153 } 154 // Otherwise it's an inter group bond - so add it here 155 else { 156 Integer firstBondIndex = allAtoms.indexOf(atom); 157 Integer secondBondIndex = allAtoms.indexOf(other); 158 if(firstBondIndex>secondBondIndex){ 159 // Don't add the same bond twice 160 int bondOrder = bond.getBondOrder(); 161 mmtfDecoderInterface.setInterGroupBond(firstBondIndex, secondBondIndex, bondOrder); 162 } 163 } 164 } 165 } 166 167 168 /** 169 * Store the entity information for a given structure. 170 * @param allChains a list of all the chains in a structure 171 * @param entityInfos a list of the entity information 172 */ 173 private void storeEntityInformation(List<Chain> allChains, List<EntityInfo> entityInfos) { 174 for (EntityInfo entityInfo : entityInfos) { 175 String description = entityInfo.getDescription(); 176 String type; 177 if (entityInfo.getType()==null){ 178 type = null; 179 } 180 else{ 181 type = entityInfo.getType().getEntityType(); 182 } 183 List<Chain> entityChains = entityInfo.getChains(); 184 if (entityChains.isEmpty()){ 185 // Error mapping chain to entity 186 logger.error("ERROR MAPPING CHAIN TO ENTITY: "+description); 187 mmtfDecoderInterface.setEntityInfo(new int[0], "", description, type); 188 } 189 else{ 190 int[] chainIndices = new int[entityChains.size()]; 191 for (int i=0; i<entityChains.size(); i++) { 192 chainIndices[i] = allChains.indexOf(entityChains.get(i)); 193 } 194 Chain chain = entityChains.get(0); 195 ChainImpl chainImpl; 196 if (chain instanceof ChainImpl){ 197 chainImpl = (ChainImpl) entityChains.get(0); 198 } 199 else{ 200 throw new RuntimeException("Encountered Chain of unexpected type"); 201 } 202 String sequence = chainImpl.getSeqResOneLetterSeq(); 203 mmtfDecoderInterface.setEntityInfo(chainIndices, sequence, description, type); 204 } 205 } 206 } 207 208 209 /** 210 * Generate the bioassembly information on in the desired form. 211 * 212 */ 213 private void storeBioassemblyInformation(Map<String, Integer> chainIdToIndexMap, Map<Integer, BioAssemblyInfo> inputBioAss) { 214 int bioAssemblyIndex = 0; 215 for (Entry<Integer, BioAssemblyInfo> entry : inputBioAss.entrySet()) { 216 Map<double[], int[]> transformMap = MmtfUtils.getTransformMap(entry.getValue(), chainIdToIndexMap); 217 for(Entry<double[], int[]> transformEntry : transformMap.entrySet()) { 218 mmtfDecoderInterface.setBioAssemblyTrans(bioAssemblyIndex, transformEntry.getValue(), transformEntry.getKey(), entry.getKey().toString()); 219 } 220 bioAssemblyIndex++; 221 } 222 } 223 224}