/* * BioJava development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public Licence. This should * be distributed with the code. If you do not have a copy, * see: * * http://www.gnu.org/copyleft/lesser.html * * Copyright for this code is held jointly by the individual * authors. These should be listed in @author doc comments. * * For more information on the BioJava project and its aims, * or to join the biojava-l mailing list, visit the home page * at: * * http://www.biojava.org/ * */ package org.biojava.nbio.structure.quaternary; import org.biojava.nbio.structure.Calc; import org.biojava.nbio.structure.Chain; import org.biojava.nbio.structure.Structure; import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssembly; import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssemblyGen; import org.biojava.nbio.structure.io.mmcif.model.PdbxStructOperList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; /** * Reconstructs the quaternary structure of a protein from an asymmetric unit * * @author Peter Rose * @author Andreas Prlic * @author Jose Duarte * */ public class BiologicalAssemblyBuilder { private static final Logger logger = LoggerFactory.getLogger(BiologicalAssemblyBuilder.class); private OperatorResolver operatorResolver; private List<PdbxStructAssemblyGen> psags; private List<BiologicalAssemblyTransformation> modelTransformations; private List<String> modelIndex = new ArrayList<String>(); public BiologicalAssemblyBuilder(){ init(); } /** * Builds a Structure object containing the quaternary structure built from given asymUnit and transformations, * by adding symmetry partners as new models. * The output Structure will be different depending on the multiModel parameter: * <li> * the symmetry-expanded chains are added as new models, one per transformId. All original models but * the first one are discarded. * </li> * <li> * as original with symmetry-expanded chains added with renamed chain ids and names (in the form * originalAsymId_transformId and originalAuthId_transformId) * </li> * @param asymUnit * @param transformations * @param useAsymIds if true use {@link Chain#getId()} to match the ids in the BiologicalAssemblyTransformation (needed if data read from mmCIF), * if false use {@link Chain#getName()} for the chain matching (needed if data read from PDB). * @param multiModel if true the output Structure will be a multi-model one with one transformId per model, * if false the outputStructure will be as the original with added chains with renamed asymIds (in the form originalAsymId_transformId and originalAuthId_transformId). * @return */ public Structure rebuildQuaternaryStructure(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations, boolean useAsymIds, boolean multiModel) { // ensure that new chains are build in the same order as they appear in the asymmetric unit orderTransformationsByChainId(asymUnit, transformations); Structure s = asymUnit.clone(); // this resets all models (not only the first one): this is important for NMR (multi-model) // like that we can be sure we start with an empty structures and we add models or chains to it s.resetModels(); for (BiologicalAssemblyTransformation transformation : transformations){ List<Chain> chainsToTransform = new ArrayList<>(); // note: for NMR structures (or any multi-model) we use the first model only and throw away the rest if (useAsymIds) { Chain c = asymUnit.getChain(transformation.getChainId()); chainsToTransform.add(c); } else { Chain polyC = asymUnit.getPolyChainByPDB(transformation.getChainId()); List<Chain> nonPolyCs = asymUnit.getNonPolyChainsByPDB(transformation.getChainId()); Chain waterC = asymUnit.getWaterChainByPDB(transformation.getChainId()); if (polyC!=null) chainsToTransform.add(polyC); if (!nonPolyCs.isEmpty()) chainsToTransform.addAll(nonPolyCs); if (waterC!=null) chainsToTransform.add(waterC); } for (Chain c: chainsToTransform) { Chain chain = (Chain)c.clone(); Calc.transform(chain, transformation.getTransformationMatrix()); String transformId = transformation.getId(); // note that the Structure.addChain/Structure.addModel methods set the parent reference to the new Structure // TODO set entities properly in the new structures! at the moment they are a mess... - JD 2016-05-19 if (multiModel) addChainMultiModel(s, chain, transformId); else addChainFlattened(s, chain, transformId); } } s.setBiologicalAssembly(true); return s; } /** * Orders model transformations by chain ids in the same order as in the asymmetric unit * @param asymUnit * @param transformations */ private void orderTransformationsByChainId(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations) { final List<String> chainIds = getChainIds(asymUnit); Collections.sort(transformations, new Comparator<BiologicalAssemblyTransformation>() { @Override public int compare(BiologicalAssemblyTransformation t1, BiologicalAssemblyTransformation t2) { // set sort order only if the two ids are identical if (t1.getId().equals(t2.getId())) { return chainIds.indexOf(t1.getChainId()) - chainIds.indexOf(t2.getChainId()); } return 0; } }); } /** * Returns a list of chain ids in the order they are specified in the ATOM * records in the asymmetric unit * @param asymUnit * @return */ private List<String> getChainIds(Structure asymUnit) { List<String> chainIds = new ArrayList<String>(); for ( Chain c : asymUnit.getChains()){ String intChainID = c.getId(); chainIds.add(intChainID); } return chainIds; } /** * Adds a chain to the given structure to form a biological assembly, * adding the symmetry expanded chains as new models per transformId. * @param s * @param newChain * @param transformId */ private void addChainMultiModel(Structure s, Chain newChain, String transformId) { // multi-model bioassembly if ( modelIndex.size() == 0) modelIndex.add("PLACEHOLDER FOR ASYM UNIT"); int modelCount = modelIndex.indexOf(transformId); if ( modelCount == -1) { modelIndex.add(transformId); modelCount = modelIndex.indexOf(transformId); } if (modelCount == 0) { s.addChain(newChain); } else if (modelCount > s.nrModels()) { List<Chain> newModel = new ArrayList<Chain>(); newModel.add(newChain); s.addModel(newModel); } else { s.addChain(newChain, modelCount-1); } } /** * Adds a chain to the given structure to form a biological assembly, * adding the symmetry-expanded chains as new chains with renamed * chain ids and names (in the form originalAsymId_transformId and originalAuthId_transformId). * @param s * @param newChain * @param transformId */ private void addChainFlattened(Structure s, Chain newChain, String transformId) { newChain.setId(newChain.getId()+"_"+transformId); newChain.setName(newChain.getName()+"_"+transformId); s.addChain(newChain); } /** * Returns a list of transformation matrices for the generation of a macromolecular * assembly for the specified assembly Id. * * @param assemblyId Id of the macromolecular assembly to be generated * @return list of transformation matrices to generate macromolecular assembly */ public ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationList(PdbxStructAssembly psa, List<PdbxStructAssemblyGen> psags, List<PdbxStructOperList> operators) { //System.out.println("Rebuilding " + psa.getDetails() + " | " + psa.getOligomeric_details() + " | " + psa.getOligomeric_count()); //System.out.println(psag); init(); this.psags = psags; //psa.getId(); for (PdbxStructOperList oper: operators){ BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(); transform.setId(oper.getId()); transform.setRotationMatrix(oper.getMatrix().getArray()); transform.setTranslation(oper.getVector()); // transform.setTransformationMatrix(oper.getMatrix(), oper.getVector()); modelTransformations.add(transform); } ArrayList<BiologicalAssemblyTransformation> transformations = getBioUnitTransformationsListUnaryOperators(psa.getId()); transformations.addAll(getBioUnitTransformationsListBinaryOperators(psa.getId())); transformations.trimToSize(); return transformations; } private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListBinaryOperators(String assemblyId) { ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); List<OrderedPair<String>> operators = operatorResolver.getBinaryOperators(); for ( PdbxStructAssemblyGen psag : psags){ if ( psag.getAssembly_id().equals(assemblyId)) { List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(",")); operatorResolver.parseOperatorExpressionString(psag.getOper_expression()); // apply binary operators to the specified chains // Example 1M4X: generates all products of transformation matrices (1-60)(61-88) for (String chainId : asymIds) { int modelNumber = 1; for (OrderedPair<String> operator : operators) { BiologicalAssemblyTransformation original1 = getModelTransformationMatrix(operator.getElement1()); BiologicalAssemblyTransformation original2 = getModelTransformationMatrix(operator.getElement2()); // ModelTransformationMatrix transform = ModelTransformationMatrix.multiply4square_x_4square2(original1, original2); BiologicalAssemblyTransformation transform = BiologicalAssemblyTransformation.combine(original1, original2); transform.setChainId(chainId); // transform.setId(original1.getId() + "x" + original2.getId()); transform.setId(String.valueOf(modelNumber)); transformations.add(transform); modelNumber++; } } } } return transformations; } private BiologicalAssemblyTransformation getModelTransformationMatrix(String operator) { for (BiologicalAssemblyTransformation transform: modelTransformations) { if (transform.getId().equals(operator)) { return transform; } } logger.error("Could not find modelTransformationmatrix for " + operator); return new BiologicalAssemblyTransformation(); } private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListUnaryOperators(String assemblyId) { ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); for ( PdbxStructAssemblyGen psag : psags){ if ( psag.getAssembly_id().equals(assemblyId)) { operatorResolver.parseOperatorExpressionString(psag.getOper_expression()); List<String> operators = operatorResolver.getUnaryOperators(); List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(",")); // apply unary operators to the specified chains for (String chainId : asymIds) { for (String operator : operators) { BiologicalAssemblyTransformation original = getModelTransformationMatrix(operator); BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(original); transform.setChainId(chainId); transform.setId(operator); transformations.add(transform); } } } } return transformations; } private void init(){ operatorResolver= new OperatorResolver(); modelTransformations = new ArrayList<BiologicalAssemblyTransformation>(1); } }