org.jmol.adapter.readers.cif.MMTFReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmol Show documentation
Show all versions of jmol Show documentation
Jmol: an open-source Java viewer for chemical structures in 3D
/* $RCSfile$
* $Author: hansonr $
* $Date: 2006-10-15 17:34:01 -0500 (Sun, 15 Oct 2006) $
* $Revision: 5957 $
*
* Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org
*
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jmol.adapter.readers.cif;
import java.util.Hashtable;
import java.util.Map;
import javajs.util.Lst;
import javajs.util.M4;
import javajs.util.PT;
import javajs.util.SB;
import org.jmol.adapter.smarter.Atom;
import org.jmol.adapter.smarter.Bond;
import org.jmol.adapter.smarter.Structure;
import org.jmol.java.BS;
import org.jmol.script.SV;
import org.jmol.util.Logger;
/**
* JmolData RCSB MMTF (macromolecular transmission format) file reader
*
* see https://github.com/rcsb/mmtf/blob/master/spec.md
*
* full specification Version: v0.2+dev (as of 2016.08.08) is
* implemented,including:
*
* reading atoms, bonds, and DSSP 1.0 secondary structure
*
* load =1f88.mmtf filter "DSSP1"
*
* [Note that the filter "DSSP1" is required, since mmtf included DSSP 1.0
* calculations, while the standard for Jmol itself is DSSP 2.0. These two
* calculations differ in their treating of helix kinks as one (1.0) or two
* (2.0) helices.]
*
*
* reading space groups and unit cells, and using those as per other readers
*
* load =1crn.mmtf {1 1 1}
*
* reading bioassemblies (biomolecules) and applying all symmetry
* transformations
*
* load =1auy.mmtf FILTER "biomolecule 1;*.CA,*.P"
*
* reading both biomolecules and lattices, and loading course-grained using the
* filter "BYCHAIN" or "BYSYMOP"
*
* load =1auy.mmtf {2 2 1} filter "biomolecule 1;bychain";spacefill 30.0; color
* property symop
*
* Many thanks to the MMTF team at RCSB for assistance in this implementation.
*
* @author Bob Hanson [email protected]
*
*/
public class MMTFReader extends MMCifReader {
private boolean haveStructure;
@Override
protected void addHeader() {
// no header for this type
}
/**
* standard set up
*
* @param fullPath
* @param htParams
* @param reader
*/
@Override
protected void setup(String fullPath, Map htParams,
Object reader) {
isBinary = true;
isMMCIF = true;
iHaveFractionalCoordinates = false;
setupASCR(fullPath, htParams, reader);
}
@Override
protected void processBinaryDocument() throws Exception {
// load xxx.mmtf filter "..." options
// NODOUBLE -- standard PDB-like structures
// DSSP2 -- use Jmol's built-in DSSP 2.0, not file's DSSP 1.0
boolean doDoubleBonds = (!isCourseGrained && !checkFilterKey("NODOUBLE"));
isDSSP1 = !checkFilterKey("DSSP2");
boolean mmtfImplementsDSSP2 = false; // so far!
applySymmetryToBonds = true;
map = (new MessagePackReader(binaryDoc, true)).readMap();
entities = (Object[]) map.get("entityList");
if (Logger.debugging) {
for (String s : map.keySet())
Logger.info(s);
}
asc.setInfo("noAutoBond", Boolean.TRUE);
Logger.info("MMTF version " + map.get("mmtfVersion"));
Logger.info("MMTF Producer " + map.get("mmtfProducer"));
String title = (String) map.get("title");
if (title != null)
appendLoadNote(title);
String id = (String) map.get("structureId");
if (id == null)
id = "?";
fileAtomCount = ((Integer) map.get("numAtoms")).intValue();
int nBonds = ((Integer) map.get("numBonds")).intValue();
groupCount = ((Integer) map.get("numGroups")).intValue();
groupModels = new int[groupCount]; // group model (file-based)
groupDSSP = new int[groupCount]; // group structure type (Jmol-based)
groupMap = new int[groupCount]; // file->jmol group index map
int modelCount = ((Integer) map.get("numModels")).intValue();
appendLoadNote("id=" + id + " numAtoms=" + fileAtomCount + " numBonds="
+ nBonds + " numGroups=" + groupCount + " numModels=" + modelCount);
getMMTFAtoms(doDoubleBonds);
if (!isCourseGrained) {
int[] bo = (int[]) decode("bondOrderList");
int[] bi = (int[]) decode("bondAtomList");
addMMTFBonds(bo, bi, 0, doDoubleBonds, true);
if (isDSSP1 || mmtfImplementsDSSP2)
getStructure();
}
setMMTFSymmetry();
getMMTFBioAssembly();
setModelPDB(true);
if (Logger.debuggingHigh)
Logger.info(SV.getVariable(map).asString());
}
@Override
public void applySymmetryAndSetTrajectory() throws Exception {
ac0 = ac;
super.applySymmetryAndSetTrajectory();
if (haveStructure)
addStructureSymmetry();
}
//////////////////////////////// MMTF-Specific /////////////////////////
private Map map; // input JSON-like map from MessagePack binary file
private int fileAtomCount;
private int opCount = 0;
private int[] groupModels, groupMap, groupDSSP, atomGroup;
private String[] labelAsymList; // created in getAtoms; used in getBioAssembly
private Atom[] atomMap; // necessary because some atoms may be deleted.
private Object[] entities;
private int groupCount;
private int ac0;
private BS[] bsStructures;
private int lastGroup;
// TODO - also consider mapping group indices
/**
* set up all atoms, including bonding, within a group
*
* @param doMulti
* true to add double bonds
*
* @throws Exception
*/
@SuppressWarnings("unchecked")
private void getMMTFAtoms(boolean doMulti) throws Exception {
// chains
int[] chainsPerModel = (int[]) map.get("chainsPerModel");
int[] groupsPerChain = (int[]) map.get("groupsPerChain"); // note that this is label_asym, not auth_asym
labelAsymList = (String[]) decode("chainIdList"); // label_asym
String[] authAsymList = (String[]) decode("chainNameList"); // Auth_asym
// groups
int[] groupTypeList = (int[]) decode("groupTypeList");
int[] groupIdList = (int[]) decode("groupIdList");
Object[] groupList = (Object[]) map.get("groupList");
char[] insCodes = (char[]) decode("insCodeList");
int[] atomId = (int[]) decode("atomIdList");
boolean haveSerial = (atomId != null);
char[] altloc = (char[]) decode("altLocList"); // rldecode32
float[] occ = (float[]) decode("occupancyList");
float[] x = (float[]) decode("xCoordList");//getFloatsSplit("xCoord", 1000f);
float[] y = (float[]) decode("yCoordList");
float[] z = (float[]) decode("zCoordList");
float[] bf = (float[]) decode("bFactorList");
String[] nameList = (useAuthorChainID ? authAsymList : labelAsymList);
int iModel = -1;
int iChain = 0;
int nChain = 0;
int iGroup = 0;
int nGroup = 0;
int chainpt = 0;
int seqNo = 0;
int iatom = 0;
String chainID = "";
String authAsym = "", labelAsym = "";
char insCode = '\0';
atomMap = new Atom[fileAtomCount];
atomGroup = new int[fileAtomCount];
for (int j = 0, thisGroup = -1; j < groupCount; j++) {
if (++iGroup >= nGroup) {
chainID = nameList[chainpt];
authAsym = authAsymList[chainpt];
labelAsym = labelAsymList[chainpt];
nGroup = groupsPerChain[chainpt++];
iGroup = 0;
if (++iChain >= nChain) {
groupModels[j] = ++iModel;
nChain = chainsPerModel[iModel];
iChain = 0;
setModelPDB(true);
incrementModel(iModel + 1);
nAtoms0 = asc.ac;
if (done)
return;
}
}
Map g = (Map) groupList[groupTypeList[j]];
String[] atomNameList = (String[]) g.get("atomNameList");
int len = atomNameList.length;
if (skipping) {
iatom += len;
continue;
}
int a0 = iatom;
if (insCodes != null)
insCode = insCodes[j];
seqNo = groupIdList[j];
String group3 = (String) g.get("groupName");
boolean isHetero = vwr.getJBR().isHetero(group3);
if (isHetero) {
// looking for CHEM_COMP_NAME here... "IRON/SULFUR CLUSTER" for SF4 in 1blu
String hetName = "" + g.get("chemCompType");
if (htHetero == null || !htHetero.containsKey(group3)) {
// this is not ideal -- see 1a37.mmtf, where PSE is LSQRQRST(PSE)TPNVHM
// (actually that should be phosphoserine, SPE...)
// It is still listed as NON-POLYMER even though it is just a modified AA.
if (entities != null && hetName.equals("NON-POLYMER"))
out: for (int i = entities.length; --i >= 0;) {
Map entity = (Map) entities[i];
int[] chainList = (int[]) entity.get("chainIndexList");
for (int k = chainList.length; --k >= 0;)
if (chainList[k] == iChain) {
hetName = "a component of the entity \"" + entity.get("description") + "\"";
break out;
}
}
addHetero(group3, hetName, false, true);
}
}
String[] elementList = (String[]) g.get("elementList");
boolean haveAtom = false;
for (int ia = 0, pt = 0; ia < len; ia++, iatom++) {
Atom a = new Atom();
a.isHetero = isHetero;
if (insCode != 0)
a.insertionCode = insCode;
setAtomCoordXYZ(a, x[iatom], y[iatom], z[iatom]);
a.elementSymbol = elementList[pt];
a.atomName = atomNameList[pt++];
if (seqNo >= 0)
maxSerial = Math.max(maxSerial, a.sequenceNumber = seqNo);
a.group3 = group3;
setChainID(a, chainID);
if (bf != null)
a.bfactor = bf[iatom];
if (altloc != null)
a.altLoc = altloc[iatom];
if (occ != null)
a.foccupancy = occ[iatom];
if (haveSerial)
a.atomSerial = atomId[iatom];
if (!filterAtom(a, -1) || !processSubclassAtom(a, labelAsym, authAsym))
continue;
if (!haveAtom) {
thisGroup++;
haveAtom = true;
}
if (haveSerial) {
asc.addAtomWithMappedSerialNumber(a);
} else {
asc.addAtom(a);
}
atomMap[iatom] = a;
atomGroup[ac] = j;
groupMap[j] = lastGroup = thisGroup;
ac++;
}
if (!isCourseGrained) {
int[] bo = (int[]) g.get("bondOrderList");
int[] bi = (int[]) g.get("bondAtomList");
addMMTFBonds(bo, bi, a0, doMulti, false);
}
}
}
private void addMMTFBonds(int[] bo, int[] bi, int a0, boolean doMulti, boolean isInter) {
if (bi == null)
return;
doMulti &= (bo != null);
for (int bj = 0, pt = 0, nj = bi.length / 2; bj < nj; bj++) {
Atom a1 = atomMap[bi[pt++] + a0];
Atom a2 = atomMap[bi[pt++] + a0];
if (a1 != null && a2 != null) {
Bond bond = new Bond(a1.index, a2.index, doMulti ? bo[bj] : 1);
asc.addBond(bond);
if (Logger.debugging && isInter) {
Logger.info("inter-group (" + (a1.atomSetIndex + 1) + "." + a1.index + "/" + (a2.atomSetIndex + 1) + "." + a2.index + ") bond " + a1.group3 + a1.sequenceNumber + "." + a1.atomName
+ " - " + a2.group3 + a2.sequenceNumber + "." + a2.atomName + " "
+ bond.order);
}
}
}
}
private void setMMTFSymmetry() {
setSpaceGroupName((String) map.get("spaceGroup"));
float[] o = (float[]) map.get("unitCell");
if (o != null)
for (int i = 0; i < 6; i++)
setUnitCellItem(i, o[i]);
}
@SuppressWarnings("unchecked")
private void getMMTFBioAssembly() {
Object[] o = (Object[]) map.get("bioAssemblyList");
if (o == null)
return;
if (vBiomolecules == null)
vBiomolecules = new Lst