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}