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

org.jmol.adapter.readers.cif.CifReader Maven / Gradle / Ivy

There is a newer version: 14.31.10
Show newest version
/* $RCSfile$
 * $Author: hansonr $
 * $Date: 2006-10-20 07:48:25 -0500 (Fri, 20 Oct 2006) $
 * $Revision: 5991 $
 *
 * 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 org.jmol.adapter.smarter.AtomSetCollection;
import org.jmol.adapter.smarter.Atom;
import org.jmol.adapter.smarter.AtomSetCollectionReader;
import org.jmol.api.JmolAdapter;
import org.jmol.api.SymmetryInterface;
import org.jmol.java.BS;

import org.jmol.script.T;

import javajs.api.GenericCifDataParser;
import javajs.util.Lst;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;

import org.jmol.util.Logger;
import org.jmol.util.Vibration;

import javajs.util.Rdr;
import javajs.util.CifDataParser;
import javajs.util.P3;
import javajs.util.PT;
import javajs.util.V3;

/**
 * A true line-free CIF file reader for CIF files.
 * 
 * Subclasses of CIF -- mmCIF/PDBx (pre-initialized) and msCIF (initialized
 * here)
 *
 * Note that a file can be a PDB file without being
 * 
 * Added nonstandard mCIF (magnetic_ tags) 5/2/2014 note that PRELIM keys can be
 * removed at some later time
 * 
 * 

* * http://www.iucr.org/iucr-top/cif/ * * * http://www.iucr.org/iucr-top/cif/standard/cifstd5.html * * @author Miguel, Egon, and Bob ([email protected]) * * symmetry added by Bob Hanson: * * setSpaceGroupName() setSymmetryOperator() setUnitCellItem() * setFractionalCoordinates() setAtomCoord() * applySymmetryAndSetTrajectory() * */ public class CifReader extends AtomSetCollectionReader { private static final String titleRecords = "__citation_title__publ_section_title__active_magnetic_irreps_details__"; private MSCifRdr mr; // no need for reflection here -- the CIF reader is already // protected by reflection GenericCifDataParser parser; private boolean filterAssembly; private boolean allowRotations = true; private boolean readIdeal = true; private int configurationPtr = Integer.MIN_VALUE; protected boolean useAuthorChainID = true; protected String thisDataSetName = "", lastDataSetName; private String chemicalName = ""; private String thisStructuralFormula = ""; private String thisFormula = ""; protected boolean iHaveDesiredModel; protected boolean isMMCIF; protected boolean isLigand; boolean isMagCIF; boolean haveHAtoms; private String molecularType = "GEOM_BOND default"; private char lastAltLoc = '\0'; private boolean haveAromatic; private int conformationIndex; private int nMolecular = 0; private String appendedData; protected boolean skipping; protected int nAtoms; protected int ac; private String auditBlockCode; private String lastSpaceGroupName; private boolean modulated; protected boolean isCourseGrained; boolean haveCellWaveVector; private String latticeType = null; private int modDim; protected Map htGroup1; protected int nAtoms0; private int titleAtomSet = 1; @Override public void initializeReader() throws Exception { initSubclass(); allowPDBFilter = true; appendedData = (String) htParams.get("appendedData"); String conf = getFilter("CONF "); if (conf != null) configurationPtr = parseIntStr(conf); isMolecular = checkFilterKey("MOLECUL") && !checkFilterKey("BIOMOLECULE"); // molecular; molecule readIdeal = !checkFilterKey("NOIDEAL"); filterAssembly = checkFilterKey("$"); useAuthorChainID = !checkFilterKey("NOAUTHORCHAINS"); if (isMolecular) { forceSymmetry(false); molecularType = "filter \"MOLECULAR\""; } asc.checkSpecial = !checkFilterKey("NOSPECIAL"); allowRotations = !checkFilterKey("NOSYM"); if (strSupercell != null && strSupercell.indexOf(",") >= 0) addCellType("conventional", strSupercell, true); if (binaryDoc != null) return; // mmtf readCifData(); continuing = false; } protected void initSubclass() { // for MMCifReader } private void readCifData() throws Exception { /* * Modified for 10.9.64 9/23/06 by Bob Hanson to remove as much as possible * of line dependence. a loop could now go: * * blah blah blah loop_ _a _b _c 0 1 2 0 3 4 0 5 6 loop_...... * * we don't actually check that any skipped loop has the proper number of * data points --- some multiple of the number of data keys -- but other * than that, we are checking here for proper CIF syntax, and Jmol will * report if it finds data where a key is supposed to be. */ parser = new CifDataParser().set(this, null); line = ""; while ((key = parser.peekToken()) != null) if (!readAllData()) break; if (appendedData != null) { parser = ((GenericCifDataParser) getInterface("javajs.util.CifDataParser")) .set(null, Rdr.getBR(appendedData)); while ((key = parser.peekToken()) != null) if (!readAllData()) break; } } private boolean readAllData() throws Exception { if (key.startsWith("data_")) { isLigand = false; if (asc.atomSetCount == 0) iHaveDesiredModel = false; if (iHaveDesiredModel) return false; if (desiredModelNumber != Integer.MIN_VALUE) appendLoadNote(null); newModel(++modelNumber); haveCellWaveVector = false; if (auditBlockCode == null) modulated = false; if (!skipping) { nAtoms0 = asc.ac; processDataParameter(); nAtoms = asc.ac; } return true; } if (skipping && key.equals("_audit_block_code")) { iHaveDesiredModel = false; skipping = false; } isLoop = key.startsWith("loop_"); if (isLoop) { if (skipping && !isMMCIF) { parser.getTokenPeeked(); parser.skipLoop(false); } else { processLoopBlock(); } return true; } // global_ and stop_ are reserved STAR keywords // see http://www.iucr.org/iucr-top/lists/comcifs-l/msg00252.html // http://www.iucr.org/iucr-top/cif/spec/version1.1/cifsyntax.html#syntax // stop_ is not allowed, because nested loop_ is not allowed // global_ is a reserved STAR word; not allowed in CIF // ah, heck, let's just flag them as CIF ERRORS /* * if (key.startsWith("global_") || key.startsWith("stop_")) { * tokenizer.getTokenPeeked(); continue; } */ if (key.indexOf("_") != 0) { Logger.warn("CIF ERROR ? should be an underscore: " + key); parser.getTokenPeeked(); } else if (!getData()) { return true; } if (!skipping) { key = parser.fixKey(key0 = key); if (key.startsWith("_chemical_name") || key.equals("_chem_comp_name")) { processChemicalInfo("name"); } else if (key.startsWith("_chemical_formula_structural")) { processChemicalInfo("structuralFormula"); } else if (key.startsWith("_chemical_formula_sum") || key.equals("_chem_comp_formula")) { processChemicalInfo("formula"); } else if (key.equals("_cell_modulation_dimension")) { modDim = parseIntStr(data); } else if (key.startsWith("_cell_") && key.indexOf("_commen_") < 0) { processCellParameter(); } else if (key.startsWith("_atom_sites_fract_tran")) { processUnitCellTransformMatrix(); } else if (key.startsWith("_audit")) { if (key.equals("_audit_block_code")) { auditBlockCode = parser.fullTrim(data).toUpperCase(); appendLoadNote(auditBlockCode); if (htAudit != null && auditBlockCode.contains("_MOD_")) { String key = PT.rep(auditBlockCode, "_MOD_", "_REFRNCE_"); if (asc.setSymmetry((SymmetryInterface) htAudit.get(key)) != null) { unitCellParams = asc.getSymmetry().getUnitCellParams(); iHaveUnitCell = true; } } else if (htAudit != null) { if (symops != null) for (int i = 0; i < symops.size(); i++) setSymmetryOperator(symops.get(i)); } if (lastSpaceGroupName != null) setSpaceGroupName(lastSpaceGroupName); } else if (key.equals("_audit_creation_date")) { symmetry = null; } } else if (key.equals(singleAtomID)) { readSingleAtom(); } else if (key.startsWith("_symmetry_space_group_name_h-m") || key.startsWith("_symmetry_space_group_name_hall") || key.startsWith("_space_group_name") || key.contains("_ssg_name") || key.contains("_magn_name") || key.contains("_bns_name") // PRELIM ) { processSymmetrySpaceGroupName(); } else if (key.startsWith("_space_group_transform")) { processUnitCellTransform(); } else if (key.contains("_database_code")) { addModelTitle("ID"); } else if (titleRecords.contains("_" + key + "__")) { addModelTitle("TITLE"); } else { processSubclassEntry(); } } return true; } private void addModelTitle(String key) { if (asc.atomSetCount > titleAtomSet) appendLoadNote("\nMODEL: " + (titleAtomSet = asc.atomSetCount)); appendLoadNote(key + ": " + parser.fullTrim(data)); } protected void processSubclassEntry() throws Exception { if (modDim > 0) getModulationReader().processEntry(); } private void processUnitCellTransform() { data = PT.replaceAllCharacters(data, " ", ""); if (key.contains("_from_parent")) addCellType("parent", data, true); else if (key.contains("_to_standard")) addCellType("standard", data, false); appendLoadNote(key + ": " + data); } private Map htCellTypes; private void addCellType(String type, String data, boolean isFrom) { if (htCellTypes == null) htCellTypes = new Hashtable(); if (data.startsWith("!")) { data = data.substring(1); isFrom = !isFrom; } String cell = (isFrom ? "!" : "") + data; htCellTypes.put(type, cell); if (type.equalsIgnoreCase(strSupercell)) { strSupercell = cell; htCellTypes.put("conventional", (isFrom ? "" : "!") + data); } } /** * No need for anything other than the atom name and symbol; coordinates will * be (0 0 0), and no other information is needed. */ private void readSingleAtom() { Atom atom = new Atom(); atom.set(0, 0, 0); atom.atomName = parser.fullTrim(data); atom.getElementSymbol(); asc.addAtom(atom); } private MSCifRdr getModulationReader() throws Exception { return (mr == null ? initializeMSCIF() : mr); } private MSCifRdr initializeMSCIF() throws Exception { if (mr == null) ms = mr = (MSCifRdr) getInterface("org.jmol.adapter.readers.cif.MSCifRdr"); modulated = (mr.initialize(this, modDim) > 0); return mr; } public Map modelMap; protected void newModel(int modelNo) throws Exception { skipping = !doGetModel(modelNumber = modelNo, null); if (skipping) { if (!isMMCIF) parser.getTokenPeeked(); return; } chemicalName = ""; thisStructuralFormula = ""; thisFormula = ""; iHaveDesiredModel = isLastModel(modelNumber); if (isCourseGrained) asc.setCurrentModelInfo("courseGrained", Boolean.TRUE); if (nAtoms0 == asc.ac) { // we found no atoms -- must revert modelNumber--; haveModel = false; asc.removeCurrentAtomSet(); } else { applySymmetryAndSetTrajectory(); } } @Override protected void finalizeSubclassReader() throws Exception { // added check for final data_global if (asc.iSet > 0 && asc.getAtomSetAtomCount(asc.iSet) == 0) asc.atomSetCount--; else if (!isMMCIF || !finalizeSubclass()) applySymmetryAndSetTrajectory(); int n = asc.atomSetCount; if (n > 1) asc.setCollectionName(""); finalizeReaderASCR(); addHeader(); if (haveAromatic) addJmolScript("calculate aromatic"); } protected void addHeader() { String header = parser.getFileHeader(); if (header.length() > 0) { String s = setLoadNote(); appendLoadNote(null); appendLoadNote(header); appendLoadNote(s); setLoadNote(); asc.setInfo("fileHeader", header); } } protected boolean finalizeSubclass() throws Exception { // MMCifReader only return false; } @Override public void doPreSymmetry() throws Exception { if (magCenterings != null) addLatticeVectors(); if (modDim > 0) getModulationReader().setModulation(false, null); if (isMagCIF) { asc.getXSymmetry().scaleFractionalVibs(); vibsFractional = true; //if (isMagCIF) { // set fractional; note that this will be set to Cartesians later } } @Override public void applySymmetryAndSetTrajectory() throws Exception { // This speeds up calculation, because no crosschecking // No special-position atoms in mmCIF files, because there will // be no center of symmetry, no rotation-inversions, // no atom-centered rotation axes, and no mirror or glide planes. if (isMMCIF) asc.checkSpecial = false; boolean doCheckBonding = doCheckUnitCell && !isMMCIF; if (isMMCIF) { int modelIndex = asc.iSet; asc.setCurrentModelInfo( "PDB_CONECT_firstAtom_count_max", new int[] { asc.getAtomSetAtomIndex(modelIndex), asc.getAtomSetAtomCount(modelIndex), maxSerial }); } if (htCellTypes != null) { for (Entry e : htCellTypes.entrySet()) asc.setCurrentModelInfo("unitcell_" + e.getKey(), e.getValue()); htCellTypes = null; } if (!haveCellWaveVector) modDim = 0; if (doApplySymmetry && !iHaveFractionalCoordinates) fractionalizeCoordinates(true); applySymTrajASCR(); if (doCheckBonding && (bondTypes.size() > 0 || isMolecular)) setBondingAndMolecules(); asc.setCurrentModelInfo("fileHasUnitCell", Boolean.TRUE); asc.xtalSymmetry = null; } @Override protected void finalizeSubclassSymmetry(boolean haveSymmetry) throws Exception { // called by applySymTrajASCR // just for modulated, audit block, and magnetic structures SymmetryInterface sym = (haveSymmetry ? asc.getXSymmetry() .getBaseSymmetry() : null); if (modDim > 0 && sym != null) { addLatticeVectors(); asc.setTensors(); getModulationReader().setModulation(true, sym); mr.finalizeModulation(); } if (isMagCIF) { asc.setNoAutoBond(); if (sym != null) { addJmolScript("vectors on;vectors 0.15;"); int n = asc.getXSymmetry().setSpinVectors(); appendLoadNote(n + " magnetic moments - use VECTORS ON/OFF or VECTOR MAX x.x or SELECT VXYZ>0"); } } if (sym != null && auditBlockCode != null && auditBlockCode.contains("REFRNCE")) { if (htAudit == null) htAudit = new Hashtable(); htAudit.put(auditBlockCode, sym); } } //////////////////////////////////////////////////////////////// // processing methods //////////////////////////////////////////////////////////////// private Hashtable htAudit; private Lst symops; /** * initialize a new atom set * */ private void processDataParameter() { bondTypes.clear(); parser.getTokenPeeked(); thisDataSetName = (key.length() < 6 ? "" : key.substring(5)); if (thisDataSetName.length() > 0) nextAtomSet(); if (debugging) Logger.debug(key); } protected void nextAtomSet() { asc.setCurrentModelInfo("isCIF", Boolean.TRUE); if (asc.iSet >= 0) { // note that there can be problems with multi-data mmCIF sets each with // multiple models; and we could be loading multiple files! if (isMMCIF) setModelPDB(true); // first model can be missed asc.newAtomSet(); if (isMMCIF) setModelPDB(true); } else { asc.setCollectionName(thisDataSetName); } } /** * reads some of the more interesting info into specific atomSetAuxiliaryInfo * elements * * @param type * "name" "formula" etc. * @return data * @throws Exception */ private String processChemicalInfo(String type) throws Exception { if (type.equals("name")) { chemicalName = data = parser.fullTrim(data); appendLoadNote(chemicalName); if (!data.equals("?")) asc.setInfo("modelLoadNote", data); } else if (type.equals("structuralFormula")) { thisStructuralFormula = data = parser.fullTrim(data); } else if (type.equals("formula")) { thisFormula = data = parser.fullTrim(data); if (thisFormula.length() > 1) appendLoadNote(thisFormula); } if (debugging) { Logger.debug(type + " = " + data); } return data; } // _space_group.magn_ssg_name_BNS "P2_1cn1'(0,0,g)000s" // _space_group.magn_ssg_number_BNS 33.1.9.5.m145.? // _space_group.magn_point_group "mm21'" /** * done by AtomSetCollectionReader * * @throws Exception */ private void processSymmetrySpaceGroupName() throws Exception { if (key.indexOf("_ssg_name") >= 0) { modulated = true; latticeType = data.substring(0, 1); } else if (modulated) { return; } data = parser.toUnicode(data); setSpaceGroupName(lastSpaceGroupName = (key.indexOf("h-m") > 0 ? "HM:" : modulated ? "SSG:" : key.indexOf("bns") >= 0 ? "BNS:" : key .indexOf("hall") >= 0 ? "Hall:" : "") + data); } private void addLatticeVectors() { lattvecs = null; if (magCenterings != null) { // could be x+1/2,y+1/2,z,+1 // or x+0.5,y+0.5,z,+1 // or 0.5+x,0.5+y,z,+1 // latticeType = "Magnetic"; lattvecs = new Lst(); for (int i = 0; i < magCenterings.size(); i++) { String s = magCenterings.get(i); float[] f = new float[modDim + 4]; if (s.indexOf("x1") >= 0) for (int j = 1; j <= modDim + 3; j++) s = PT.rep(s, "x" + j, ""); String[] tokens = PT.split(PT.replaceAllCharacters(s, "xyz+", ""), ","); int n = 0; for (int j = 0; j < tokens.length; j++) { s = tokens[j].trim(); if (s.length() == 0) continue; if ((f[j] = PT.parseFloatFraction(s)) != 0) n++; } if (n >= 2) // needs to have an x y or z as well as a +/-1; lattvecs.addLast(f); } magCenterings = null; } else if (latticeType != null && "ABCFI".indexOf(latticeType) >= 0) { lattvecs = new Lst(); try { ms.addLatticeVector(lattvecs, latticeType); } catch (Exception e) { // n/a } } if (lattvecs != null && lattvecs.size() > 0 && asc.getSymmetry().addLatticeVectors(lattvecs)) appendLoadNote("Note! " + lattvecs.size() + " symmetry operators added for lattice centering " + latticeType); latticeType = null; } /** * unit cell parameters -- two options, so we use MOD 6 * * @throws Exception */ private void processCellParameter() throws Exception { for (int i = JmolAdapter.cellParamNames.length; --i >= 0;) if (key.equals(JmolAdapter.cellParamNames[i])) { setUnitCellItem(i, parseFloatStr(data)); return; } } final private static String[] TransformFields = { "x[1][1]", "x[1][2]", "x[1][3]", "r[1]", "x[2][1]", "x[2][2]", "x[2][3]", "r[2]", "x[3][1]", "x[3][2]", "x[3][3]", "r[3]", }; /** * * the PDB transformation matrix cartesian --> fractional * * @throws Exception */ private void processUnitCellTransformMatrix() throws Exception { /* * PDB: SCALE1 .024414 0.000000 -.000328 0.00000 SCALE2 0.000000 .053619 0.000000 0.00000 SCALE3 0.000000 0.000000 .044409 0.00000 * CIF: _atom_sites.fract_transf_matrix[1][1] .024414 _atom_sites.fract_transf_matrix[1][2] 0.000000 _atom_sites.fract_transf_matrix[1][3] -.000328 _atom_sites.fract_transf_matrix[2][1] 0.000000 _atom_sites.fract_transf_matrix[2][2] .053619 _atom_sites.fract_transf_matrix[2][3] 0.000000 _atom_sites.fract_transf_matrix[3][1] 0.000000 _atom_sites.fract_transf_matrix[3][2] 0.000000 _atom_sites.fract_transf_matrix[3][3] .044409 _atom_sites.fract_transf_vector[1] 0.00000 _atom_sites.fract_transf_vector[2] 0.00000 _atom_sites.fract_transf_vector[3] 0.00000 */ float v = parseFloatStr(data); if (Float.isNaN(v)) return; //could enable EM box: unitCellParams[0] = 1; for (int i = 0; i < TransformFields.length; i++) { if (key.indexOf(TransformFields[i]) >= 0) { setUnitCellItem(6 + i, v); return; } } } //////////////////////////////////////////////////////////////// // non-loop_ processing //////////////////////////////////////////////////////////////// String key, key0; String data; private boolean isLoop; /** * * @return TRUE if data, even if ''; FALSE if '.' or '?' or eof. * * @throws Exception */ private boolean getData() throws Exception { key = parser.getTokenPeeked(); data = parser.getNextToken(); //if (debugging && data != null && data.charAt(0) != '\0') //Logger.debug(key + " " + data); if (data == null) { Logger.warn("CIF ERROR ? end of file; data missing: " + key); return false; } return (data.length() == 0 || data.charAt(0) != '\0'); } //////////////////////////////////////////////////////////////// // loop_ processing //////////////////////////////////////////////////////////////// /** * processes loop_ blocks of interest or skips the data * * @throws Exception */ protected void processLoopBlock() throws Exception { parser.getTokenPeeked(); //loop_ key = parser.peekToken(); if (key == null) return; key = parser.fixKey(key0 = key); if (modDim > 0) switch (getModulationReader().processLoopBlock()) { case 0: break; case -1: parser.skipLoop(false); //$FALL-THROUGH$ case 1: return; } boolean isLigand = false; if (key.startsWith(FAMILY_ATOM) || (isLigand = key.equals("_chem_comp_atom_comp_id"))) { if (!processAtomSiteLoopBlock(isLigand)) return; if (thisDataSetName.equals("global")) asc.setCollectionName(thisDataSetName = chemicalName); if (!thisDataSetName.equals(lastDataSetName)) { asc.setAtomSetName(thisDataSetName); lastDataSetName = thisDataSetName; } asc.setCurrentModelInfo("chemicalName", chemicalName); asc.setCurrentModelInfo("structuralFormula", thisStructuralFormula); asc.setCurrentModelInfo("formula", thisFormula); return; } if (key.startsWith(FAMILY_SGOP) || key.startsWith("_symmetry_equiv_pos") || key.startsWith("_symmetry_ssg_equiv")) { if (ignoreFileSymmetryOperators) { Logger.warn("ignoring file-based symmetry operators"); parser.skipLoop(false); } else { processSymmetryOperationsLoopBlock(); } return; } if (key.startsWith("_citation")) { processCitationListBlock(); return; } if (key.startsWith("_atom_type")) { processAtomTypeLoopBlock(); return; } if ((isMolecular || !doApplySymmetry) && key.startsWith("_geom_bond")) { processGeomBondLoopBlock(); return; } if (processSubclassLoopBlock()) return; if (key.equals("_propagation_vector_seq_id")) {// Bilbao mCIF addMore(); return; } parser.skipLoop(false); } protected boolean processSubclassLoopBlock() throws Exception { return false; } private void addMore() { String str; int n = 0; try { while ((str = parser.peekToken()) != null && str.charAt(0) == '_') { parser.getTokenPeeked(); n++; } int m = 0; String s = ""; while ((str = parser.getNextDataToken()) != null) { s += str + (m % n == 0 ? "=" : " "); if (++m % n == 0) { appendUunitCellInfo(s.trim()); s = ""; } } } catch (Exception e) { } } protected int fieldProperty(int i) { return (i >= 0 && (field = parser.getColumnData(i)).length() > 0 && (firstChar = field.charAt(0)) != '\0' ? col2key[i] : NONE); } int[] col2key = new int[CifDataParser.KEY_MAX]; // 100 int[] key2col = new int[CifDataParser.KEY_MAX]; String field; protected char firstChar = '\0'; /** * sets up arrays and variables for tokenizer.getData() * * @param fields * @throws Exception */ void parseLoopParameters(String[] fields) throws Exception { parser.parseDataBlockParameters(fields, isLoop ? null : key0, data, key2col, col2key); } void parseLoopParametersFor(String key, String[] fields) throws Exception { // just once for static fields // first field must start with * if any do if (fields[0].charAt(0) == '*') for (int i = fields.length; --i >= 0;) if (fields[i].charAt(0) == '*') fields[i] = key + fields[i].substring(1); parseLoopParameters(fields); } /** * * used for turning off fractional or nonfractional coord. * * @param fieldIndex */ private void disableField(int fieldIndex) { int i = key2col[fieldIndex]; if (i != NONE) col2key[i] = NONE; } //////////////////////////////////////////////////////////////// // atom type data //////////////////////////////////////////////////////////////// private Map htOxStates; private Lst bondTypes = new Lst(); private String disorderAssembly = "."; private String lastDisorderAssembly; private Lst lattvecs; private Lst magCenterings; protected int maxSerial; final private static byte ATOM_TYPE_SYMBOL = 0; final private static byte ATOM_TYPE_OXIDATION_NUMBER = 1; final private static String[] atomTypeFields = { "_atom_type_symbol", "_atom_type_oxidation_number" }; /** * * reads the oxidation number and associates it with an atom name, which can * then later be associated with the right atom indirectly. * * @throws Exception */ private void processAtomTypeLoopBlock() throws Exception { parseLoopParameters(atomTypeFields); if (!checkAllFieldsPresent(atomTypeFields, -1, false)) { parser.skipLoop(false); return; } String atomTypeSymbol; float oxidationNumber = 0; while (parser.getData()) { if (isNull(atomTypeSymbol = getField(ATOM_TYPE_SYMBOL)) || Float .isNaN(oxidationNumber = parseFloatStr(getField(ATOM_TYPE_OXIDATION_NUMBER)))) continue; if (htOxStates == null) htOxStates = new Hashtable(); htOxStates.put(atomTypeSymbol, Float.valueOf(oxidationNumber)); } } //////////////////////////////////////////////////////////////// // atom site data //////////////////////////////////////////////////////////////// protected final static byte NONE = -1; final private static byte TYPE_SYMBOL = 0; final private static byte LABEL = 1; final private static byte AUTH_ATOM = 2; final private static byte FRACT_X = 3; final private static byte FRACT_Y = 4; final private static byte FRACT_Z = 5; final private static byte CARTN_X = 6; final private static byte CARTN_Y = 7; final private static byte CARTN_Z = 8; final private static byte OCCUPANCY = 9; final private static byte B_ISO = 10; final private static byte COMP_ID = 11; final private static byte AUTH_ASYM_ID = 12; final private static byte AUTH_SEQ_ID = 13; final private static byte INS_CODE = 14; final private static byte ALT_ID = 15; final private static byte GROUP_PDB = 16; final private static byte MODEL_NO = 17; final private static byte DUMMY_ATOM = 18; final private static byte DISORDER_GROUP = 19; final private static byte ANISO_LABEL = 20; final private static byte ANISO_MMCIF_ID = 21; final private static byte ANISO_U11 = 22; final private static byte ANISO_U22 = 23; final private static byte ANISO_U33 = 24; final private static byte ANISO_U12 = 25; final private static byte ANISO_U13 = 26; final private static byte ANISO_U23 = 27; final private static byte ANISO_MMCIF_U11 = 28; final private static byte ANISO_MMCIF_U22 = 29; final private static byte ANISO_MMCIF_U33 = 30; final private static byte ANISO_MMCIF_U12 = 31; final private static byte ANISO_MMCIF_U13 = 32; final private static byte ANISO_MMCIF_U23 = 33; final private static byte U_ISO_OR_EQUIV = 34; final private static byte ANISO_B11 = 35; final private static byte ANISO_B22 = 36; final private static byte ANISO_B33 = 37; final private static byte ANISO_B12 = 38; final private static byte ANISO_B13 = 39; final private static byte ANISO_B23 = 40; final private static byte ANISO_BETA_11 = 41; final private static byte ANISO_BETA_22 = 42; final private static byte ANISO_BETA_33 = 43; final private static byte ANISO_BETA_12 = 44; final private static byte ANISO_BETA_13 = 45; final private static byte ANISO_BETA_23 = 46; final private static byte ADP_TYPE = 47; final private static byte CC_COMP_ID = 48; final private static byte CC_ATOM_ID = 49; final private static byte CC_ATOM_SYM = 50; final private static byte CC_ATOM_CHARGE = 51; final private static byte CC_ATOM_X = 52; final private static byte CC_ATOM_Y = 53; final private static byte CC_ATOM_Z = 54; final private static byte CC_ATOM_X_IDEAL = 55; final private static byte CC_ATOM_Y_IDEAL = 56; final private static byte CC_ATOM_Z_IDEAL = 57; final private static byte DISORDER_ASSEMBLY = 58; final private static byte ASYM_ID = 59; final private static byte SUBSYS_ID = 60; final private static byte SITE_MULT = 61; final private static byte THERMAL_TYPE = 62; final private static byte MOMENT_LABEL = 63; final private static byte MOMENT_PRELIM_X = 64; final private static byte MOMENT_PRELIM_Y = 65; final private static byte MOMENT_PRELIM_Z = 66; final private static byte MOMENT_X = 67; final private static byte MOMENT_Y = 68; final private static byte MOMENT_Z = 69; final private static byte ATOM_ID = 70; final private static byte SEQ_ID = 71; final protected static String FAMILY_ATOM = "_atom_site"; final private static String[] atomFields = { "*_type_symbol", "*_label", "*_auth_atom_id", "*_fract_x", "*_fract_y", "*_fract_z", "*_cartn_x", "*_cartn_y", "*_cartn_z", "*_occupancy", "*_b_iso_or_equiv", "*_auth_comp_id", "*_auth_asym_id", "*_auth_seq_id", "*_pdbx_pdb_ins_code", "*_label_alt_id", "*_group_pdb", "*_pdbx_pdb_model_num", "*_calc_flag", "*_disorder_group", "*_aniso_label", "*_anisotrop_id", "*_aniso_u_11", "*_aniso_u_22", "*_aniso_u_33", "*_aniso_u_12", "*_aniso_u_13", "*_aniso_u_23", "*_anisotrop_u[1][1]", "*_anisotrop_u[2][2]", "*_anisotrop_u[3][3]", "*_anisotrop_u[1][2]", "*_anisotrop_u[1][3]", "*_anisotrop_u[2][3]", "*_u_iso_or_equiv", "*_aniso_b_11", "*_aniso_b_22", "*_aniso_b_33", "*_aniso_b_12", "*_aniso_b_13", "*_aniso_b_23", "*_aniso_beta_11", "*_aniso_beta_22", "*_aniso_beta_33", "*_aniso_beta_12", "*_aniso_beta_13", "*_aniso_beta_23", "*_adp_type", "_chem_comp_atom_comp_id", "_chem_comp_atom_atom_id", "_chem_comp_atom_type_symbol", "_chem_comp_atom_charge", "_chem_comp_atom_model_cartn_x", "_chem_comp_atom_model_cartn_y", "_chem_comp_atom_model_cartn_z", "_chem_comp_atom_pdbx_model_cartn_x_ideal", "_chem_comp_atom_pdbx_model_cartn_y_ideal", "_chem_comp_atom_pdbx_model_cartn_z_ideal", "*_disorder_assembly", "*_label_asym_id", "*_subsystem_code", "*_symmetry_multiplicity", "*_thermal_displace_type", "*_moment_label", "*_moment_crystalaxis_mx", "*_moment_crystalaxis_my", "*_moment_crystalaxis_mz", "*_moment_crystalaxis_x", "*_moment_crystalaxis_y", "*_moment_crystalaxis_z", "*_id", "*_label_seq_id" }; final private static String singleAtomID = atomFields[CC_COMP_ID]; /* to: [email protected] * from: Zukang Feng [email protected] * re: Two mmCIF issues * date: 4/18/2006 10:30 PM * "You should always use _atom_site.auth_asym_id for PDB chain IDs." * * */ /** * reads atom data in any order * @param isLigand * * @return TRUE if successful; FALS if EOF encountered * @throws Exception */ boolean processAtomSiteLoopBlock(boolean isLigand) throws Exception { this.isLigand = isLigand; int pdbModelNo = -1; // PDBX boolean haveCoord = true; parseLoopParametersFor(FAMILY_ATOM, atomFields); if (key2col[CC_ATOM_X_IDEAL] != NONE) { setFractionalCoordinates(false); } else if (key2col[CARTN_X] != NONE || key2col[CC_ATOM_X] != NONE) { setFractionalCoordinates(false); disableField(FRACT_X); disableField(FRACT_Y); disableField(FRACT_Z); if (key2col[GROUP_PDB] != NONE && !isMMCIF) { // this should not happen setIsPDB(); isMMCIF = true; } } else if (key2col[FRACT_X] != NONE) { setFractionalCoordinates(true); disableField(CARTN_X); disableField(CARTN_Y); disableField(CARTN_Z); } else if (key2col[ANISO_LABEL] != NONE || key2col[ANISO_MMCIF_ID] != NONE || key2col[MOMENT_LABEL] != NONE) { haveCoord = false; // no coordinates, but valuable information } else { // it is a different kind of _atom_site loop block parser.skipLoop(false); return false; } int modelField = key2col[MODEL_NO]; int siteMult = 0; while (parser.getData()) { if (modelField >= 0) { // mmCIF only pdbModelNo = checkPDBModelField(modelField, pdbModelNo); if (pdbModelNo < 0) break; if (skipping) continue; } Atom atom = null; if (haveCoord) { atom = new Atom(); } else { if (fieldProperty(key2col[ANISO_LABEL]) != NONE || fieldProperty(key2col[ANISO_MMCIF_ID]) != NONE || fieldProperty(key2col[MOMENT_LABEL]) != NONE) { if ((atom = asc.getAtomFromName(field)) == null) continue; // atom has been filtered out } else { continue; } } String componentId = null; String strChain = null; String id = null; int seqID = 0; int n = parser.getColumnCount(); for (int i = 0; i < n; ++i) { int tok = fieldProperty(i); switch (tok) { case NONE: break; case SEQ_ID: seqID = parseIntStr(field); break; case ATOM_ID: id = field; break; case CC_ATOM_SYM: case TYPE_SYMBOL: String elementSymbol; if (field.length() < 2) { elementSymbol = field; } else { char ch1 = Character.toLowerCase(field.charAt(1)); if (Atom.isValidSym2(firstChar, ch1)) { elementSymbol = "" + firstChar + ch1; } else { elementSymbol = "" + firstChar; if (!haveHAtoms && firstChar == 'H') haveHAtoms = true; } } atom.elementSymbol = elementSymbol; if (htOxStates != null && htOxStates.containsKey(field)) { float charge = htOxStates.get(field).floatValue(); atom.formalCharge = Math.round(charge); //because otherwise -1.6 is rounded UP to -1, and 1.6 is rounded DOWN to 1 if (Math.abs(atom.formalCharge - charge) > 0.1) if (debugging) { Logger.debug("CIF charge on " + field + " was " + charge + "; rounded to " + atom.formalCharge); } } break; case CC_ATOM_ID: case LABEL: case AUTH_ATOM: atom.atomName = field; break; case CC_ATOM_X_IDEAL: float x = parseFloatStr(field); if (readIdeal && !Float.isNaN(x)) atom.x = x; break; case CC_ATOM_Y_IDEAL: float y = parseFloatStr(field); if (readIdeal && !Float.isNaN(y)) atom.y = y; break; case CC_ATOM_Z_IDEAL: float z = parseFloatStr(field); if (readIdeal && !Float.isNaN(z)) atom.z = z; break; case CC_ATOM_X: case CARTN_X: case FRACT_X: atom.x = parseFloatStr(field); break; case CC_ATOM_Y: case CARTN_Y: case FRACT_Y: atom.y = parseFloatStr(field); break; case CC_ATOM_Z: case CARTN_Z: case FRACT_Z: atom.z = parseFloatStr(field); break; case CC_ATOM_CHARGE: atom.formalCharge = parseIntStr(field); break; case OCCUPANCY: float floatOccupancy = parseFloatStr(field); if (!Float.isNaN(floatOccupancy)) atom.foccupancy = floatOccupancy; break; case B_ISO: atom.bfactor = parseFloatStr(field) * (isMMCIF ? 1 : 100f); break; case CC_COMP_ID: case COMP_ID: atom.group3 = field; break; case ASYM_ID: componentId = field; if (!useAuthorChainID) setChainID(atom, strChain = field); break; case AUTH_ASYM_ID: if (useAuthorChainID) setChainID(atom, strChain = field); break; case AUTH_SEQ_ID: maxSerial = Math.max(maxSerial, atom.sequenceNumber = parseIntStr(field)); break; case INS_CODE: atom.insertionCode = firstChar; break; case ALT_ID: case SUBSYS_ID: atom.altLoc = firstChar; break; case DISORDER_ASSEMBLY: disorderAssembly = field; break; case DISORDER_GROUP: if (firstChar == '-' && field.length() > 1) { atom.altLoc = field.charAt(1); atom.ignoreSymmetry = true; } else { atom.altLoc = firstChar; } break; case GROUP_PDB: if ("HETATM".equals(field)) atom.isHetero = true; break; case DUMMY_ATOM: //see http://www.iucr.org/iucr-top/cif/cifdic_html/ // 1/cif_core.dic/Iatom_site_calc_flag.html if ("dum".equals(field)) { atom.x = Float.NaN; continue; //skip } break; case SITE_MULT: if (modulated) siteMult = parseIntStr(field); break; case THERMAL_TYPE: case ADP_TYPE: if (field.equalsIgnoreCase("Uiso")) { int j = key2col[U_ISO_OR_EQUIV]; if (j != NONE) asc.setU(atom, 7, parseFloatStr(parser.getColumnData(j))); } break; case ANISO_U11: case ANISO_U22: case ANISO_U33: case ANISO_U12: case ANISO_U13: case ANISO_U23: case ANISO_MMCIF_U11: case ANISO_MMCIF_U22: case ANISO_MMCIF_U33: case ANISO_MMCIF_U12: case ANISO_MMCIF_U13: case ANISO_MMCIF_U23: // Ortep Type 8: D = 2pi^2, C = 2, a*b* asc.setU(atom, (col2key[i] - ANISO_U11) % 6, parseFloatStr(field)); break; case ANISO_B11: case ANISO_B22: case ANISO_B33: case ANISO_B12: case ANISO_B13: case ANISO_B23: // Ortep Type 4: D = 1/4, C = 2, a*b* asc.setU(atom, 6, 4); asc.setU(atom, (col2key[i] - ANISO_B11) % 6, parseFloatStr(field)); break; case ANISO_BETA_11: case ANISO_BETA_22: case ANISO_BETA_33: case ANISO_BETA_12: case ANISO_BETA_13: case ANISO_BETA_23: //Ortep Type 0: D = 1, c = 2 -- see org.jmol.symmetry/UnitCell.java asc.setU(atom, 6, 0); asc.setU(atom, (col2key[i] - ANISO_BETA_11) % 6, parseFloatStr(field)); break; case MOMENT_PRELIM_X: case MOMENT_PRELIM_Y: case MOMENT_PRELIM_Z: case MOMENT_X: case MOMENT_Y: case MOMENT_Z: isMagCIF = true; V3 pt = atom.vib; if (pt == null) atom.vib = pt = new Vibration().setType(Vibration.TYPE_SPIN); float v = parseFloatStr(field); switch (tok) { case MOMENT_PRELIM_X: case MOMENT_X: pt.x = v; appendLoadNote("magnetic moment: " + line); break; case MOMENT_PRELIM_Y: case MOMENT_Y: pt.y = v; break; case MOMENT_PRELIM_Z: case MOMENT_Z: pt.z = v; break; } break; } } if (!haveCoord) continue; if (Float.isNaN(atom.x) || Float.isNaN(atom.y) || Float.isNaN(atom.z)) { Logger.warn("atom " + atom.atomName + " has invalid/unknown coordinates"); continue; } if (atom.elementSymbol == null && atom.atomName != null) atom.getElementSymbol(); if (!filterCIFAtom(atom, componentId)) continue; setAtomCoord(atom); if (isMMCIF && !processSubclassAtom(atom, componentId, strChain)) continue; if (asc.iSet < 0) nextAtomSet(); asc.addAtomWithMappedName(atom); if (id != null) { asc.atomSymbolicMap.put(id, atom); if (seqID > 0) { V3 pt = atom.vib; if (pt == null) pt = asc.addVibrationVector(atom.index, 0, Float.NaN, T.seqid); pt.x = seqID; } } ac++; if (modDim > 0 && siteMult != 0) atom.vib = V3.new3(siteMult, 0, Float.NaN); } asc.setCurrentModelInfo("isCIF", Boolean.TRUE); if (isMMCIF) setModelPDB(true); if (isMMCIF && skipping) skipping = false; return true; } /** * @param modelField * @param currentModelNo * @return new currentModelNo * @throws Exception */ protected int checkPDBModelField(int modelField, int currentModelNo) throws Exception { // overridden in MMCIF reader return 0; } /** * @param atom * @param assemblyId * @param strChain * @return true if valid atom */ protected boolean processSubclassAtom(Atom atom, String assemblyId, String strChain) { return true; } protected boolean filterCIFAtom(Atom atom, String componentId) { if (!filterAtom(atom, -1)) return false; if (filterAssembly && filterReject(filter, "$", componentId)) return false; if (configurationPtr > 0) { if (!disorderAssembly.equals(lastDisorderAssembly)) { lastDisorderAssembly = disorderAssembly; lastAltLoc = '\0'; conformationIndex = configurationPtr; } // ignore atoms that have no designation if (atom.altLoc != '\0') { // count down until we get the desired index into the list if (conformationIndex >= 0 && atom.altLoc != lastAltLoc) { lastAltLoc = atom.altLoc; conformationIndex--; } if (conformationIndex != 0) { Logger.info("ignoring " + atom.atomName); return false; } } } return true; } final private static byte CITATION_TITLE = 0; final private static String[] citationFields = { "_citation_title" }; private void processCitationListBlock() throws Exception { parseLoopParameters(citationFields); while (parser.getData()) { String title = getField(CITATION_TITLE); if (!isNull(title)) appendLoadNote("TITLE: " + parser.toUnicode(title)); } } //////////////////////////////////////////////////////////////// // symmetry operations //////////////////////////////////////////////////////////////// //loop_ //_space_group_symop.magn_centering_id //_space_group_symop.magn_centering_xyz //1 x+2/3,y+1/3,z+1/3,+1 mx,my,mz //2 x+1/3,y+2/3,z+2/3,+1 mx,my,mz //3 x,y,z,+1 mx,my,mz //_space_group_symop.magn_ssg_centering_id //_space_group_symop.magn_ssg_centering_algebraic // 1 x1,x2,x3,x4,+1 // 2 x1,x2,x3,x4+1/2,-1 final private static byte SYM_XYZ = 0; final private static byte SYM_MAGN_XYZ = 1; final private static byte SYM_SSG_ALG = 2; final private static byte SYM_MAGN_SSG_ALG = 3; final private static byte SYM_EQ_XYZ = 4; final private static byte SYM_SSG_EQ_XYZ = 5; final private static byte SYM_MAGN_REV = 7; final private static byte SYM_MAGN_SSG_REV = 8; final private static byte SYM_MAGN_REV_PRELIM = 6; final private static byte SYM_MAGN_CENTERING = 9; final private static byte SYM_MAGN_SSG_CENTERING = 10; final private static byte SYM_MAGN_SSG_CENT_XYZ = 11; final private static String FAMILY_SGOP = "_space_group_symop"; final private static String[] symmetryOperationsFields = { "*_operation_xyz", "*_magn_operation_xyz", "*_ssg_operation_algebraic", "*_magn_ssg_operation_algebraic", "_symmetry_equiv_pos_as_xyz", // old "_symmetry_ssg_equiv_pos_as_xyz", // old "*_magn_operation_timereversal", // second iteration "*_magn_ssg_operation_timereversal", // another iteration "*_operation_timereversal", // preliminary only "*_magn_centering_xyz", "*_magn_ssg_centering_algebraic", "*_magn_ssg_centering_xyz" // preliminary }; /** * retrieves symmetry operations * * @throws Exception */ private void processSymmetryOperationsLoopBlock() throws Exception { parseLoopParametersFor(FAMILY_SGOP, symmetryOperationsFields); int n; symops = new Lst(); for (n = symmetryOperationsFields.length; --n >= 0;) if (key2col[n] != NONE) break; if (n < 0) { Logger.warn("required " + FAMILY_SGOP + " key not found"); parser.skipLoop(false); return; } n = 0; boolean isMag = false; while (parser.getData()) { boolean ssgop = false; int nn = parser.getColumnCount(); int timeRev = (fieldProperty(key2col[SYM_MAGN_REV]) == NONE && fieldProperty(key2col[SYM_MAGN_SSG_REV]) == NONE && fieldProperty(key2col[SYM_MAGN_REV_PRELIM]) == NONE ? 0 : field .equals("-1") ? -1 : 1); for (int i = 0, tok; i < nn; ++i) { switch (tok = fieldProperty(i)) { case SYM_SSG_EQ_XYZ: // check for non-standard record x~1~,x~2~,x~3~,x~4~ kIsfCqpM.cif if (field.indexOf('~') >= 0) field = PT.rep(field, "~", ""); //$FALL-THROUGH$ case SYM_SSG_ALG: case SYM_MAGN_SSG_ALG: modulated = true; ssgop = true; //$FALL-THROUGH$ case SYM_XYZ: case SYM_EQ_XYZ: case SYM_MAGN_XYZ: if (allowRotations || timeRev != 0 || ++n == 1) if (!modulated || ssgop) { if (tok == SYM_MAGN_XYZ || tok == SYM_MAGN_SSG_ALG) { isMag = true; timeRev = (field.endsWith(",+1") || field.endsWith(",1") ? 1 : field.endsWith(",-1") ? -1 : 0); if (timeRev != 0) field = field.substring(0, field.lastIndexOf(',')); } if (timeRev != 0) field += "," + (timeRev == 1 ? "m" : "-m"); field = field.replace(';', ' '); symops.addLast(field); setSymmetryOperator(field); } break; case SYM_MAGN_CENTERING: case SYM_MAGN_SSG_CENTERING: case SYM_MAGN_SSG_CENT_XYZ: isMag = true; if (magCenterings == null) magCenterings = new Lst(); magCenterings.addLast(field); break; } } } if (ms != null && !isMag) addLatticeVectors(); } public int getBondOrder(String field) { switch (field.toUpperCase().charAt(0)) { default: Logger.warn("unknown CIF bond order: " + field); //$FALL-THROUGH$ case '\0': case 'S': return JmolAdapter.ORDER_COVALENT_SINGLE; case 'D': return JmolAdapter.ORDER_COVALENT_DOUBLE; case 'T': return JmolAdapter.ORDER_COVALENT_TRIPLE; case 'Q': return JmolAdapter.ORDER_COVALENT_QUAD; case 'A': haveAromatic = true; return JmolAdapter.ORDER_AROMATIC; } } final private static byte GEOM_BOND_ATOM_SITE_LABEL_1 = 0; final private static byte GEOM_BOND_ATOM_SITE_LABEL_2 = 1; final private static byte GEOM_BOND_DISTANCE = 2; final private static byte CCDC_GEOM_BOND_TYPE = 3; //final private static byte GEOM_BOND_SITE_SYMMETRY_2 = 3; final private static String[] geomBondFields = { "_geom_bond_atom_site_label_1", "_geom_bond_atom_site_label_2", "_geom_bond_distance", "_ccdc_geom_bond_type" // "_geom_bond_site_symmetry_2", }; /** * * reads bond data -- N_ijk symmetry business is ignored, so we only indicate * bonds within the unit cell to just the original set of atoms. "connect" * script or "set forceAutoBond" will override these values, but see below. * * @throws Exception */ private void processGeomBondLoopBlock() throws Exception { // broken in 13.3.4_dev_2013.08.20c // fixed in 14.4.3_2016.02.16 boolean bondLoopBug = (stateScriptVersionInt >= 130304 && stateScriptVersionInt < 140403 || stateScriptVersionInt >= 150000 && stateScriptVersionInt < 150403); parseLoopParameters(geomBondFields); if (bondLoopBug || !checkAllFieldsPresent(geomBondFields, 2, true)) { parser.skipLoop(false); return; } int bondCount = 0; String name1, name2 = null; while (parser.getData()) { if (asc.getAtomIndex(name1 = getField(GEOM_BOND_ATOM_SITE_LABEL_1)) < 0 || asc.getAtomIndex(name2 = getField(GEOM_BOND_ATOM_SITE_LABEL_2)) < 0) continue; int order = getBondOrder(getField(CCDC_GEOM_BOND_TYPE)); String sdist = getField(GEOM_BOND_DISTANCE); float distance = parseFloatStr(sdist); if (distance == 0 || Float.isNaN(distance)) { if (!iHaveFractionalCoordinates) { // maybe this is a simple Cartesian file with coordinates and bonds Atom a = asc.getAtomFromName(name1); Atom b = asc.getAtomFromName(name2); if (a != null && b != null) asc.addNewBondWithOrder(a.index, b.index, order); } continue; } float dx = 0; int pt = sdist.indexOf('('); if (pt >= 0) { char[] data = sdist.toCharArray(); // 3.567(12) --> 0.012 String sdx = sdist.substring(pt + 1, sdist.length() - 1); int n = sdx.length(); for (int j = pt; --j >= 0;) { if (data[j] == '.' && --j < 0) break; data[j] = (--n < 0 ? '0' : sdx.charAt(n)); } dx = parseFloatStr(String.valueOf(data)); if (Float.isNaN(dx)) { Logger.info("error reading uncertainty for " + line); dx = 0.015f; } // TODO -- this is the full +/- (dx) in x.xxx(dx) -- is that too large? } else { dx = 0.015f; } // This field is from Materials Studio. See supplemental material for // http://pubs.rsc.org/en/Content/ArticleLanding/2012/CC/c2cc34714h // http://www.rsc.org/suppdata/cc/c2/c2cc34714h/c2cc34714h.txt // Jmol list discussion: https://sourceforge.net/p/jmol/mailman/message/31308577/ // this 5-model file can be read using one model at a time: load "c2cc34714h.txt" 3 // but it is far from perfect, and still the best way is load "c2cc34714h.txt" 3 packed bondCount++; bondTypes.addLast(new Object[] { name1, name2, Float.valueOf(distance), Float.valueOf(dx), Integer.valueOf(order) }); } if (bondCount > 0) { Logger.info(bondCount + " bonds read"); if (!doApplySymmetry) { isMolecular = true; forceSymmetry(false); } } } ///////////////////////////////////// // bonding and molecular ///////////////////////////////////// private float[] atomRadius; private BS[] bsConnected; private BS[] bsSets; final private P3 ptOffset = new P3(); private BS bsMolecule; private BS bsExclude; private int firstAtom; private Atom[] atoms; private BS bsBondDuplicates; /** * (1) If GEOM_BOND records are present, we (a) use them to generate bonds (b) * add H atoms to bonds if necessary (c) turn off autoBonding ("hasBonds") (2) * If MOLECULAR, then we (a) use {1 1 1} if lattice is not defined (b) use * asc.bonds[] to construct a preliminary molecule and connect as we go (c) * check symmetry for connections to molecule in any one of the 27 3x3 * adjacent cells (d) move those atoms and their connected branch set (e) * iterate as necessary to get all atoms desired (f) delete unselected atoms * (g) set all coordinates as Cartesians (h) remove all unit cell information */ private void setBondingAndMolecules() { atoms = asc.atoms; firstAtom = asc.getLastAtomSetAtomIndex(); int nat = asc.getLastAtomSetAtomCount(); ac = firstAtom + nat; Logger.info("CIF creating molecule for " + nat + " atoms " + (bondTypes.size() > 0 ? " using GEOM_BOND records" : "")); // get list of sites based on atom names bsSets = new BS[nat]; symmetry = asc.getSymmetry(); for (int i = firstAtom; i < ac; i++) { int ipt = asc.getAtomFromName(atoms[i].atomName).index - firstAtom; if (bsSets[ipt] == null) bsSets[ipt] = new BS(); bsSets[ipt].set(i - firstAtom); } // if molecular, we need atom connection lists and radii if (isMolecular) { atomRadius = new float[ac]; for (int i = firstAtom; i < ac; i++) { int elemnoWithIsotope = JmolAdapter.getElementNumber(atoms[i] .getElementSymbol()); atoms[i].elementNumber = (short) elemnoWithIsotope; int charge = (atoms[i].formalCharge == Integer.MIN_VALUE ? 0 : atoms[i].formalCharge); if (elemnoWithIsotope > 0) atomRadius[i] = JmolAdapter.getBondingRadius(elemnoWithIsotope, charge); } bsConnected = new BS[ac]; for (int i = firstAtom; i < ac; i++) bsConnected[i] = new BS(); // Set up a working set of atoms in the "molecule". bsMolecule = new BS(); // Set up a working set of atoms that should be excluded // because they would map onto an equivalent atom's position. bsExclude = new BS(); } boolean isFirst = true; bsBondDuplicates = new BS(); while (createBonds(isFirst)) { isFirst = false; // main loop continues until no new atoms are found } if (isMolecular && iHaveFractionalCoordinates && !bsMolecule.isEmpty()) { // Set bsAtoms to control which atoms and // bonds are delivered by the iterators. if (asc.bsAtoms == null) asc.bsAtoms = new BS(); asc.bsAtoms.clearBits(firstAtom, ac); asc.bsAtoms.or(bsMolecule); asc.bsAtoms.andNot(bsExclude); // Set atom positions to be Cartesians and clear out unit cell // so that the model displays without it. for (int i = firstAtom; i < ac; i++) { if (asc.bsAtoms.get(i)) symmetry.toCartesian(atoms[i], true); else if (debugging) Logger.debug(molecularType + " removing " + i + " " + atoms[i].atomName + " " + atoms[i]); } asc.setCurrentModelInfo("unitCellParams", null); if (nMolecular++ == asc.iSet) { asc.clearGlobalBoolean(AtomSetCollection.GLOBAL_FRACTCOORD); asc.clearGlobalBoolean(AtomSetCollection.GLOBAL_SYMMETRY); asc.clearGlobalBoolean(AtomSetCollection.GLOBAL_UNITCELLS); } } // Set return info to enable desired defaults. if (bondTypes.size() > 0) asc.setCurrentModelInfo("hasBonds", Boolean.TRUE); // Clear temporary fields. bondTypes.clear(); atomRadius = null; bsSets = null; bsConnected = null; bsMolecule = null; bsExclude = null; } private void fixAtomForBonding(P3 pt, int i) { pt.setT(atoms[i]);//pt = (pt == null ? atoms[i] : P3.newP(atoms[i])); if (iHaveFractionalCoordinates) symmetry.toCartesian(pt, true); } /** * Use the site bitset to check for atoms that are within +/-dx Angstroms of * the specified distances in GEOM_BOND where dx is determined by the * uncertainty (dx) in the record. Note that this also "connects" the atoms * that might have been moved in a previous iteration. * * Also connect H atoms based on a distance <= 1.1 Angstrom from a nearby * atom. * * Then create molecules. * * @param doInit * @return TRUE if need to continue */ private boolean createBonds(boolean doInit) { // process GEOM_BOND records String list = ""; boolean haveH = false; for (int i = bondTypes.size(); --i >= 0;) { if (bsBondDuplicates.get(i)) continue; Object[] o = bondTypes.get(i); float distance = ((Float) o[2]).floatValue(); float dx = ((Float) o[3]).floatValue(); int order = ((Integer) o[4]).intValue(); int iatom1 = asc.getAtomIndex((String) o[0]); int iatom2 = asc.getAtomIndex((String) o[1]); if (doInit) { String key = ";" + iatom1 + ";" + iatom2 + ";" + distance; if (list.indexOf(key) >= 0) { bsBondDuplicates.set(i); continue; } list += key; } BS bs1 = bsSets[iatom1 - firstAtom]; BS bs2 = bsSets[iatom2 - firstAtom]; if (bs1 == null || bs2 == null) continue; if (atoms[iatom1].elementNumber == 1 || atoms[iatom2].elementNumber == 1) haveH = true; for (int j = bs1.nextSetBit(0); j >= 0; j = bs1.nextSetBit(j + 1)) { for (int k = bs2.nextSetBit(0); k >= 0; k = bs2.nextSetBit(k + 1)) { if ((!isMolecular || !bsConnected[j + firstAtom].get(k)) && checkBondDistance(atoms[j + firstAtom], atoms[k + firstAtom], distance, dx)) addNewBond(j + firstAtom, k + firstAtom, order); } } } if (!iHaveFractionalCoordinates) return false; // do a quick check for H-X bonds if we have GEOM_BOND if (bondTypes.size() > 0 && !haveH) for (int i = firstAtom; i < ac; i++) if (atoms[i].elementNumber == 1) { boolean checkAltLoc = (atoms[i].altLoc != '\0'); for (int k = firstAtom; k < ac; k++) if (k != i && atoms[k].elementNumber != 1 && (!checkAltLoc || atoms[k].altLoc == '\0' || atoms[k].altLoc == atoms[i].altLoc)) { if (!bsConnected[i].get(k) && checkBondDistance(atoms[i], atoms[k], 1.1f, 0)) addNewBond(i, k, 1); } } if (!isMolecular) return false; // generate the base atom set if (doInit) for (int i = firstAtom; i < ac; i++) if (atoms[i].atomSite + firstAtom == i && !bsMolecule.get(i)) setBs(atoms, i, bsConnected, bsMolecule); // Now look through unchecked atoms for ones that // are within bonding distance of the "molecular" set // in any one of the 27 adjacent cells in 444 - 666. // If an atom is found, move it along with its "branch" // to the new location. BUT also check that we are // not overlaying another atom -- if that happens // go ahead and move it, but mark it as excluded. float bondTolerance = vwr.getFloat(T.bondtolerance); BS bsBranch = new BS(); P3 cart1 = new P3(); P3 cart2 = new P3(); int nFactor = 2; // 1 was not enough. (see data/cif/triclinic_issue.cif) for (int i = firstAtom; i < ac; i++) if (!bsMolecule.get(i) && !bsExclude.get(i)) for (int j = bsMolecule.nextSetBit(0); j >= 0; j = bsMolecule .nextSetBit(j + 1)) if (symmetry.checkDistance(atoms[j], atoms[i], atomRadius[i] + atomRadius[j] + bondTolerance, 0, nFactor, nFactor, nFactor, ptOffset)) { setBs(atoms, i, bsConnected, bsBranch); for (int k = bsBranch.nextSetBit(0); k >= 0; k = bsBranch .nextSetBit(k + 1)) { atoms[k].add(ptOffset); fixAtomForBonding(cart1, k); BS bs = bsSets[asc.getAtomIndex(atoms[k].atomName) - firstAtom]; if (bs != null) for (int ii = bs.nextSetBit(0); ii >= 0; ii = bs .nextSetBit(ii + 1)) { if (ii + firstAtom == k) continue; fixAtomForBonding(cart2, ii + firstAtom); if (cart2.distance(cart1) < 0.1f) { bsExclude.set(k); break; } } bsMolecule.set(k); } //System.out.println("returning true\n" + bsMolecule); return true; } return false; } private boolean checkBondDistance(Atom a, Atom b, float distance, float dx) { if (iHaveFractionalCoordinates) return symmetry.checkDistance(a, b, distance, dx, 0, 0, 0, ptOffset); float d = a.distance(b); return (dx > 0 ? Math.abs(d - distance) <= dx : d <= distance && d > 0.1f); // same as in Symmetry } /** * add the bond and mark it for molecular processing * * @param i * @param j * @param order */ private void addNewBond(int i, int j, int order) { asc.addNewBondWithOrder(i, j, order); if (!isMolecular) return; //System.out.println("bonding " + i + " " + j); bsConnected[i].set(j); bsConnected[j].set(i); } /** * iteratively run through connected atoms, adding them to the set * * @param atoms * @param iatom * @param bsBonds * @param bs */ private void setBs(Atom[] atoms, int iatom, BS[] bsBonds, BS bs) { BS bsBond = bsBonds[iatom]; bs.set(iatom); for (int i = bsBond.nextSetBit(0); i >= 0; i = bsBond.nextSetBit(i + 1)) { if (!bs.get(i)) setBs(atoms, i, bsBonds, bs); } } protected boolean checkSubclassSymmetry() { return doCheckUnitCell; } protected boolean checkAllFieldsPresent(String[] keys, int lastKey, boolean critical) { for (int i = (lastKey < 0 ? keys.length : lastKey); --i >= 0;) if (key2col[i] == NONE) { if (critical) Logger.warn("CIF reader missing property: " + keys[i]); return false; } return true; } protected String getField(byte type) { int i = key2col[type]; return (i == NONE ? "\0" : parser.getColumnData(i)); } protected boolean isNull(String key) { return key.equals("\0"); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy