Package org.jmol.adapter.smarter

Source Code of org.jmol.adapter.smarter.AtomSetCollectionReader

/* $RCSfile$
* $Author: hansonr $
* $Date: 2006-10-22 14:12:46 -0500 (Sun, 22 Oct 2006) $
* $Revision: 5999 $
*
* Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
*
* Contact: jmol-developers@lists.sf.net
*
*  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.smarter;

import org.jmol.api.Interface;
import org.jmol.api.JmolAdapter;
import org.jmol.api.JmolViewer;
import org.jmol.api.SymmetryInterface;
import org.jmol.util.BitSetUtil;
import org.jmol.util.Logger;
import org.jmol.util.Parser;
import org.jmol.util.Quaternion;

import java.io.BufferedReader;
import java.io.OutputStream;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import javax.vecmath.Matrix3f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/*
* Notes 9/2006 Bob Hanson
*
* all parsing functions now moved to org.jmol.util.Parser
*
* to add symmetry capability to any reader, some or all of the following
* methods need to be there:
*
*  setFractionalCoordinates()
*  setSpaceGroupName()
*  setUnitCell()
*  setUnitCellItem()
*  setAtomCoord()
*
* At the very minimum, you need:
*
*  setAtomCoord()
*
* so that:
*  (a) atom coordinates can be turned fractional by load parameters
*  (b) symmetry can be applied once per model in the file
*  If you know where the end of the atom+bond data are, then you can
*  use applySymmetryAndSetTrajectory() once, just before exiting. Otherwise, use it
*  twice -- it has a check to make sure it doesn't RUN twice -- once
*  at the beginning and once at the end of the model.
* htParams is used for passing information to the readers
* and for returning information from the readers
*
* It won't be null at this stage.
*
* from Eval or Viewer:
*
*  applySymmetryToBonds
*  atomTypes (for Mol2Reader)
*  bsModels
*  filter
*  firstLastStep
*  firstLastSteps
*  getHeader
*  isTrajectory
*  lattice
*  manifest (for SmarterJmolAdapter)
*  modelNumber
*  spaceGroupIndex
*  symmetryRange
*  unitcell
*  packed
* from FileManager:
*
*  fullPathName
*  subFileList (for SmarterJmolAdapter)
*
* from MdTopReader:
*  
*  isPeriodic
*  templateAtomCount
* from MdCrdReader:  
*
*  trajectorySteps
* from Resolver:
*
*  filteredAtomCount
*  ptFile
*  readerName
*  templateAtomCount
* from AtomSetCollectionReader:
*  bsFilter
*
*/

public abstract class AtomSetCollectionReader {

  public final static float ANGSTROMS_PER_BOHR = 0.5291772f; // used by SpartanArchive

  protected AtomSetCollection atomSetCollection;
  protected BufferedReader reader;
  protected String readerName;
  public Hashtable htParams;
  //protected String parameterData;

  // buffer
  public String line, prevline;
  protected int[] next = new int[1];
  protected long ptLine;

  // protected/public state variables
  public int[] latticeCells;
  public boolean doProcessLines;
  public boolean iHaveUnitCell;
  public boolean iHaveSymmetryOperators;
  protected boolean doApplySymmetry;
  protected boolean ignoreFileSymmetryOperators;
  protected boolean isTrajectory;
  public boolean applySymmetryToBonds;
  protected boolean doCheckUnitCell;
  protected boolean getHeader;
  protected boolean isSequential;
  protected int templateAtomCount;
  public int modelNumber;
  protected int vibrationNumber;
  public int desiredVibrationNumber = Integer.MIN_VALUE;
  protected BitSet bsModels;
  protected String spaceGroup;
  protected boolean havePartialChargeFilter;
  public String calculationType = "?";
  // private state variables
  private boolean iHaveFractionalCoordinates;
  private boolean doPackUnitCell;
  private boolean doConvertToFractional;
  private boolean merging;
  private boolean fileCoordinatesAreFractional;
  protected boolean ignoreFileUnitCell;
  protected boolean ignoreFileSpaceGroupName;
  private float symmetryRange; 
  protected float[] notionalUnitCell; //0-5 a b c alpha beta gamma; 6-21 matrix c->f
  private int[] firstLastStep;
  protected int desiredModelNumber = Integer.MIN_VALUE;
  private int lastModelNumber = Integer.MAX_VALUE;
  private int desiredSpaceGroupIndex = -1;
  protected SymmetryInterface symmetry;
  protected OutputStream os;
  private Point3f fileScaling;
  private Point3f fileOffset;
 
/* 
  public void finalize() {
    System.out.println(this + " finalized");
  }
*/ 

  String fileName;
 
  void setup(String fileName, Hashtable htParams, BufferedReader reader) {
    this.htParams = htParams;
    this.fileName = fileName.replace('\\','/');
    this.reader = reader;   
  }

