1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2006-10-15 17:34:01 -0500 (Sun, 15 Oct 2006) $ 4 * $Revision: 5957 $ 5 * 6 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 25 package org.jmol.adapter.readers.cif; 26 27 import java.util.Hashtable; 28 import java.util.Map; 29 30 import javajs.util.Lst; 31 import javajs.util.M4; 32 import javajs.util.MessagePackReader; 33 import javajs.util.PT; 34 import javajs.util.SB; 35 36 import org.jmol.adapter.smarter.Atom; 37 import org.jmol.adapter.smarter.Bond; 38 import org.jmol.adapter.smarter.Structure; 39 import javajs.util.BS; 40 import org.jmol.script.SV; 41 import org.jmol.util.Logger; 42 43 /** 44 * JmolData RCSB MMTF (macromolecular transmission format) file reader 45 * 46 * see https://github.com/rcsb/mmtf/blob/master/spec.md 47 * 48 * full specification Version: v0.2+dev (as of 2016.08.08) is 49 * implemented,including: 50 * 51 * reading atoms, bonds, and DSSP 1.0 secondary structure 52 * 53 * load =1f88.mmtf filter "DSSP1" 54 * 55 * [Note that the filter "DSSP1" is required, since mmtf included DSSP 1.0 56 * calculations, while the standard for Jmol itself is DSSP 2.0. These two 57 * calculations differ in their treating of helix kinks as one (1.0) or two 58 * (2.0) helices.] 59 * 60 * 61 * reading space groups and unit cells, and using those as per other readers 62 * 63 * load =1crn.mmtf {1 1 1} 64 * 65 * reading bioassemblies (biomolecules) and applying all symmetry 66 * transformations 67 * 68 * load =1auy.mmtf FILTER "biomolecule 1;*.CA,*.P" 69 * 70 * reading both biomolecules and lattices, and loading course-grained using the 71 * filter "BYCHAIN" or "BYSYMOP" 72 * 73 * load =1auy.mmtf {2 2 1} filter "biomolecule 1;bychain";spacefill 30.0; color 74 * property symop 75 * 76 * Many thanks to the MMTF team at RCSB for assistance in this implementation. 77 * 78 * @author Bob Hanson hansonr@stolaf.edu 79 * 80 */ 81 82 public class MMTFReader extends MMCifReader { 83 84 private boolean haveStructure; 85 private String pdbID; 86 87 88 @Override addHeader()89 protected void addHeader() { 90 // no header for this type 91 } 92 93 /** 94 * standard set up 95 * 96 * @param fullPath 97 * @param htParams 98 * @param reader 99 */ 100 @Override setup(String fullPath, Map<String, Object> htParams, Object reader)101 protected void setup(String fullPath, Map<String, Object> htParams, 102 Object reader) { 103 isBinary = true; 104 isMMCIF = true; 105 iHaveFractionalCoordinates = false; 106 setupASCR(fullPath, htParams, reader); 107 } 108 109 @Override processBinaryDocument()110 protected void processBinaryDocument() throws Exception { 111 112 // load xxx.mmtf filter "..." options 113 114 // NODOUBLE -- standard PDB-like structures 115 // DSSP2 -- use Jmol's built-in DSSP 2.0, not file's DSSP 1.0 116 117 boolean doDoubleBonds = (!isCourseGrained && !checkFilterKey("NODOUBLE")); 118 isDSSP1 = !checkFilterKey("DSSP2"); 119 boolean mmtfImplementsDSSP2 = false; // so far! 120 applySymmetryToBonds = true; 121 map = (new MessagePackReader(binaryDoc, true)).readMap(); 122 entities = (Object[]) map.get("entityList"); 123 if (Logger.debugging) { 124 for (String s : map.keySet()) 125 Logger.info(s); 126 } 127 asc.setInfo("noAutoBond", Boolean.TRUE); 128 Logger.info("MMTF version " + map.get("mmtfVersion")); 129 Logger.info("MMTF Producer " + map.get("mmtfProducer")); 130 String title = (String) map.get("title"); 131 if (title != null) 132 appendLoadNote(title); 133 pdbID = (String) map.get("structureId"); 134 if (pdbID == null) 135 pdbID = (String) map.get("pdbId"); 136 fileAtomCount = ((Integer) map.get("numAtoms")).intValue(); 137 int nBonds = ((Integer) map.get("numBonds")).intValue(); 138 139 groupCount = ((Integer) map.get("numGroups")).intValue(); 140 groupModels = new int[groupCount]; // group model (file-based) 141 groupDSSP = new int[groupCount]; // group structure type (Jmol-based) 142 groupMap = new int[groupCount]; // file->jmol group index map 143 144 int modelCount = ((Integer) map.get("numModels")).intValue(); 145 appendLoadNote("id=" + pdbID + " numAtoms=" + fileAtomCount + " numBonds=" 146 + nBonds + " numGroups=" + groupCount + " numModels=" + modelCount); 147 getMMTFAtoms(doDoubleBonds); 148 if (!isCourseGrained) { 149 int[] bo = (int[]) decode("bondOrderList"); 150 int[] bi = (int[]) decode("bondAtomList"); 151 addMMTFBonds(bo, bi, 0, doDoubleBonds, true); 152 if (isDSSP1 || mmtfImplementsDSSP2) 153 getStructure(); 154 } 155 setMMTFSymmetry(); 156 getMMTFBioAssembly(); 157 setModelPDB(true); 158 if (Logger.debuggingHigh) 159 Logger.info(SV.getVariable(map).asString()); 160 } 161 162 @Override applySymmetryAndSetTrajectory()163 public void applySymmetryAndSetTrajectory() throws Exception { 164 ac0 = ac; 165 super.applySymmetryAndSetTrajectory(); 166 if (haveStructure) 167 addStructureSymmetry(); 168 } 169 170 //////////////////////////////// MMTF-Specific ///////////////////////// 171 172 private Map<String, Object> map; // input JSON-like map from MessagePack binary file 173 private int fileAtomCount; 174 private int opCount = 0; 175 private int[] groupModels, groupMap, groupDSSP, atomGroup; 176 private String[] labelAsymList; // created in getAtoms; used in getBioAssembly 177 private Atom[] atomMap; // necessary because some atoms may be deleted. 178 private Object[] entities; 179 private int groupCount; 180 private int ac0; 181 private BS[] bsStructures; 182 private int lastGroup; 183 184 185 // TODO - also consider mapping group indices 186 187 /** 188 * set up all atoms, including bonding, within a group 189 * 190 * @param doMulti 191 * true to add double bonds 192 * 193 * @throws Exception 194 */ 195 @SuppressWarnings("unchecked") getMMTFAtoms(boolean doMulti)196 private void getMMTFAtoms(boolean doMulti) throws Exception { 197 198 // chains 199 int[] chainsPerModel = (int[]) map.get("chainsPerModel"); 200 int[] groupsPerChain = (int[]) map.get("groupsPerChain"); // note that this is label_asym, not auth_asym 201 labelAsymList = (String[]) decode("chainIdList"); // label_asym 202 String[] authAsymList = (String[]) decode("chainNameList"); // Auth_asym 203 204 // groups 205 int[] groupTypeList = (int[]) decode("groupTypeList"); 206 int[] groupIdList = (int[]) decode("groupIdList"); 207 Object[] groupList = (Object[]) map.get("groupList"); 208 char[] insCodes = (char[]) decode("insCodeList"); 209 int[] atomId = (int[]) decode("atomIdList"); 210 boolean haveSerial = (atomId != null); 211 char[] altloc = (char[]) decode("altLocList"); // rldecode32 212 float[] occ = (float[]) decode("occupancyList"); 213 float[] x = (float[]) decode("xCoordList");//getFloatsSplit("xCoord", 1000f); 214 float[] y = (float[]) decode("yCoordList"); 215 float[] z = (float[]) decode("zCoordList"); 216 float[] bf = (float[]) decode("bFactorList"); 217 String[] nameList = (useAuthorChainID ? authAsymList : labelAsymList); 218 int iModel = -1; 219 int iChain = 0; 220 int nChain = 0; 221 int iGroup = 0; 222 int nGroup = 0; 223 int chainpt = 0; 224 int seqNo = 0; 225 int iatom = 0; 226 String chainID = ""; 227 String authAsym = "", labelAsym = ""; 228 char insCode = '\0'; 229 atomMap = new Atom[fileAtomCount]; 230 atomGroup = new int[fileAtomCount]; 231 for (int j = 0, thisGroup = -1; j < groupCount; j++) { 232 if (++iGroup >= nGroup) { 233 chainID = nameList[chainpt]; 234 authAsym = authAsymList[chainpt]; 235 labelAsym = labelAsymList[chainpt]; 236 nGroup = groupsPerChain[chainpt++]; 237 iGroup = 0; 238 if (++iChain >= nChain) { 239 groupModels[j] = ++iModel; 240 nChain = chainsPerModel[iModel]; 241 iChain = 0; 242 setModelPDB(true); 243 incrementModel(iModel + 1); 244 asc.setCurrentModelInfo("pdbID", pdbID); 245 nAtoms0 = asc.ac; 246 if (done) 247 return; 248 } 249 } 250 Map<String, Object> g = (Map<String, Object>) groupList[groupTypeList[j]]; 251 String[] atomNameList = (String[]) g.get("atomNameList"); 252 int len = atomNameList.length; 253 if (skipping) { 254 iatom += len; 255 continue; 256 } 257 int a0 = iatom; 258 if (insCodes != null) 259 insCode = insCodes[j]; 260 seqNo = groupIdList[j]; 261 String group3 = (String) g.get("groupName"); 262 boolean isHetero = vwr.getJBR().isHetero(group3); 263 if (isHetero) { 264 // looking for CHEM_COMP_NAME here... "IRON/SULFUR CLUSTER" for SF4 in 1blu 265 String hetName = "" + g.get("chemCompType"); 266 if (htHetero == null || !htHetero.containsKey(group3)) { 267 268 // this is not ideal -- see 1a37.mmtf, where PSE is LSQRQRST(PSE)TPNVHM 269 // (actually that should be phosphoserine, SPE...) 270 // It is still listed as NON-POLYMER even though it is just a modified AA. 271 272 if (entities != null && hetName.equals("NON-POLYMER")) 273 out: for (int i = entities.length; --i >= 0;) { 274 Map<String, Object> entity = (Map<String, Object>) entities[i]; 275 int[] chainList = (int[]) entity.get("chainIndexList"); 276 for (int k = chainList.length; --k >= 0;) 277 if (chainList[k] == iChain) { 278 hetName = "a component of the entity \"" + entity.get("description") + "\""; 279 break out; 280 } 281 } 282 addHetero(group3, hetName, false, true); 283 } 284 } 285 String[] elementList = (String[]) g.get("elementList"); 286 boolean haveAtom = false; 287 for (int ia = 0, pt = 0; ia < len; ia++, iatom++) { 288 Atom a = new Atom(); 289 a.isHetero = isHetero; 290 if (insCode != 0) 291 a.insertionCode = insCode; 292 setAtomCoordXYZ(a, x[iatom], y[iatom], z[iatom]); 293 a.elementSymbol = elementList[pt]; 294 a.atomName = atomNameList[pt++]; 295 if (seqNo >= 0) 296 maxSerial = Math.max(maxSerial, a.sequenceNumber = seqNo); 297 a.group3 = group3; 298 setChainID(a, chainID); 299 if (bf != null) 300 a.bfactor = bf[iatom]; 301 if (altloc != null) 302 a.altLoc = altloc[iatom]; 303 if (occ != null) 304 a.foccupancy = occ[iatom]; 305 if (haveSerial) 306 a.atomSerial = atomId[iatom]; 307 if (!filterAtom(a, -1) || !processSubclassAtom(a, labelAsym, authAsym)) 308 continue; 309 310 if (!haveAtom) { 311 thisGroup++; 312 haveAtom = true; 313 } 314 if (haveSerial) { 315 asc.addAtomWithMappedSerialNumber(a); 316 } else { 317 asc.addAtom(a); 318 } 319 atomMap[iatom] = a; 320 atomGroup[ac] = j; 321 groupMap[j] = lastGroup = thisGroup; 322 ac++; 323 } 324 if (!isCourseGrained) { 325 int[] bo = (int[]) g.get("bondOrderList"); 326 int[] bi = (int[]) g.get("bondAtomList"); 327 addMMTFBonds(bo, bi, a0, doMulti, false); 328 } 329 } 330 asc.setCurrentModelInfo("pdbID", pdbID); 331 } 332 addMMTFBonds(int[] bo, int[] bi, int a0, boolean doMulti, boolean isInter)333 private void addMMTFBonds(int[] bo, int[] bi, int a0, boolean doMulti, boolean isInter) { 334 if (bi == null) 335 return; 336 doMulti &= (bo != null); 337 for (int bj = 0, pt = 0, nj = bi.length / 2; bj < nj; bj++) { 338 Atom a1 = atomMap[bi[pt++] + a0]; 339 Atom a2 = atomMap[bi[pt++] + a0]; 340 if (a1 != null && a2 != null) { 341 Bond bond = new Bond(a1.index, a2.index, doMulti ? bo[bj] : 1); 342 asc.addBond(bond); 343 if (Logger.debugging && isInter) { 344 Logger.info("inter-group (" + (a1.atomSetIndex + 1) + "." + a1.index + "/" + (a2.atomSetIndex + 1) + "." + a2.index + ") bond " + a1.group3 + a1.sequenceNumber + "." + a1.atomName 345 + " - " + a2.group3 + a2.sequenceNumber + "." + a2.atomName + " " 346 + bond.order); 347 } 348 } 349 } 350 } 351 setMMTFSymmetry()352 private void setMMTFSymmetry() { 353 setSpaceGroupName((String) map.get("spaceGroup")); 354 float[] o = (float[]) map.get("unitCell"); 355 if (o != null) 356 for (int i = 0; i < 6; i++) 357 setUnitCellItem(i, o[i]); 358 } 359 360 @SuppressWarnings("unchecked") getMMTFBioAssembly()361 private void getMMTFBioAssembly() { 362 Object[] o = (Object[]) map.get("bioAssemblyList"); 363 if (o == null) 364 return; 365 if (vBiomolecules == null) 366 vBiomolecules = new Lst<Map<String, Object>>(); 367 368 for (int i = o.length; --i >= 0;) { 369 Map<String, Object> info = new Hashtable<String, Object>(); 370 vBiomolecules.addLast(info); 371 int iMolecule = i + 1; 372 checkFilterAssembly("" + iMolecule, info); 373 info.put("name", "biomolecule " + iMolecule); 374 info.put("molecule", Integer.valueOf(iMolecule)); 375 Lst<String> assemb = new Lst<String>(); 376 Lst<String> ops = new Lst<String>(); 377 info.put("biomts", new Lst<M4>()); 378 info.put("chains", new Lst<String>()); 379 info.put("assemblies", assemb); 380 info.put("operators", ops); 381 // need to add NCS here. 382 Map<String, Object> m = (Map<String, Object>) o[i]; 383 Object[] tlist = (Object[]) m.get("transformList"); 384 SB chlist = new SB(); 385 386 for (int j = 0, n = tlist.length; j < n; j++) { 387 388 Map<String, Object> t = (Map<String, Object>) tlist[j]; 389 390 // for every transformation... 391 392 chlist.setLength(0); 393 394 // for compatibility with the mmCIF reader, we create 395 // string lists of the chains in the form $A$B$C... 396 397 int[] chainList = (int[]) t.get("chainIndexList"); 398 for (int k = 0, kn = chainList.length; k < kn; k++) 399 chlist.append("$").append(labelAsymList[chainList[k]]); 400 assemb.addLast(chlist.append("$").toString()); 401 402 // now save the 4x4 matrix transform for this operation 403 404 String id = "" + (++opCount); 405 addMatrix(id, M4.newA16((float[]) t.get("matrix")), false); 406 ops.addLast(id); 407 } 408 } 409 } 410 411 // Code Name 412 // 0* pi helix 413 // 1 bend (ignored) 414 // 2* alpha helix 415 // 3* extended (sheet) 416 // 4* 3-10 helix 417 // 5 bridge (ignored) 418 // 6* turn 419 // 7 coil (ignored) 420 // -1 undefined 421 422 // 1F88: "secStructList": [7713371173311776617777666177444172222222222222222222222222222222166771222222222222222222262222222222617662222222222222222222222222222222222177111777722222222222222222221222261173333666633337717776666222222222226622222222222226611771777 423 // DSSP (Jmol): ...EE....EE....TT.....TTT...GGG..HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.TT...HHHHHHHHHHHHHHHHHHHTHHHHHHHHHHT..TTHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH..........HHHHHHHHHHHHHHHHHHH.HHHHT...EEEETTTTEEEE......TTTTHHHHHHHHHHHTTHHHHHHHHHHHHHTT........ 424 425 /** 426 * Get and translate the DSSP string from digit format 427 * 428 * input data 429 */ getStructure()430 private void getStructure() { 431 int[] a = (int[]) decode("secStructList"); 432 if (Logger.debugging) 433 Logger.info(PT.toJSON("secStructList", a)); 434 bsStructures = new BS[] { new BS(), null, new BS(), new BS(), 435 new BS(), null, new BS() }; 436 int lastGroup = -1; 437 for (int j = 0; j < a.length; j++) { 438 int type = a[j]; 439 switch (type) { 440 case 0: // PI 441 case 2: // alpha 442 case 3: // sheet 443 case 4: // 3-10 444 case 6: // turn 445 int igroup = groupMap[j]; 446 bsStructures[type].set(igroup); 447 groupDSSP[igroup] = type + 1; 448 lastGroup = j; 449 } 450 } 451 452 int n = (isDSSP1 ? asc.iSet : groupModels[lastGroup]); 453 if (lastGroup >= 0) { 454 // a single structure takes care of everything 455 haveStructure = true; 456 asc.addStructure(new Structure(n, null, null, null, 0, 0, bsStructures)); 457 } 458 } 459 460 /** 461 * We must add groups to the proper bsStructure element 462 * 463 */ addStructureSymmetry()464 private void addStructureSymmetry() { 465 if (asc.ac == 0) 466 return; 467 Atom[] atoms = asc.atoms; 468 BS bsAtoms = asc.bsAtoms; 469 470 // must point to groups here. 471 472 int ptGroup = lastGroup; 473 int mygroup = -1; 474 for (int i = ac0, n = asc.ac; i < n; i++) { 475 if (bsAtoms == null || bsAtoms.get(i)) { 476 Atom a = atoms[i]; 477 int igroup = atomGroup[a.atomSite]; 478 if (igroup != mygroup) { 479 mygroup = igroup; 480 ptGroup++; 481 } 482 int dssp = groupDSSP[igroup]; 483 if (dssp > 0) { 484 bsStructures[dssp - 1].set(ptGroup); 485 } 486 } 487 } 488 } 489 490 decode(String key)491 private Object decode(String key) { 492 return MessagePackReader.decode((byte[]) map.get(key)); 493 } 494 } 495