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