All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.biojava.nbio.structure.quaternary.BiologicalAssemblyBuilder Maven / Gradle / Ivy

The newest version!
/*
 *                    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.EntityInfo;
import org.biojava.nbio.structure.Structure;
import org.rcsb.cif.schema.mm.PdbxStructAssembly;
import org.rcsb.cif.schema.mm.PdbxStructAssemblyGen;
import org.rcsb.cif.schema.mm.PdbxStructOperList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.vecmath.Matrix4d;
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);

	/**
	 * The character separating the original chain identifier from the operator id.
	 */
	public static final String SYM_CHAIN_ID_SEPARATOR = "_";

	/**
	 * The character separating operator ids that are composed.
	 */
	public static final String COMPOSED_OPERATOR_SEPARATOR = "x";

	private OperatorResolver operatorResolver;


	/**
	 * All matrix operators present in _pdbx_struct_oper_list.
	 * Identifiers (_pdbx_struct_oper_list.id) to matrix operators.
	 */
	private Map allTransformations;

	private List modelIndex = new ArrayList<>();

	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:
	 * 
    *
  • * the symmetry-expanded chains are added as new models, one per transformId. All original models but * the first one are discarded. *
  • *
  • * as original with symmetry-expanded chains added with renamed chain ids and names (in the form * originalAsymId_transformId and originalAuthId_transformId) *
  • *