  Object readData() throws Exception {
    initialize();
    atomSetCollection = new AtomSetCollection(readerName, this);
    try {
      initializeReader();
      if (line == null && continuing)
        readLine();
      while (line != null && continuing)
        if (checkLine())
          readLine();
      finalizeReader();
    } catch (Throwable e) {
      setError(e);
    }
    reader.close();
    return finish();
 
 
  protected Object readData(Object DOMNode) {
    initialize();
    readAtomSetCollectionFromDOM(DOMNode);
    return finish();
  }

  public void readAtomSetCollectionFromDOM(Object DOMNode) {
    // XML readers only
  }
 
  public boolean continuing = true;

  protected JmolViewer viewer;
 
  protected void initializeReader() throws Exception {
    // reader-dependent
  }

  /**
   * @return true if need to read new line
   * @throws Exception
   *
   */
  protected boolean checkLine() throws Exception {
    // reader-dependent
    return true;
  }
 
  /**
   * sets continuing and doProcessLines
   *
   * @return TRUE if continuing, FALSE if not
   *
   */
  public boolean checkLastModel() {
    if (isLastModel(modelNumber) && doProcessLines) {
      continuing = false;
      return false;
    }
    doProcessLines = false;
    return true;
  }

  /**
   * after reading a model, Q: Is this the last model?
   *
   * @param modelNumber
   * @return  Yes/No
   */
  public boolean isLastModel(int modelNumber) {
    return (desiredModelNumber > 0 || modelNumber >= lastModelNumber);
  }
 
  protected void finalizeReader() throws Exception {
    applySymmetryAndSetTrajectory();
  }

  /////////////////////////////////////////////////////////////////////////////////////
 
  private Object finish() {
    String s = (String) htParams.get("loadState");
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo("loadState", s == null ? "" : s);
    s = (String) htParams.get("smilesString");
    if (s != null)
      atomSetCollection.setAtomSetCollectionAuxiliaryInfo("smilesString", s);
    if (!htParams.containsKey("templateAtomCount"))
      htParams.put("templateAtomCount", new Integer(atomSetCollection
          .getAtomCount()));
    if (htParams.containsKey("bsFilter"))
      htParams.put("filteredAtomCount", new Integer(BitSetUtil
          .cardinalityOf((BitSet) htParams.get("bsFilter"))));
    if (!calculationType.equals("?"))
      atomSetCollection.setAtomSetCollectionAuxiliaryInfo("calculationType",
          calculationType);

    String name = atomSetCollection.getFileTypeName();
    String fileType = name;
    if (fileType.indexOf("(") >= 0)
      fileType = fileType.substring(0, fileType.indexOf("("));
    for (int i = atomSetCollection.getAtomSetCount(); --i >= 0;) {
      atomSetCollection.setAtomSetAuxiliaryInfo("fileName", fileName, i);
      atomSetCollection.setAtomSetAuxiliaryInfo("fileType", fileType, i);
    }
    atomSetCollection.freeze();
    if (atomSetCollection.errorMessage != null)
      return atomSetCollection.errorMessage + "\nfor file " + fileName
          + "\ntype " + name;
    if (atomSetCollection.getAtomCount() == 0)
      return "No atoms found\nfor file " + fileName + "\ntype " + name;
    return atomSetCollection;
  }

  private void setError(Throwable e) {
    e.printStackTrace();
    if (line == null)
      atomSetCollection.errorMessage = "Unexpected end of file after line "
          + --ptLine + ":\n" + prevline;
    else
      atomSetCollection.errorMessage = "Error reading file at line " + ptLine
          + ":\n" + line + "\n" + e.getMessage();
  }
 
  private void initialize() {
    initializeSymmetry();
    this.viewer = (JmolViewer) htParams.get("viewer");
    htParams.remove("viewer"); // don't pass this on to user
    merging = htParams.containsKey("merging");
    getHeader = htParams.containsKey("getHeader");
    isSequential = htParams.containsKey("isSequential");
    readerName = (String) htParams.get("readerName");
    if (htParams.containsKey("OutputStream"))
      os = (OutputStream) htParams.get("OutputStream");
    //parameterData = (String) htParams.get("parameterData");
    if (htParams.containsKey("vibrationNumber"))
      desiredVibrationNumber = ((Integer) htParams.get("vibrationNumber")).intValue();
    else if (htParams.containsKey("modelNumber"))
      desiredModelNumber = ((Integer) htParams.get("modelNumber")).intValue();
    applySymmetryToBonds = htParams.containsKey("applySymmetryToBonds");
    bsFilter = (BitSet) htParams.get("bsFilter");
    setFilter((String) htParams.get("filter"));
    // ptFile < 0 indicates just one file being read
    // ptFile >= 0 indicates multiple files are being loaded
    // if the file is not the first read in the LOAD command, then
    // we look to see if it was loaded using LOAD ... "..." COORD ....
    int ptFile = (htParams.containsKey("ptFile") ? ((Integer) htParams
        .get("ptFile")).intValue() : -1);
    isTrajectory = htParams.containsKey("isTrajectory");
    if (ptFile > 0 && htParams.containsKey("firstLastSteps")) {
      Object val = ((List) htParams.get("firstLastSteps"))
          .get(ptFile - 1);
      if (val instanceof BitSet) {
        bsModels = (BitSet) val;
      } else {
        firstLastStep = (int[]) val;
      }
    } else if (htParams.containsKey("firstLastStep")) {
      firstLastStep = (int[]) htParams.get("firstLastStep");
    } else if (htParams.containsKey("bsModels")) {
      isTrajectory = htParams.containsKey("isTrajectory");
      bsModels = (BitSet) htParams.get("bsModels");
    }
    if (htParams.containsKey("templateAtomCount"))
      templateAtomCount = ((Integer) htParams.get("templateAtomCount"))
      .intValue();
    if (bsModels != null || firstLastStep != null)
      desiredModelNumber = Integer.MIN_VALUE;
    if (bsModels == null && firstLastStep != null) {
      if (firstLastStep[0] < 0)
        firstLastStep[0] = 0;
      if (firstLastStep[2] == 0 || firstLastStep[1] < firstLastStep[0])
        firstLastStep[1] = -1;
      if (firstLastStep[2] < 1)
        firstLastStep[2] = 1;
      bsModels = BitSetUtil.setBit(firstLastStep[0]);
      if (firstLastStep[1] > firstLastStep[0]) {
        for (int i = firstLastStep[0]; i <= firstLastStep[1]; i += firstLastStep[2])
          bsModels.set(i);
      }
    }
    if (bsModels != null && (firstLastStep == null || firstLastStep[1] != -1))
      lastModelNumber = bsModels.length();

    symmetryRange = (htParams.containsKey("symmetryRange") ? ((Float) htParams
        .get("symmetryRange")).floatValue() : 0);
    latticeCells = new int[3];
    if (htParams.containsKey("lattice")) {
      Point3f pt = ((Point3f) htParams.get("lattice"));
      latticeCells[0] = (int) pt.x;
      latticeCells[1] = (int) pt.y;
      latticeCells[2] = (int) pt.z;
      doPackUnitCell = (htParams.containsKey("packed") || latticeCells[2] < 0);
    }
    doApplySymmetry = (latticeCells[0] > 0 && latticeCells[1] > 0);
    //allows for {1 1 1} or {1 1 -1} or {555 555 0|1|-1} (-1  being "packed")
    if (!doApplySymmetry) {
      latticeCells[0] = 0;
      latticeCells[1] = 0;
      latticeCells[2] = 0;
    }

    //this flag FORCES symmetry -- generally if coordinates are not fractional,
    //we may note the unit cell, but we do not apply symmetry
    //with this flag, we convert any nonfractional coordinates to fractional
    //if a unit cell is available.

    if (htParams.containsKey("spaceGroupIndex")) {
      // three options include:
      // = -1: normal -- use operators if present or name if not
      // = -2: user is supplying operators or name
      // >=0: spacegroup fully determined
      // = -999: ignore -- just the operators

      desiredSpaceGroupIndex = ((Integer) htParams.get("spaceGroupIndex"))
          .intValue();
      if (desiredSpaceGroupIndex == -2)
        spaceGroup = (String) htParams.get("spaceGroupName");
      ignoreFileSpaceGroupName = (desiredSpaceGroupIndex == -2 || desiredSpaceGroupIndex >= 0);
      ignoreFileSymmetryOperators = (desiredSpaceGroupIndex != -1);
    }
    if (htParams.containsKey("unitcell")) {
      if (merging)
        setFractionalCoordinates(true);     
      float[] fParams = (float[]) htParams.get("unitcell");
      setUnitCell(fParams[0], fParams[1], fParams[2], fParams[3], fParams[4],
          fParams[5]);
      ignoreFileUnitCell = iHaveUnitCell;
      if (merging && !iHaveUnitCell)
        setFractionalCoordinates(false);
      // with appendNew == false and UNITCELL parameter, we assume fractional coordinates
    }   
  }

  public boolean haveModel;

  public boolean doGetModel(int modelNumber) {
    // modelNumber is 1-based, but firstLastStep is 0-based
    boolean isOK = (bsModels == null ? desiredModelNumber < 1
        || modelNumber == desiredModelNumber
        : modelNumber > lastModelNumber ? false : modelNumber > 0
            && bsModels.get(modelNumber - 1)
            || haveModel
            && firstLastStep != null
            && firstLastStep[1] < 0
            && (firstLastStep[2] < 2 || (modelNumber - 1 - firstLastStep[0])
                % firstLastStep[2] == 0));
    if (isOK && desiredModelNumber == 0)
      atomSetCollection.discardPreviousAtoms();
    haveModel |= isOK;
    if (isOK)
      doProcessLines = true;
    return isOK;
  }
 
  private String previousSpaceGroup;
  private float[] previousUnitCell;
 
  protected void initializeSymmetry() {
    previousSpaceGroup = spaceGroup;
    previousUnitCell = notionalUnitCell;
    iHaveUnitCell = ignoreFileUnitCell;
    if (!ignoreFileUnitCell) {
      notionalUnitCell = new float[22];
      //0-5 a b c alpha beta gamma; 6-21 m00 m01... m33 cartesian-->fractional
      for (int i = 22; --i >= 0;)
        notionalUnitCell[i] = Float.NaN;
      symmetry = null;
    }
    if (!ignoreFileSpaceGroupName)
      spaceGroup = "unspecified!";

    doCheckUnitCell = false;
  }
 
  protected void newAtomSet(String name) {
    if (atomSetCollection.getCurrentAtomSetIndex() >= 0) {
      atomSetCollection.newAtomSet();
      atomSetCollection.setCollectionName("<collection of "
          + (atomSetCollection.getCurrentAtomSetIndex() + 1) + " models>");
    } else {
      atomSetCollection.setCollectionName(name);
    }
    Logger.debug(name);
  }

 
  protected int cloneLastAtomSet(int atomCount) throws Exception {
    applySymmetryAndSetTrajectory();
    int lastAtomCount = atomSetCollection.getLastAtomSetAtomCount();
    atomSetCollection.cloneLastAtomSet(atomCount);
    if (atomSetCollection.haveUnitCell) {
      iHaveUnitCell = true;
      doCheckUnitCell = true;
      spaceGroup = previousSpaceGroup;
      notionalUnitCell = previousUnitCell;
    }
    return lastAtomCount;
  }

  public void setSpaceGroupName(String name) {
    if (ignoreFileSpaceGroupName)
      return;
    spaceGroup = name.trim();
    Logger.info("Setting space group name to " + spaceGroup);
  }

  public void setSymmetryOperator(String xyz) {
    if (ignoreFileSymmetryOperators)
      return;
    atomSetCollection.setLatticeCells(latticeCells, applySymmetryToBonds, doPackUnitCell);
    if (!atomSetCollection.addSpaceGroupOperation(xyz))
      Logger.warn("Skipping symmetry operation " + xyz);
    iHaveSymmetryOperators = true;
  }

  private int nMatrixElements = 0;
  private void initializeCartesianToFractional() {
    for (int i = 0; i < 16; i++)
      if (!Float.isNaN(notionalUnitCell[6 + i]))
        return; //just do this once
    for (int i = 0; i < 16; i++)
      notionalUnitCell[6 + i] = ((i % 5 == 0 ? 1 : 0));
    nMatrixElements = 0;
  }

  public void clearLatticeParameters() {
    if (ignoreFileUnitCell)
      return;
    for (int i = 6; i < notionalUnitCell.length; i++)
      notionalUnitCell[i] = Float.NaN;
    checkUnitCell(6);   
  }
 
  public void setUnitCellItem(int i, float x) {
    if (ignoreFileUnitCell)
      return;
    if (i == 0 && x == 1
        || i == 3 && x == 0)
      return;
    if (!Float.isNaN(x) && i >= 6 && Float.isNaN(notionalUnitCell[6]))
      initializeCartesianToFractional();
    notionalUnitCell[i] = x;
    if (Logger.debugging) {
      Logger.debug("setunitcellitem " + i + " " + x);
    }
    //System.out.println("atomSetCollection unitcell item " + i + " = " + x);
    if (i < 6 || Float.isNaN(x))
      iHaveUnitCell = checkUnitCell(6);
    else if(++nMatrixElements == 12)
      checkUnitCell(22);
  }

  protected Matrix3f matUnitCellOrientation;
 
  public void setUnitCell(float a, float b, float c, float alpha, float beta,
                   float gamma) {
    if (ignoreFileUnitCell)
      return;
    notionalUnitCell[0] = a;
    notionalUnitCell[1] = b;
    notionalUnitCell[2] = c;
    if (alpha != 0)
      notionalUnitCell[3] = alpha;
    if (beta != 0)
      notionalUnitCell[4] = beta;
    if (gamma != 0)
      notionalUnitCell[5] = gamma;
    iHaveUnitCell = checkUnitCell(6);
  }
 
  public void addPrimitiveLatticeVector(int i, float[] xyz, int i0) {
    if (ignoreFileUnitCell)
      return;
    if (i == 0)
      for (int j = 0; j < 6; j++)
        notionalUnitCell[j] = 0;
    i = 6 + i * 3;
    notionalUnitCell[i++] = xyz[i0++];
    notionalUnitCell[i++] = xyz[i0++];
    notionalUnitCell[i] = xyz[i0];
    if (Float.isNaN(notionalUnitCell[0])) {
      for (i = 0; i < 6; i++)
        notionalUnitCell[i] = -1;
    }
    iHaveUnitCell = checkUnitCell(15);
  }

  private boolean checkUnitCell(int n) {
    for (int i = 0; i < n; i++)
      if (Float.isNaN(notionalUnitCell[i]))
        return false;
    getSymmetry().setUnitCell(notionalUnitCell);
    if (doApplySymmetry)
      doConvertToFractional = !fileCoordinatesAreFractional;
    //if (but not only if) applying symmetry do we force conversion
    return true;
  }

  protected SymmetryInterface getSymmetry() {
    symmetry = (SymmetryInterface) Interface.getOptionInterface("symmetry.Symmetry");
    return symmetry;
  }

  public void setFractionalCoordinates(boolean TF) {
    iHaveFractionalCoordinates = fileCoordinatesAreFractional = TF;
  }

  /////////// FILTER /////////////////
 
  protected BitSet bsFilter;
  protected String filter;
  private boolean haveAtomFilter;
  private boolean filterAltLoc;
  private boolean filterGroup3;
  private boolean filterChain;
  private boolean filterAtomType;
  private boolean doSetOrientation;
  protected boolean addVibrations;
  public boolean readMolecularOrbitals;

  // MANY: "NOVIB" "NOMO"
  // CSF, SPARTAN: "NOORIENT"
  // CRYSTAL: "CONV" (conventional), "INPUT"
  // GAMESS-US:  "CHARGE=LOW"
  // JME, MOL: "NOMIN"
  // MOL:  "2D"
  // MOReaders: "NBOCHARGES"
  // PDB: "BIOMOLECULE n;" "NOSYMMETRY"  "CONF n"
  // Spartan: "INPUT", "ESPCHARGES"

  protected void setFilter(String filter0) {
    if (filter0 != null)
      filter0 = filter0.toUpperCase();
    filter = filter0;
    doSetOrientation = !checkFilter("NOORIENT");
    addVibrations = !checkFilter("NOVIB");
    readMolecularOrbitals = !checkFilter("NOMO");
    if (filter == null)
      return;
    filterAtomType = checkFilter("*.") || checkFilter("!.");
    filterGroup3 = checkFilter("[");
    filterChain = checkFilter(":");
    filterAltLoc = checkFilter("%");
    haveAtomFilter = filterAtomType || filterGroup3 || filterChain || filterAltLoc;
    if (bsFilter == null) {
      // bsFilter is usually null, but from MDTOP it gets set to indicate
      // which atoms were selected by the filter. This then
      // gets used by COORD files to load just those coordinates
      // and it returns the bitset of filtered atoms
      bsFilter = new BitSet();
      htParams.put("bsFilter", bsFilter);
      filter = (";" + filter + ";").replace(',', ';');
      Logger.info("filtering with " + filter);
    }
  }

  public boolean checkFilter(String key) {
    return (filter != null && filter.indexOf(key) >= 0);
  }

  /**
   * @param atom
   * @param iAtom
   * @return        true if we want this atom
   */
  protected boolean filterAtom(Atom atom, int iAtom) {
    if (!haveAtomFilter)
      return true;
    // cif, mdtop, pdb, gromacs, pqr
    boolean isOK =
      (!filterGroup3 || atom.group3 == null
          || !filterReject("![", "[" + atom.group3.toUpperCase() + "]"))
      && (!filterAtomType || atom.atomName == null
          || !filterReject("!.", "." + atom.atomName.toUpperCase() + ";"))
      && (!filterChain || atom.chainID == '\0'
          || !filterReject("!:", ":" + atom.chainID))
      && (!filterAltLoc || atom.alternateLocationID == '\0'
          || !filterReject("!%", "%" + atom.alternateLocationID));
    bsFilter.set(iAtom >= 0 ? iAtom : atomSetCollection.getAtomCount(), isOK);
    return isOK;
  }

  private boolean filterReject(String notCode, String code) {
    return (filter.indexOf(notCode) >= 0 ? filter.indexOf(code) >= 0
        : filter.indexOf(code) < 0);
  }

  protected void set2D() {
    // MOL and JME
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo("is2D", Boolean.TRUE);
    if (!checkFilter("NOMIN"))
      atomSetCollection.setAtomSetCollectionAuxiliaryInfo("doMinimize",
          Boolean.TRUE);
  }

  public boolean doGetVibration(int vibrationNumber) {
    // vibrationNumber is 1-based
    return addVibrations
        && (desiredVibrationNumber <= 0 || vibrationNumber == desiredVibrationNumber);
  }

  private Matrix3f matrixRotate;

  public void setTransform(float x1, float y1, float z1, float x2, float y2,
                           float z2, float x3, float y3, float z3) {
    if (matrixRotate != null || !doSetOrientation)
      return;
    matrixRotate = new Matrix3f();
    Vector3f v = new Vector3f();
    // rows in Sygress/CAChe and Spartan become columns here
    v.set(x1, y1, z1);
    v.normalize();
    matrixRotate.setColumn(0, v);
    v.set(x2, y2, z2);
    v.normalize();
    matrixRotate.setColumn(1, v);
    v.set(x3, y3, z3);
    v.normalize();
    matrixRotate.setColumn(2, v);
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo(
        "defaultOrientationMatrix", new Matrix3f(matrixRotate));
    // first two matrix column vectors define quaternion X and XY plane
    Quaternion q = new Quaternion(matrixRotate);
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo(
        "defaultOrientationQuaternion", q);
    Logger.info("defaultOrientationMatrix = " + matrixRotate);

  }

  /////////////////////////////
 
  public void setAtomCoord(Atom atom, float x, float y, float z) {
    atom.set(x, y, z);
    setAtomCoord(atom);
  }

  public void setAtomCoord(Atom atom) {
    // fileScaling is used by the PLOT command to
    // put data into PDB format, preserving name/residue information,
    // and still get any xyz data into the allotted column space.
    if (fileScaling != null) {
      atom.x = atom.x * fileScaling.x + fileOffset.x;
      atom.y = atom.y * fileScaling.y + fileOffset.y;
      atom.z = atom.z * fileScaling.z + fileOffset.z;
    }
    if (doConvertToFractional && !fileCoordinatesAreFractional
        && symmetry != null) {
      if (!symmetry.haveUnitCell())
        symmetry.setUnitCell(notionalUnitCell);
      symmetry.toFractional(atom, false);
      iHaveFractionalCoordinates = true;
    }
    //if (Logger.debugging)
    //Logger.debug(" atom "+atom.atomName + " " + atom.x + " " + atom.y+" "+atom.z);
    doCheckUnitCell = true;
  }

  protected void addSites(Hashtable htSites) {
    atomSetCollection.setAtomSetAuxiliaryInfo("pdbSites", htSites);
    Enumeration e = htSites.keys();
    String sites = "";
    while (e.hasMoreElements()) {
      String name = (String) e.nextElement();
      Hashtable htSite = (Hashtable) htSites.get(name);
      char ch;
      for (int i = name.length(); --i >= 0; )
        if (!Character.isLetterOrDigit(ch = name.charAt(i)) && ch != '\'')
          name = name.substring(0, i) + "_" + name.substring(i + 1);
      //String seqNum = (String) htSite.get("seqNum");
      String groups = (String) htSite.get("groups");
      if (groups.length() == 0)
        continue;
      addSiteScript("@site_" + name + " " + groups);
      //addJmolScript("@" + seqNum + " " + groups);
      addSiteScript("site_" + name + " = \"" + groups + "\".split(\",\")");
      sites += (sites == "" ? "" : ",") + "site_" + name;
    }
    addSiteScript("site_list = \"" + sites + "\".split(\",\")");
  }

  public void applySymmetryAndSetTrajectory() throws Exception {
    if (iHaveUnitCell && doCheckUnitCell) {
      atomSetCollection.setCoordinatesAreFractional(iHaveFractionalCoordinates);
      atomSetCollection.setNotionalUnitCell(notionalUnitCell,
          matUnitCellOrientation, unitCellOffset);
      atomSetCollection.setAtomSetSpaceGroupName(spaceGroup);
      atomSetCollection.setSymmetryRange(symmetryRange);
      if (doConvertToFractional || fileCoordinatesAreFractional) {
        atomSetCollection.setLatticeCells(latticeCells, applySymmetryToBonds,
            doPackUnitCell);
        if (ignoreFileSpaceGroupName || !iHaveSymmetryOperators) {
          if (!merging || symmetry == null)
            getSymmetry();
          if (symmetry.createSpaceGroup(desiredSpaceGroupIndex, (spaceGroup
              .indexOf("!") >= 0 ? "P1" : spaceGroup), notionalUnitCell,
              atomSetCollection.doNormalize)) {
            atomSetCollection.setAtomSetSpaceGroupName(symmetry
                .getSpaceGroupName());
            atomSetCollection.applySymmetry(symmetry);
          }
        } else {
          atomSetCollection.applySymmetry();
        }
      }
      if (iHaveFractionalCoordinates && merging && symmetry != null) {
        // when merging (with appendNew false), we must return cartesians
        atomSetCollection.toCartesian(symmetry);
        atomSetCollection.setAtomSetAuxiliaryInfo("coordinatesAreFractional",
            Boolean.FALSE);
        // We no longer allow merging of multiple-model files
        // when the file to be appended has fractional coordinates and vibrations
        addVibrations = false;
      }
    }
    if (isTrajectory)
      atomSetCollection.setTrajectory();
    initializeSymmetry();
  }

  public void setMOData(Hashtable moData) {
    atomSetCollection.setAtomSetAuxiliaryInfo("moData", moData);
    if (moData == null)
      return;
    List orbitals = (List) moData.get("mos");
    if (orbitals != null)
      Logger.info(orbitals.size() + " molecular orbitals read in model " + atomSetCollection.getAtomSetCount());
  }

  public static String getElementSymbol(int elementNumber) {
    return JmolAdapter.getElementSymbol(elementNumber);
  }
 
  /**
   * fills an array with a predefined number of lines of data that is
   * arranged in fixed FORTRAN-like column format
   *  
   * @param data
   * @param col0
   * @param colWidth
   * @throws Exception
   */
  protected void fillDataBlock(String[][] data, int col0, int colWidth) throws Exception {
    if (colWidth == 0) {
      fillDataBlock(data);
      return;
    }
    int nLines = data.length;
    for (int i = 0; i < nLines; i++) {
      discardLinesUntilNonBlank();
      int nFields = (line.length() - col0) / colWidth;
      data[i] = new String[nFields];
      for (int j = 0, start = col0; j < nFields; j++, start += colWidth)
        data[i][j] = line.substring(start, start + colWidth);
    }
  }
 
  /**
   * fills an array with a pre-defined number of lines of token data,
   * skipping blank lines in the process
   *
   * @param data
   * @throws Exception
   */
  protected void fillDataBlock(String[][] data) throws Exception {
    int nLines = data.length;
    for (int i = 0; i < nLines; i++)
      data[i] = getTokens(discardLinesUntilNonBlank());
  }

  /**
   * fills a float array with string data from a file
   * @param temp
   * @param line0 TODO
   * @param width TODO
   * @throws Exception
   */
  protected void fillFloatArray(float[] temp, String line0, int width) throws Exception {
    String[] tokens = new String[0];
    int pt = 0;
    for (int i = 0; i < temp.length; i++) {
      while (tokens != null && pt >= tokens.length) {
        if (line0 == null)
          line0 = readLine();
        if (width == 0) {
          tokens = getTokens(line0);
        } else {
          tokens = new String[line0.length() / width];
          for (int j = 0; j < tokens.length; j++)
            tokens[j] = line0.substring(j * width, (j + 1) * width);
        }
        line0 = null;
        pt = 0;
      }
      if (tokens == null)
        break;
      temp[i] = parseFloat(tokens[pt++]);
    }
  }

  /**
   * Extracts a block of frequency data from a file. This block may be of two
   * types -- either X Y Z across a row or each of X Y Z on a separate line.
   * Data is presumed to be in fixed FORTRAN-like column format, not
   * space-separated columns.
   *
   * @param iAtom0
   *          the first atom to be assigned a frequency
   * @param atomCount
   *          the number of atoms to be assigned
   * @param modelAtomCount
   *          the number of atoms in each model
   * @param ignore
   *          the frequencies to ignore because the user has selected only
   *          certain vibrations to be read or for whatever reason; length
   *          serves to set the number of frequencies to be read
   * @param isWide
   *          when TRUE, this is a table that has X Y Z for each mode within the
   *          same row; when FALSE, this is a table that has X Y Z for each mode
   *          on a separate line.
   * @param col0
   *          the column in which data starts
   * @param colWidth
   *          the width of the data columns
   * @param atomIndexes
   *          an array either null or indicating exactly which atoms get the
   *          frequencies (used by CrystalReader)
   * @throws Exception
   */
  protected void fillFrequencyData(int iAtom0, int atomCount,
                                   int modelAtomCount, boolean[] ignore,
                                   boolean isWide, int col0, int colWidth,
                                   int[] atomIndexes) throws Exception {
    boolean withSymmetry = (modelAtomCount != atomCount);
    if (atomIndexes != null)
      atomCount = atomIndexes.length;
    int nLines = (isWide ? atomCount : atomCount * 3);
    int nFreq = ignore.length;
    String[][] data = new String[nLines][];
    fillDataBlock(data, col0, colWidth);
    for (int i = 0, atomPt = 0; i < nLines; i++, atomPt++) {
      String[] values = data[i];
      String[] valuesY = (isWide ? null : data[++i]);
      String[] valuesZ = (isWide ? null : data[++i]);
      int dataPt = values.length - (isWide ? nFreq * 3 : nFreq) - 1;
      for (int j = 0; j < nFreq; j++) {
        ++dataPt;
        String x = values[dataPt];
        if (x.charAt(0) == ')') // AMPAC reader!
          x = x.substring(1);
        float vx = parseFloat(x);
        float vy = parseFloat(isWide ? values[++dataPt] : valuesY[dataPt]);
        float vz = parseFloat(isWide ? values[++dataPt] : valuesZ[dataPt]);
        if (ignore[j])
          continue;
        int iAtom = (atomIndexes == null ? atomPt : atomIndexes[atomPt]);
        if (iAtom < 0)
          continue;
        if (Logger.debugging)
          Logger.debug("vib " + iAtom + "/" + j + ": " + vx + " " + vy + " "
              + vz);
        atomSetCollection.addVibrationVector(iAtom0 + modelAtomCount * j
            + iAtom, vx, vy, vz, withSymmetry);
      }
    }
  }

  protected void discardLines(int nLines) throws Exception {
    for (int i = nLines; --i >= 0;)
      readLine();
  }

  protected String discardLinesUntilStartsWith(String startsWith) throws Exception {
    while (readLine() != null && !line.startsWith(startsWith)) {
    }
    return line;
  }

  protected String discardLinesUntilContains(String containsMatch) throws Exception {
    while (readLine() != null && line.indexOf(containsMatch) < 0) {
    }
    return line;
  }

  protected String discardLinesUntilContains(String s1, String s2)
      throws Exception {
    while (readLine() != null && line.indexOf(s1) < 0 && line.indexOf(s2) < 0) {
    }
    return line;
  }

  protected void discardLinesUntilBlank() throws Exception {
    while (readLine() != null && line.trim().length() != 0) {
    }
  }

  protected String discardLinesUntilNonBlank() throws Exception {
    while (readLine() != null && line.trim().length() == 0) {
    }
    return line;
  }

  protected void checkLineForScript(String line) {
    this.line = line;
    checkLineForScript();
  }
 
  private Point3f unitCellOffset;
 
  public void checkLineForScript() {
    if (line.indexOf("Jmol") >= 0) {
      if (line.indexOf("Jmol PDB-encoded data") >= 0) {
        atomSetCollection.setAtomSetCollectionAuxiliaryInfo("jmolData", line);
        if (!line.endsWith("#noautobond"))
          line += "#noautobond";
      }
      if (line.indexOf("Jmol data min") >= 0) {
        Logger.info(line);
        // The idea here is to use a line such as the following:
        //
        // REMARK   6 Jmol data min = {-1 -1 -1} max = {1 1 1}
        //                      unScaledXyz = xyz / {10 10 10} + {0 0 0}
        //                      plotScale = {100 100 100}
        //
        // to pass on to Jmol how to graph non-molecular data.
        // The format allows for the actual data to be linearly transformed
        // so that it fits into the PDB format for x, y, and z coordinates.
        // This adapter will then unscale the data and also pass on to
        // Jmol the unit cell equivalent that takes the actual data (which
        // will be considered the fractional coordinates) to Jmol coordinates,
        // which will be a cube centered at {0 0 0} and ranging from {-100 -100 -100}
        // to {100 100 100}.
        //
        // Jmol 12.0.RC23 uses this to pass through the adapter a quaternion,
        // ramachandran, or other sort of plot.
       
        float[] data = new float[15];
        parseStringInfestedFloatArray(line.substring(10)
            .replace('=', ' ').replace('{', ' ').replace('}',' '), data);
        Point3f minXYZ = new Point3f(data[0], data[1], data[2]);
        Point3f maxXYZ = new Point3f(data[3], data[4], data[5]);
        fileScaling = new Point3f(data[6], data[7], data[8]);
        fileOffset = new Point3f(data[9], data[10], data[11]);
        Point3f plotScale = new Point3f(data[12], data[13], data[14]);
        if (plotScale.x <= 0)
          plotScale.x = 100;
        if (plotScale.y <= 0)
          plotScale.y = 100;
        if (plotScale.z <= 0)
          plotScale.z = 100;
        if (fileScaling.y == 0)
          fileScaling.y = 1;
        if (fileScaling.z == 0)
          fileScaling.z = 1;
        setFractionalCoordinates(true);
        latticeCells = new int[3];
        atomSetCollection.setLatticeCells(latticeCells, true, false);
        setUnitCell(plotScale.x * 2 / (maxXYZ.x - minXYZ.x),
            plotScale.y * 2 / (maxXYZ.y - minXYZ.y),
            plotScale.z * 2 / (maxXYZ.z == minXYZ.z ? 1 : maxXYZ.z - minXYZ.z),
            90, 90, 90);
        /*
        unitCellOffset = new Point3f(minXYZ);
        symmetry.toCartesian(unitCellOffset);
        System.out.println(unitCellOffset);
        unitCellOffset = new Point3f(maxXYZ);
        symmetry.toCartesian(unitCellOffset);
        System.out.println(unitCellOffset);
        */
        unitCellOffset = new Point3f(plotScale);
        unitCellOffset.scale(-1);
        symmetry.toFractional(unitCellOffset, false);
        unitCellOffset.scaleAdd(-1f, minXYZ, unitCellOffset);
        symmetry.setUnitCellOffset(unitCellOffset);
        /*
        Point3f pt = new Point3f(minXYZ);
        symmetry.toCartesian(pt);
        System.out.println("ASCR minXYZ " + pt);
        pt.set(maxXYZ);
        symmetry.toCartesian(pt);
        System.out.println("ASCR maxXYZ " + pt);
        */
        atomSetCollection.setAtomSetCollectionAuxiliaryInfo("jmolDataScaling",
            new Point3f[] {minXYZ, maxXYZ, plotScale});
      }
    }
    if (line.endsWith("#noautobond")) {
      line = line.substring(0, line.lastIndexOf('#')).trim();
      atomSetCollection.setNoAutoBond();
    }
    int pt = line.indexOf("jmolscript:");
    if (pt >= 0) {
      String script = line.substring(pt + 11, line.length());
      if (script.indexOf("#") >= 0) {
        script = script.substring(0, script.indexOf("#"));
      }
      addJmolScript(script);
      line = line.substring(0, pt).trim();
    }
  }

  private String previousScript; 
  protected void addJmolScript(String script) {
    Logger.info("#jmolScript: " + script);
    if (previousScript == null)
      previousScript = "";
    else if (!previousScript.endsWith(";"))
      previousScript += ";";
    previousScript += script;
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo("jmolscript",
        previousScript);
  }

  private String siteScript; 
  protected void addSiteScript(String script) {
    if (siteScript == null)
      siteScript = "";
    else if (!siteScript.endsWith(";"))
      siteScript += ";";
    siteScript += script;
    atomSetCollection.setAtomSetCollectionAuxiliaryInfo("sitescript",
        siteScript);
  }

  public String readLine() throws Exception {
    prevline = line;
    line = reader.readLine();
    if (os !=null && line != null) {
      os.write(line.getBytes());
      os.write('\n');
    }
    ptLine++;
    if (Logger.debugging)
      Logger.debug(line);
    //System.out.println("readLine " + ptLine + " " + line);
    return line;
  }

  final static protected String[] getStrings(String sinfo, int nFields, int width) {
    String[] fields = new String[nFields];
    for (int i = 0, pt = 0; i < nFields; i++, pt += width)
      fields[i] = sinfo.substring(pt, pt + width);
    return fields;
  }

  // parser functions are static, so they need notstatic counterparts
 
  protected void parseStringInfestedFloatArray(String s, float[] data) {
    Parser.parseStringInfestedFloatArray(s, null, data);
  }
  protected String[] getTokens() {
    return Parser.getTokens(line)
  }
 
  protected static void getTokensFloat(String s, float[] f, int n) {
    Parser.parseFloatArray(getTokens(s), f, n);
  }
 
  public static String[] getTokens(String s) {
    return Parser.getTokens(s)
  }
 
  protected static String[] getTokens(String s, int iStart) {
    return Parser.getTokens(s, iStart)
  }
 
  protected float parseFloat() {
    return Parser.parseFloat(line, next);
  }

  public float parseFloat(String s) {
    next[0] = 0;
    return Parser.parseFloat(s, next);
  }

  protected float parseFloat(String s, int iStart, int iEnd) {
    next[0] = iStart;
    return Parser.parseFloat(s, iEnd, next);
  }
 
  protected int parseInt() {
    return Parser.parseInt(line, next);
  }
 
  public int parseInt(String s) {
    next[0] = 0;
    return Parser.parseInt(s, next);
  }
 
  protected int parseInt(String s, int iStart) {
    next[0] = iStart;
    return Parser.parseInt(s, next);
  }
 
  protected int parseInt(String s, int iStart, int iEnd) {
    next[0] = iStart;
    return Parser.parseInt(s, iEnd, next);
  }

  protected String parseToken() {
    return Parser.parseToken(line, next);
  }
 
  protected String parseToken(String s) {
    next[0] = 0;
    return Parser.parseToken(s, next);
  }
 
  protected String parseTokenNext(String s) {
    return Parser.parseToken(s, next);
  }

  protected String parseToken(String s, int iStart, int iEnd) {
    next[0] = iStart;
    return Parser.parseToken(s, iEnd, next);
  }
 
  protected static String parseTrimmed(String s, int iStart) {
    return Parser.parseTrimmed(s, iStart);
  }
 
  protected static String parseTrimmed(String s, int iStart, int iEnd) {
    return Parser.parseTrimmed(s, iStart, iEnd);
  }

  /**
   * get all integers after letters
   * negative entries are spaces (1Xn)
   *
   * @param s
   * @return Vector of integers
   */
  protected static ArrayList getFortranFormatLengths(String s) {
    ArrayList vdata = new ArrayList();
    int n = 0;
    int c = 0;
    int factor = 1;
    boolean inN = false;
    boolean inCount = true;
    s += ",";
    for (int i = 0; i < s.length(); i++) {
      char ch = s.charAt(i);
      switch (ch) {
      case '.':
        inN = false;
        continue;
      case ',':
        for (int j = 0; j < c; j++)
          vdata.add(Integer.valueOf(n * factor));
      inN = false;
      inCount = true;
      c = 0;
      continue;
      case 'X':
        n = c;
        c = 1;
        factor = -1;
        continue;
     
      }
      boolean isDigit = Character.isDigit(ch);
      if (isDigit) {
        if (inN)
          n = n * 10 + ch - '0';
        else if (inCount)
          c = c * 10 + ch - '0';
      } else if (Character.isLetter(ch)) {
        n = 0;
        inN = true;
        inCount = false;
        factor = 1;
      } else {
        inN = false;
      }
    }
    return vdata;
  }

}
TOP

Related Classes of org.jmol.adapter.smarter.AtomSetCollectionReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.