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.Calc; 025import org.biojava.nbio.structure.Chain; 026import org.biojava.nbio.structure.Structure; 027import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssembly; 028import org.biojava.nbio.structure.io.mmcif.model.PdbxStructAssemblyGen; 029import org.biojava.nbio.structure.io.mmcif.model.PdbxStructOperList; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import java.util.*; 034 035/** 036 * Reconstructs the quaternary structure of a protein from an asymmetric unit 037 * 038 * @author Peter Rose 039 * @author Andreas Prlic 040 * @author Jose Duarte 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 /** 059 * Builds a Structure object containing the quaternary structure built from given asymUnit and transformations, 060 * by adding symmetry partners as new models. 061 * The output Structure will be different depending on the multiModel parameter: 062 * <li> 063 * the symmetry-expanded chains are added as new models, one per transformId. All original models but 064 * the first one are discarded. 065 * </li> 066 * <li> 067 * as original with symmetry-expanded chains added with renamed chain ids and names (in the form 068 * originalAsymId_transformId and originalAuthId_transformId) 069 * </li> 070 * @param asymUnit 071 * @param transformations 072 * @param useAsymIds if true use {@link Chain#getId()} to match the ids in the BiologicalAssemblyTransformation (needed if data read from mmCIF), 073 * if false use {@link Chain#getName()} for the chain matching (needed if data read from PDB). 074 * @param multiModel if true the output Structure will be a multi-model one with one transformId per model, 075 * if false the outputStructure will be as the original with added chains with renamed asymIds (in the form originalAsymId_transformId and originalAuthId_transformId). 076 * @return 077 */ 078 public Structure rebuildQuaternaryStructure(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations, boolean useAsymIds, boolean multiModel) { 079 080 // ensure that new chains are build in the same order as they appear in the asymmetric unit 081 orderTransformationsByChainId(asymUnit, transformations); 082 083 Structure s = asymUnit.clone(); 084 085 086 // this resets all models (not only the first one): this is important for NMR (multi-model) 087 // like that we can be sure we start with an empty structures and we add models or chains to it 088 s.resetModels(); 089 090 for (BiologicalAssemblyTransformation transformation : transformations){ 091 092 List<Chain> chainsToTransform = new ArrayList<>(); 093 094 // note: for NMR structures (or any multi-model) we use the first model only and throw away the rest 095 if (useAsymIds) { 096 Chain c = asymUnit.getChain(transformation.getChainId()); 097 chainsToTransform.add(c); 098 } else { 099 Chain polyC = asymUnit.getPolyChainByPDB(transformation.getChainId()); 100 List<Chain> nonPolyCs = asymUnit.getNonPolyChainsByPDB(transformation.getChainId()); 101 Chain waterC = asymUnit.getWaterChainByPDB(transformation.getChainId()); 102 if (polyC!=null) 103 chainsToTransform.add(polyC); 104 if (!nonPolyCs.isEmpty()) 105 chainsToTransform.addAll(nonPolyCs); 106 if (waterC!=null) 107 chainsToTransform.add(waterC); 108 } 109 110 for (Chain c: chainsToTransform) { 111 112 Chain chain = (Chain)c.clone(); 113 114 Calc.transform(chain, transformation.getTransformationMatrix()); 115 116 String transformId = transformation.getId(); 117 118 // note that the Structure.addChain/Structure.addModel methods set the parent reference to the new Structure 119 120 // TODO set entities properly in the new structures! at the moment they are a mess... - JD 2016-05-19 121 122 if (multiModel) 123 addChainMultiModel(s, chain, transformId); 124 else 125 addChainFlattened(s, chain, transformId); 126 127 } 128 } 129 130 s.setBiologicalAssembly(true); 131 return s; 132 } 133 134 /** 135 * Orders model transformations by chain ids in the same order as in the asymmetric unit 136 * @param asymUnit 137 * @param transformations 138 */ 139 private void orderTransformationsByChainId(Structure asymUnit, List<BiologicalAssemblyTransformation> transformations) { 140 final List<String> chainIds = getChainIds(asymUnit); 141 Collections.sort(transformations, new Comparator<BiologicalAssemblyTransformation>() { 142 @Override 143 public int compare(BiologicalAssemblyTransformation t1, BiologicalAssemblyTransformation t2) { 144 // set sort order only if the two ids are identical 145 if (t1.getId().equals(t2.getId())) { 146 return chainIds.indexOf(t1.getChainId()) - chainIds.indexOf(t2.getChainId()); 147 } 148 return 0; 149 } 150 }); 151 } 152 153 /** 154 * Returns a list of chain ids in the order they are specified in the ATOM 155 * records in the asymmetric unit 156 * @param asymUnit 157 * @return 158 */ 159 private List<String> getChainIds(Structure asymUnit) { 160 List<String> chainIds = new ArrayList<String>(); 161 for ( Chain c : asymUnit.getChains()){ 162 String intChainID = c.getId(); 163 chainIds.add(intChainID); 164 } 165 return chainIds; 166 } 167 168 /** 169 * Adds a chain to the given structure to form a biological assembly, 170 * adding the symmetry expanded chains as new models per transformId. 171 * @param s 172 * @param newChain 173 * @param transformId 174 */ 175 private void addChainMultiModel(Structure s, Chain newChain, String transformId) { 176 177 // multi-model bioassembly 178 179 if ( modelIndex.size() == 0) 180 modelIndex.add("PLACEHOLDER FOR ASYM UNIT"); 181 182 int modelCount = modelIndex.indexOf(transformId); 183 if ( modelCount == -1) { 184 modelIndex.add(transformId); 185 modelCount = modelIndex.indexOf(transformId); 186 } 187 188 if (modelCount == 0) { 189 s.addChain(newChain); 190 } else if (modelCount > s.nrModels()) { 191 List<Chain> newModel = new ArrayList<Chain>(); 192 newModel.add(newChain); 193 s.addModel(newModel); 194 } else { 195 s.addChain(newChain, modelCount-1); 196 } 197 198 } 199 200 /** 201 * Adds a chain to the given structure to form a biological assembly, 202 * adding the symmetry-expanded chains as new chains with renamed 203 * chain ids and names (in the form originalAsymId_transformId and originalAuthId_transformId). 204 * @param s 205 * @param newChain 206 * @param transformId 207 */ 208 private void addChainFlattened(Structure s, Chain newChain, String transformId) { 209 newChain.setId(newChain.getId()+"_"+transformId); 210 newChain.setName(newChain.getName()+"_"+transformId); 211 s.addChain(newChain); 212 } 213 214 /** 215 * Returns a list of transformation matrices for the generation of a macromolecular 216 * assembly for the specified assembly Id. 217 * 218 * @param assemblyId Id of the macromolecular assembly to be generated 219 * @return list of transformation matrices to generate macromolecular assembly 220 */ 221 public ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationList(PdbxStructAssembly psa, List<PdbxStructAssemblyGen> psags, List<PdbxStructOperList> operators) { 222 //System.out.println("Rebuilding " + psa.getDetails() + " | " + psa.getOligomeric_details() + " | " + psa.getOligomeric_count()); 223 //System.out.println(psag); 224 init(); 225 this.psags = psags; 226 227 //psa.getId(); 228 229 for (PdbxStructOperList oper: operators){ 230 BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(); 231 transform.setId(oper.getId()); 232 transform.setRotationMatrix(oper.getMatrix().getArray()); 233 transform.setTranslation(oper.getVector()); 234// transform.setTransformationMatrix(oper.getMatrix(), oper.getVector()); 235 modelTransformations.add(transform); 236 } 237 238 ArrayList<BiologicalAssemblyTransformation> transformations = getBioUnitTransformationsListUnaryOperators(psa.getId()); 239 transformations.addAll(getBioUnitTransformationsListBinaryOperators(psa.getId())); 240 transformations.trimToSize(); 241 return transformations; 242 } 243 244 245 private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListBinaryOperators(String assemblyId) { 246 247 ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); 248 249 List<OrderedPair<String>> operators = operatorResolver.getBinaryOperators(); 250 251 252 for ( PdbxStructAssemblyGen psag : psags){ 253 if ( psag.getAssembly_id().equals(assemblyId)) { 254 255 List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(",")); 256 257 operatorResolver.parseOperatorExpressionString(psag.getOper_expression()); 258 259 // apply binary operators to the specified chains 260 // Example 1M4X: generates all products of transformation matrices (1-60)(61-88) 261 for (String chainId : asymIds) { 262 263 int modelNumber = 1; 264 for (OrderedPair<String> operator : operators) { 265 BiologicalAssemblyTransformation original1 = getModelTransformationMatrix(operator.getElement1()); 266 BiologicalAssemblyTransformation original2 = getModelTransformationMatrix(operator.getElement2()); 267 // ModelTransformationMatrix transform = ModelTransformationMatrix.multiply4square_x_4square2(original1, original2); 268 BiologicalAssemblyTransformation transform = BiologicalAssemblyTransformation.combine(original1, original2); 269 transform.setChainId(chainId); 270 // transform.setId(original1.getId() + "x" + original2.getId()); 271 transform.setId(String.valueOf(modelNumber)); 272 transformations.add(transform); 273 modelNumber++; 274 } 275 } 276 } 277 278 } 279 280 return transformations; 281 } 282 283 private BiologicalAssemblyTransformation getModelTransformationMatrix(String operator) { 284 for (BiologicalAssemblyTransformation transform: modelTransformations) { 285 if (transform.getId().equals(operator)) { 286 return transform; 287 } 288 } 289 logger.error("Could not find modelTransformationmatrix for " + operator); 290 return new BiologicalAssemblyTransformation(); 291 } 292 293 private ArrayList<BiologicalAssemblyTransformation> getBioUnitTransformationsListUnaryOperators(String assemblyId) { 294 ArrayList<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); 295 296 297 for ( PdbxStructAssemblyGen psag : psags){ 298 if ( psag.getAssembly_id().equals(assemblyId)) { 299 300 operatorResolver.parseOperatorExpressionString(psag.getOper_expression()); 301 List<String> operators = operatorResolver.getUnaryOperators(); 302 303 List<String>asymIds= Arrays.asList(psag.getAsym_id_list().split(",")); 304 305 // apply unary operators to the specified chains 306 for (String chainId : asymIds) { 307 for (String operator : operators) { 308 309 BiologicalAssemblyTransformation original = getModelTransformationMatrix(operator); 310 BiologicalAssemblyTransformation transform = new BiologicalAssemblyTransformation(original); 311 transform.setChainId(chainId); 312 transform.setId(operator); 313 transformations.add(transform); 314 } 315 } 316 } 317 } 318 319 return transformations; 320 } 321 322 private void init(){ 323 operatorResolver= new OperatorResolver(); 324 modelTransformations = new ArrayList<BiologicalAssemblyTransformation>(1); 325 } 326}