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 */
021
022package org.biojava.nbio.structure.quaternary;
023
024import org.biojava.nbio.structure.Atom;
025import org.biojava.nbio.structure.Chain;
026import org.biojava.nbio.structure.Group;
027import org.biojava.nbio.structure.Structure;
028import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssembly;
029import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssemblyGen;
030import org.biojava.nbio.structure.io.mmcif.model.PdbxStructOperList;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import java.util.*;
035
036/**
037 * Reconstructs the quaternary structure of a protein from an asymmetric unit
038 *
039 * @author Peter Rose
040 * @author Andreas Prlic
041 *
042 */
043public class BiologicalAssemblyBuilder {
044
045        private static final Logger logger = LoggerFactory.getLogger(BiologicalAssemblyBuilder.class);
046
047        private OperatorResolver operatorResolver;
048        private List<PdbxStructAssemblyGen> psags;
049
050        private List<BiologicalAssemblyTransformation> modelTransformations;
051
052        private List<String> modelIndex = new ArrayList<String>();
053
054        public BiologicalAssemblyBuilder(){
055                init();
056        }
057
058        public Structure rebuildQuaternaryStructure(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations){
059                // ensure that new chains are build in the same order as they appear in the asymmetric unit
060        orderTransformationsByChainId(asymUnit, transformations);
061
062                Structure s = asymUnit.clone();
063                // this resets all models (not only the first one): this is important for NMR (multi-model)
064                // structures, otherwise we could not add new models below
065                s.resetModels();
066
067                for (BiologicalAssemblyTransformation transformation : transformations){
068
069                        // note: for NMR structures (or any multi-model) we use the first model only and throw away the rest
070                        for (Chain c : asymUnit.getChains()){
071
072                                String intChainID = c.getInternalChainID();
073                                if (intChainID == null) {
074                                        logger.info("No internal chain ID found while building bioassembly, using chain ID instead: " + c.getChainID());
075                                        intChainID = c.getChainID();
076                                }
077
078                                if (transformation.getChainId().equals(intChainID)){
079                                        Chain chain = (Chain)c.clone();
080
081                                        for (Group g : chain.getAtomGroups()) {
082
083                                                for (Atom a: g.getAtoms()) {
084
085                                                        transformation.transformPoint(a.getCoords());
086
087                                                }
088                                        }
089
090                                        String transformId = transformation.getId();
091
092                                        addChainAndModel(s, chain, transformId);
093                                }
094                        }
095                }
096
097                s.setBiologicalAssembly(true);
098                return s;
099        }
100
101        /**
102         * Orders model transformations by chain ids in the same order as in the asymmetric unit
103         * @param asymUnit
104         * @param transformations
105         */
106        private void orderTransformationsByChainId(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations) {
107                final List<String> chainIds = getChainIds(asymUnit);
108                Collections.sort(transformations, new Comparator<BiologicalAssemblyTransformation>() {
109                        @Override
110                        public int compare(BiologicalAssemblyTransformation t1, BiologicalAssemblyTransformation t2) {
111                                // set sort order only if the two ids are identical
112                                if (t1.getId().equals(t2.getId())) {
113                                         return chainIds.indexOf(t1.getChainId()) - chainIds.indexOf(t2.getChainId());
114                                }
115                            return 0;
116                    }
117                });
118        }
119
120        /**
121         * Returns a list of chain ids in the order they are specified in the ATOM
122         * records in the asymmetric unit
123         * @param asymUnit
124         * @return
125         */
126        private List<String> getChainIds(Structure asymUnit) {
127                List<String> chainIds = new ArrayList<String>();
128                for ( Chain c : asymUnit.getChains()){
129                        String intChainID = c.getInternalChainID();
130                        if ( intChainID == null) {
131                                //System.err.println("no internal chain ID found, using " + c.getChainID() + " ( while looking for " + max.ndbChainId+")");
132                                intChainID = c.getChainID();
133                        }
134                        chainIds.add(intChainID);
135                }
136                return chainIds;
137        }
138
139        private void addChainAndModel(Structure s, Chain newChain, String modelId) {
140
141                if ( modelIndex.size() == 0)
142                        modelIndex.add("PLACEHOLDER FOR ASYM UNIT");
143
144                int modelCount = modelIndex.indexOf(modelId);
145                if ( modelCount == -1)  {
146                        modelIndex.add(modelId);
147                        modelCount = modelIndex.indexOf(modelId);
148                }
149
150                if (modelCount == 0) {
151                        s.addChain(newChain);
152                } else if (modelCount > s.nrModels()) {
153                        List<Chain> newModel = new ArrayList<Chain>();
154                        newModel.add(newChain);
155                        s.addModel(newModel);
156                } else {
157                        s.addChain(newChain, modelCount-1);
158                }
159        }
160
161        /**
162         * Returns a list of transformation matrices for the generation of a macromolecular
163         * assembly for the specified assembly Id.
164         *
165         * @param assemblyId Id of the macromolecular assembly to be generated
166         * @return list of transformation matrices to generate macromolecular assembly
167         */
168        public ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationList(PdbxStructAssembly psa, List<PdbxStructAssemblyGen> psags, List<PdbxStructOperList> operators) {
169                //System.out.println("Rebuilding " + psa.getDetails() + " | " + psa.getOligomeric_details() + " | " + psa.getOligomeric_count());
170                //System.out.println(psag);
171                init();
172                this.psags = psags;
173
174                //psa.getId();
175
176                for (PdbxStructOperList oper: operators){
177                        BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation();
178                        transform.setId(oper.getId());
179                        transform.setRotationMatrix(oper.getMatrix().getArray());
180                        transform.setTranslation(oper.getVector());
181//                      transform.setTransformationMatrix(oper.getMatrix(), oper.getVector());
182                        modelTransformations.add(transform);
183                }
184
185                ArrayList<BiologicalAssemblyTransformation> transformations = getBioUnitTransformationsListUnaryOperators(psa.getId());
186                transformations.addAll(getBioUnitTransformationsListBinaryOperators(psa.getId()));
187                transformations.trimToSize();
188                return transformations;
189        }
190
191
192        private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListBinaryOperators(String assemblyId) {
193
194                ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>();
195
196                List<OrderedPair<String>> operators = operatorResolver.getBinaryOperators();
197
198
199                for ( PdbxStructAssemblyGen psag : psags){
200                        if ( psag.getAssembly_id().equals(assemblyId)) {
201
202                                List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(","));
203
204                                operatorResolver.parseOperatorExpressionString(psag.getOper_expression());
205
206                                // apply binary operators to the specified chains
207                                // Example 1M4X: generates all products of transformation matrices (1-60)(61-88)
208                                for (String chainId : asymIds) {
209
210                                        int modelNumber = 1;
211                                        for (OrderedPair<String> operator : operators) {
212                                                BiologicalAssemblyTransformation original1 = getModelTransformationMatrix(operator.getElement1());
213                                                BiologicalAssemblyTransformation original2 = getModelTransformationMatrix(operator.getElement2());
214                        //                      ModelTransformationMatrix transform = ModelTransformationMatrix.multiply4square_x_4square2(original1, original2);
215                                                BiologicalAssemblyTransformation transform = BiologicalAssemblyTransformation.combine(original1, original2);
216                                                transform.setChainId(chainId);
217                                //              transform.setId(original1.getId() + "x" + original2.getId());
218                                                transform.setId(String.valueOf(modelNumber));
219                                                transformations.add(transform);
220                                                modelNumber++;
221                                        }
222                                }
223                        }
224
225                }
226
227                return transformations;
228        }
229
230        private BiologicalAssemblyTransformation getModelTransformationMatrix(String operator) {
231                for (BiologicalAssemblyTransformation transform: modelTransformations) {
232                        if (transform.getId().equals(operator)) {
233                                return transform;
234                        }
235                }
236                logger.error("Could not find modelTransformationmatrix for " + operator);
237                return new BiologicalAssemblyTransformation();
238        }
239
240        private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListUnaryOperators(String assemblyId) {
241                ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>();
242
243
244                for ( PdbxStructAssemblyGen psag : psags){
245                        if ( psag.getAssembly_id().equals(assemblyId)) {
246
247                                operatorResolver.parseOperatorExpressionString(psag.getOper_expression());
248                                List<String> operators = operatorResolver.getUnaryOperators();
249
250                                List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(","));
251
252                                // apply unary operators to the specified chains
253                                for (String chainId : asymIds) {
254                                        for (String operator : operators) {
255
256                                                BiologicalAssemblyTransformation original = getModelTransformationMatrix(operator);
257                                                BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(original);
258                                                transform.setChainId(chainId);
259                                                transform.setId(operator);
260                                                transformations.add(transform);
261                                        }
262                                }
263                        }
264                }
265
266                return transformations;
267        }
268
269        private void init(){
270                operatorResolver= new OperatorResolver();
271                modelTransformations = new ArrayList<BiologicalAssemblyTransformation>(1);
272        }
273}