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