* @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 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(); Map entityInfoMap = new HashMap<>(); // 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(); s.setEntityInfos(new ArrayList<>()); for (BiologicalAssemblyTransformation transformation : transformations){ List 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 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 if (multiModel) addChainMultiModel(s, chain, transformId); else addChainFlattened(s, chain, transformId); EntityInfo entityInfo; if (!entityInfoMap.containsKey(chain.getEntityInfo().getMolId())) { entityInfo = new EntityInfo(chain.getEntityInfo()); entityInfoMap.put(chain.getEntityInfo().getMolId(), entityInfo); s.addEntityInfo(entityInfo); } else { entityInfo = entityInfoMap.get(chain.getEntityInfo().getMolId()); } chain.setEntityInfo(entityInfo); entityInfo.addChain(chain); } } 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 transformations) { final List chainIds = getChainIds(asymUnit); Collections.sort(transformations, new Comparator() { @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()); } else { return t1.getId().compareTo(t2.getId()); } } }); } /** * 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 getChainIds(Structure asymUnit) { List chainIds = new ArrayList<>(); 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 newModel = new ArrayList<>(); 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()+SYM_CHAIN_ID_SEPARATOR+transformId); newChain.setName(newChain.getName()+SYM_CHAIN_ID_SEPARATOR+transformId); s.addChain(newChain); } /** * Returns a list of transformation matrices for the generation of a macromolecular * assembly for the specified assembly Id. * * @param pdbxStructAssembly * @param assemblyIndex * @param pdbxStructAssemblyGen * @param pdbxStructOperList * @return list of transformation matrices to generate macromolecular assembly */ public List getBioUnitTransformationList(PdbxStructAssembly pdbxStructAssembly, int assemblyIndex, PdbxStructAssemblyGen pdbxStructAssemblyGen, PdbxStructOperList pdbxStructOperList) { init(); // first we populate the list of all operators from pdbx_struct_oper_list so that we can then // get them from getBioUnitTransformationsListUnaryOperators() and getBioUnitTransformationsListBinaryOperators() for (int i = 0; i < pdbxStructOperList.getRowCount(); i++) { try { Matrix4d m = new Matrix4d(); m.m00 = pdbxStructOperList.getMatrix11().get(i); m.m01 = pdbxStructOperList.getMatrix12().get(i); m.m02 = pdbxStructOperList.getMatrix13().get(i); m.m10 = pdbxStructOperList.getMatrix21().get(i); m.m11 = pdbxStructOperList.getMatrix22().get(i); m.m12 = pdbxStructOperList.getMatrix23().get(i); m.m20 = pdbxStructOperList.getMatrix31().get(i); m.m21 = pdbxStructOperList.getMatrix32().get(i); m.m22 = pdbxStructOperList.getMatrix33().get(i); m.m03 = pdbxStructOperList.getVector1().get(i); m.m13 = pdbxStructOperList.getVector2().get(i); m.m23 = pdbxStructOperList.getVector3().get(i); m.m30 = 0; m.m31 = 0; m.m32 = 0; m.m33 = 1; allTransformations.put(pdbxStructOperList.getId().get(i), m); } catch (NumberFormatException e) { logger.warn("Could not parse a matrix value from pdbx_struct_oper_list for id {}. The operator id will be ignored. Error: {}", pdbxStructOperList.getId().get(i), e.getMessage()); } } String assemblyId = pdbxStructAssembly.getId().get(assemblyIndex); ArrayList transformations = getBioUnitTransformationsListUnaryOperators(assemblyId, pdbxStructAssemblyGen); transformations.addAll(getBioUnitTransformationsListBinaryOperators(assemblyId, pdbxStructAssemblyGen)); transformations.trimToSize(); return transformations; } private ArrayList getBioUnitTransformationsListBinaryOperators(String assemblyId, PdbxStructAssemblyGen pdbxStructAssemblyGen) { ArrayList transformations = new ArrayList<>(); List> operators = operatorResolver.getBinaryOperators(); for (int i = 0; i < pdbxStructAssemblyGen.getRowCount(); i++) { if (!pdbxStructAssemblyGen.getAssemblyId().get(i).equals(assemblyId)) { continue; } String[] asymIds= pdbxStructAssemblyGen.getAsymIdList().get(i).split(","); operatorResolver.parseOperatorExpressionString(pdbxStructAssemblyGen.getOperExpression().get(i)); // apply binary operators to the specified chains // Example 1M4X: generates all products of transformation matrices (1-60)(61-88) for (String chainId : asymIds) { for (OrderedPair operator : operators) { Matrix4d original1 = allTransformations.get(operator.getElement1()); Matrix4d original2 = allTransformations.get(operator.getElement2()); if (original1 == null || original2 == null) { logger.warn("Could not find matrix operator for operator id {} or {}. Assembly id {} will not contain the composed operator.", operator.getElement1(), operator.getElement2(), assemblyId); continue; } Matrix4d composed = new Matrix4d(original1); composed.mul(original2); BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(); transform.setChainId(chainId); transform.setId(operator.getElement1() + COMPOSED_OPERATOR_SEPARATOR + operator.getElement2()); transform.setTransformationMatrix(composed); transformations.add(transform); } } } return transformations; } private ArrayList getBioUnitTransformationsListUnaryOperators(String assemblyId, PdbxStructAssemblyGen pdbxStructAssemblyGen) { ArrayList transformations = new ArrayList<>(); for (int i = 0; i < pdbxStructAssemblyGen.getRowCount(); i++) { if (!pdbxStructAssemblyGen.getAssemblyId().get(i).equals(assemblyId)) { continue; } operatorResolver.parseOperatorExpressionString(pdbxStructAssemblyGen.getOperExpression().get(i)); List operators = operatorResolver.getUnaryOperators(); String[] asymIds = pdbxStructAssemblyGen.getAsymIdList().get(i).split(","); // apply unary operators to the specified chains for (String chainId : asymIds) { for (String operator : operators) { Matrix4d original = allTransformations.get(operator); if (original == null) { logger.warn("Could not find matrix operator for operator id {}. Assembly id {} will not contain the operator.", operator, assemblyId); continue; } BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(); transform.setChainId(chainId); transform.setId(operator); transform.setTransformationMatrix(original); transformations.add(transform); } } } return transformations; } private void init() { operatorResolver = new OperatorResolver(); allTransformations = new HashMap<>(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy