1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2006-10-22 14:12:46 -0500 (Sun, 22 Oct 2006) $ 4 * $Revision: 5999 $ 5 * 6 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 25 package org.jmol.adapter.smarter; 26 27 28 import java.io.BufferedReader; 29 import java.util.Map; 30 31 import javajs.api.GenericBinaryDocument; 32 import javajs.api.GenericLineReader; 33 import javajs.util.Lst; 34 import javajs.util.M3; 35 import javajs.util.M4; 36 import javajs.util.OC; 37 import javajs.util.P3; 38 import javajs.util.PT; 39 import javajs.util.Quat; 40 import javajs.util.SB; 41 import javajs.util.T3; 42 import javajs.util.T4; 43 import javajs.util.V3; 44 45 import org.jmol.api.Interface; 46 import org.jmol.api.JmolAdapter; 47 import org.jmol.api.SymmetryInterface; 48 import javajs.util.BS; 49 import org.jmol.script.SV; 50 import org.jmol.symmetry.Symmetry; 51 import org.jmol.util.BSUtil; 52 import org.jmol.util.Logger; 53 import org.jmol.viewer.Viewer; 54 55 56 /* 57 * Notes 9/2006 Bob Hanson 58 * 59 * all parsing functions now moved to org.jmol.util.Parser 60 * 61 * to add symmetry capability to any reader, some or all of the following 62 * methods need to be there: 63 * 64 * setFractionalCoordinates() 65 * setSpaceGroupName() 66 * setUnitCell() 67 * setUnitCellItem() 68 * setAtomCoord() 69 * 70 * At the very minimum, you need: 71 * 72 * setAtomCoord() 73 * 74 * so that: 75 * (a) atom coordinates can be turned fractional by load parameters 76 * (b) symmetry can be applied once per model in the file 77 * 78 * If you know where the end of the atom+bond data are, then you can 79 * use applySymmetryAndSetTrajectory() once, just before exiting. Otherwise, use it 80 * twice -- it has a check to make sure it doesn't RUN twice -- once 81 * at the beginning and once at the end of the model. 82 * 83 * htParams is used for passing information to the readers 84 * and for returning information from the readers 85 * 86 * It won't be null at this stage. 87 * 88 * from Eval or Viewer: 89 * 90 * applySymmetryToBonds 91 * atomTypes (for Mol2Reader) 92 * bsModels 93 * filter 94 * firstLastStep 95 * firstLastSteps 96 * getHeader 97 * isTrajectory 98 * lattice 99 * manifest (for SmarterJmolAdapter) 100 * modelNumber 101 * spaceGroupIndex 102 * symmetryRange 103 * unitcell 104 * packed 105 * 106 * from FileManager: 107 * 108 * fullPathName 109 * subFileList (for SmarterJmolAdapter) 110 * 111 * from MdTopReader: 112 * 113 * isPeriodic 114 * templateAtomCount 115 * 116 * from MdCrdReader: 117 * 118 * trajectorySteps 119 * 120 * from Resolver: 121 * 122 * filteredAtomCount 123 * ptFile 124 * readerName 125 * templateAtomCount 126 * 127 * 128 * from AtomSetCollectionReader: 129 * 130 * bsFilter 131 * 132 * 133 */ 134 135 public abstract class AtomSetCollectionReader implements GenericLineReader { 136 137 public final static float ANGSTROMS_PER_BOHR = 0.5291772f; // used by SpartanArchive 138 139 public boolean isBinary; 140 public boolean debugging; 141 protected boolean requiresBSFilter; 142 143 public M3 primitiveToCrystal; 144 145 public AtomSetCollection asc; 146 protected BufferedReader reader; 147 protected GenericBinaryDocument binaryDoc; 148 protected String readerName; 149 public Map<String, Object> htParams; 150 public Lst<P3[]> trajectorySteps; 151 private Object domains; 152 public Object validation, dssr; 153 protected boolean isConcatenated; 154 public String addedData, addedDataKey; 155 public boolean fixJavaFloat = true; 156 public Map<String, Object> thisBiomolecule; 157 public Lst<M4> lstNCS; 158 159 //protected String parameterData; 160 161 // buffer 162 public String line, prevline; 163 protected int[] next = new int[1]; 164 protected int ptLine; 165 166 // protected/public state variables 167 168 protected String latticeType; 169 public int[] latticeCells; 170 public Object fillRange; 171 public boolean doProcessLines; 172 public boolean iHaveUnitCell; 173 public boolean iHaveSymmetryOperators; 174 public boolean continuing = true; 175 176 public Viewer vwr; // used by GenNBOReader and by CifReader 177 178 public boolean doApplySymmetry; 179 protected boolean ignoreFileSymmetryOperators; 180 protected boolean isTrajectory; 181 public boolean applySymmetryToBonds; 182 protected boolean doCheckUnitCell; 183 protected boolean getHeader; 184 protected boolean isSequential; 185 public boolean isMolecular; // only for CIF so that it can read multiple unit cells 186 protected int templateAtomCount; 187 public int modelNumber; 188 public int vibrationNumber; 189 public int desiredVibrationNumber = Integer.MIN_VALUE; 190 protected BS bsModels; 191 protected boolean useFileModelNumbers; // PDB, MMCIF only 192 protected boolean havePartialChargeFilter; 193 public String calculationType = "?"; 194 protected String sgName; 195 protected boolean ignoreFileUnitCell; 196 protected boolean ignoreFileSpaceGroupName; 197 public float[] unitCellParams; //0-5 a b c alpha beta gamma; 6-21 matrix c->f 198 protected int desiredModelNumber = Integer.MIN_VALUE; 199 public SymmetryInterface symmetry; 200 protected OC out; 201 protected boolean iHaveFractionalCoordinates; 202 public boolean doPackUnitCell; 203 protected P3 ptSupercell; 204 protected boolean mustFinalizeModelSet; 205 protected boolean forcePacked; 206 public float packingError = 0.02f; 207 protected boolean rotateHexCell; // aflow CIF reader only 208 protected boolean isPrimitive; // VASP POSCAR reasder 209 public int modDim; // modulation dimension 210 211 212 // private state variables 213 214 private SB loadNote = new SB(); 215 public boolean doConvertToFractional; 216 boolean fileCoordinatesAreFractional; 217 boolean merging; 218 float symmetryRange; 219 private int[] firstLastStep; 220 private int lastModelNumber = Integer.MAX_VALUE; 221 public int desiredSpaceGroupIndex = -1; 222 protected P3 fileScaling; 223 protected float latticeScaling = Float.NaN; 224 protected P3 fileOffset; 225 private P3 fileOffsetFractional; 226 protected P3 unitCellOffset; 227 private boolean unitCellOffsetFractional; 228 private Lst<String> moreUnitCellInfo; 229 public T3 paramsLattice; 230 public boolean paramsCentroid; 231 private boolean paramsPacked; 232 233 234 protected String filePath; 235 protected String fileName; 236 237 /** 238 * first atom index for this collection, current modelset.ac 239 */ 240 public int baseAtomIndex; 241 242 public int baseBondIndex; 243 244 protected int stateScriptVersionInt = Integer.MAX_VALUE; // for compatibility PDB reader Jmol 12.0.RC24 fix 245 // http://jmol.svn.sourceforge.net/viewvc/jmol/trunk/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java?r1=13502&r2=13525 246 setup(String fullPath, Map<String, Object> htParams, Object readerOrDocument)247 protected void setup(String fullPath, Map<String, Object> htParams, Object readerOrDocument) { 248 setupASCR(fullPath, htParams, readerOrDocument); 249 } 250 setupASCR(String fullPath, Map<String, Object> htParams, Object readerOrDocument)251 protected void setupASCR(String fullPath, Map<String, Object> htParams, Object readerOrDocument) { 252 if (fullPath == null) 253 return; 254 debugging = Logger.debugging; 255 this.htParams = htParams; 256 filePath = "" + htParams.get("fullPathName"); 257 int i = filePath.lastIndexOf('/'); 258 fileName = filePath.substring(i + 1); 259 if (readerOrDocument instanceof BufferedReader) 260 this.reader = (BufferedReader) readerOrDocument; 261 else if (readerOrDocument instanceof GenericBinaryDocument) 262 binaryDoc = (GenericBinaryDocument) readerOrDocument; 263 } 264 readData()265 Object readData() throws Exception { 266 initialize(); 267 asc = new AtomSetCollection(readerName, this, null, null); 268 try { 269 initializeReader(); 270 if (binaryDoc == null) { 271 if (line == null && continuing) 272 rd(); 273 while (line != null && continuing) 274 if (checkLine()) 275 rd(); 276 } else { 277 binaryDoc.setOutputChannel(out); 278 processBinaryDocument(); 279 } 280 finalizeSubclassReader(); // upstairs 281 if (!isFinalized) 282 finalizeReaderASCR(); 283 } catch (Throwable e) { 284 Logger.info("Reader error: " + e); 285 e.printStackTrace(); 286 setError(e); 287 } 288 if (reader != null) 289 reader.close(); 290 if (binaryDoc != null) 291 binaryDoc.close(); 292 return finish(); 293 } 294 fixBaseIndices()295 private void fixBaseIndices() { 296 try { 297 int baseModelIndex = ((Integer) htParams.get("baseModelIndex")).intValue(); 298 baseAtomIndex += asc.ac; 299 baseBondIndex += asc.bondCount; 300 baseModelIndex += asc.atomSetCount; 301 htParams.put("baseAtomIndex", Integer.valueOf(baseAtomIndex)); 302 htParams.put("baseBondIndex", Integer.valueOf(baseBondIndex)); 303 htParams.put("baseModelIndex", Integer.valueOf(baseModelIndex)); 304 } catch (Exception e) { 305 // ignore 306 } 307 } 308 readDataObject(Object node)309 protected Object readDataObject(Object node) throws Exception { 310 initialize(); 311 asc = new AtomSetCollection(readerName, this, null, null); 312 initializeReader(); 313 processDOM(node); 314 return finish(); 315 } 316 317 /** 318 * 319 * @param DOMNode 320 */ processDOM(Object DOMNode)321 protected void processDOM(Object DOMNode) { 322 // XML readers only 323 } 324 325 /** 326 * @throws Exception 327 */ processBinaryDocument()328 protected void processBinaryDocument() throws Exception { 329 // Binary readers only 330 } 331 initializeReader()332 protected void initializeReader() throws Exception { 333 // reader-dependent 334 } 335 336 /** 337 * @return true if need to read new line 338 * @throws Exception 339 * 340 */ checkLine()341 protected boolean checkLine() throws Exception { 342 // reader-dependent 343 return true; 344 } 345 346 /** 347 * sets continuing and doProcessLines 348 * 349 * @return TRUE if continuing, FALSE if not 350 * 351 */ checkLastModel()352 public boolean checkLastModel() { 353 if (isLastModel(modelNumber) && doProcessLines) 354 return (continuing = doProcessLines = false); 355 doProcessLines = false; 356 return true; 357 } 358 359 /** 360 * after reading a model, Q: Is this the last model? 361 * 362 * @param modelNumber 363 * @return Yes/No 364 */ isLastModel(int modelNumber)365 public boolean isLastModel(int modelNumber) { 366 return (desiredModelNumber > 0 || modelNumber >= lastModelNumber); 367 } 368 appendLoadNote(String info)369 public void appendLoadNote(String info) { 370 if (info == null) { 371 loadNote = new SB(); 372 return; 373 } 374 loadNote.append(info).append("\n"); 375 Logger.info(info); 376 } 377 378 @SuppressWarnings("unchecked") initializeTrajectoryFile()379 protected void initializeTrajectoryFile() { 380 // add a dummy atom, just so not "no atoms found" 381 asc.addAtom(new Atom()); 382 trajectorySteps = (Lst<P3[]>) htParams.get("trajectorySteps"); 383 if (trajectorySteps == null) 384 htParams.put("trajectorySteps", trajectorySteps = new Lst<P3[]>()); 385 } 386 387 /** 388 * optional reader-specific method run first. 389 * @throws Exception 390 */ finalizeSubclassReader()391 protected void finalizeSubclassReader() throws Exception { 392 // can be customized 393 } 394 395 protected boolean isFinalized; 396 finalizeReaderASCR()397 protected void finalizeReaderASCR() throws Exception { 398 isFinalized = true; 399 if (asc.atomSetCount > 0) { 400 if (asc.atomSetCount == 1) { 401 asc.setCurrentModelInfo("dbName", htParams.get("dbName")); 402 asc.setCurrentModelInfo("auxFiles", htParams.get("auxFiles")); 403 } 404 applySymmetryAndSetTrajectory(); 405 asc.finalizeStructures(); 406 if (doCentralize) 407 asc.centralize(); 408 if (fillRange != null)// && previousUnitCell == null) 409 asc.setInfo("boundbox", fillRange); 410 Map<String, Object> info = asc.getAtomSetAuxiliaryInfo(0); 411 if (info != null) { 412 if (domains != null) { 413 asc.setGlobalBoolean(AtomSetCollection.GLOBAL_DOMAINS); 414 String s = ((SV) domains).getMapKeys(2, true); 415 int pt = s.indexOf("{ ", 2); 416 if (pt >= 0) 417 s = s.substring(pt + 2); 418 pt = s.indexOf("_metadata"); 419 if (pt < 0) 420 pt = s.indexOf("metadata"); 421 if (pt >= 0) 422 s = s.substring(0, pt); 423 s = PT.rep(PT.replaceAllCharacters(s, "{}", "").trim(), "\n", "\n ") 424 + "\n\nUse SHOW DOMAINS for details."; 425 appendLoadNote("\nDomains loaded:\n " + s); 426 for (int i = asc.atomSetCount; --i >= 0;) { 427 info = asc.getAtomSetAuxiliaryInfo(i); 428 info.put("domains", domains); 429 } 430 } 431 if (validation != null) { 432 for (int i = asc.atomSetCount; --i >= 0;) { 433 info = asc.getAtomSetAuxiliaryInfo(i); 434 info.put("validation", validation); 435 } 436 } 437 if (dssr != null) { 438 info.put("dssrJSON", Boolean.TRUE); 439 for (int i = asc.atomSetCount; --i >= 0;) { 440 info = asc.getAtomSetAuxiliaryInfo(i); 441 info.put("dssr", dssr); 442 } 443 } 444 } 445 } 446 if (!fixJavaFloat) 447 asc.setInfo("legacyJavaFloat", Boolean.TRUE); 448 setLoadNote(); 449 } 450 451 ///////////////////////////////////////////////////////////////////////////////////// 452 setLoadNote()453 protected String setLoadNote() { 454 String s = loadNote.toString(); 455 if (loadNote.length() > 0) 456 asc.setInfo("modelLoadNote", s); 457 return s; 458 } 459 setIsPDB()460 public void setIsPDB() { 461 asc.setGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB); 462 if (htParams.get("pdbNoHydrogens") != null) 463 asc.setInfo("pdbNoHydrogens", 464 htParams.get("pdbNoHydrogens")); 465 if (checkFilterKey("ADDHYDROGENS")) 466 asc.setInfo("pdbAddHydrogens",Boolean.TRUE); 467 } 468 setModelPDB(boolean isPDB)469 protected void setModelPDB(boolean isPDB) { 470 if (isPDB) 471 asc.setGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB); 472 else 473 asc.clearGlobalBoolean(AtomSetCollection.GLOBAL_ISPDB); 474 asc.setCurrentModelInfo("isPDB", isPDB ? Boolean.TRUE : null); 475 } 476 finish()477 private Object finish() { 478 String s = (String) htParams.get("loadState"); 479 asc.setInfo("loadState", 480 s == null ? "" : s); 481 s = (String) htParams.get("smilesString"); 482 if (s != null) 483 asc.setInfo("smilesString", s); 484 if (!htParams.containsKey("templateAtomCount")) 485 htParams.put("templateAtomCount", Integer.valueOf(asc 486 .ac)); 487 if (bsFilter != null) { 488 htParams.put("filteredAtomCount", Integer.valueOf(BSUtil 489 .cardinalityOf(bsFilter))); 490 htParams.put("bsFilter", bsFilter); 491 } 492 if (!calculationType.equals("?")) 493 asc.setInfo("calculationType", 494 calculationType); 495 496 String name = asc.fileTypeName; 497 String fileType = name; 498 if (fileType.indexOf("(") >= 0) 499 fileType = fileType.substring(0, fileType.indexOf("(")); 500 for (int i = asc.atomSetCount; --i >= 0;) { 501 asc.setModelInfoForSet("fileName", filePath, i); 502 asc.setModelInfoForSet("fileType", fileType, i); 503 } 504 asc.freeze(reverseModels); 505 if (asc.errorMessage != null) 506 return asc.errorMessage + "\nfor file " + filePath 507 + "\ntype " + name; 508 if ((asc.bsAtoms == null ? asc.ac == 0 509 : asc.bsAtoms.nextSetBit(0) < 0) 510 && fileType.indexOf("DataOnly") < 0 && asc.atomSetInfo.get("dataOnly") == null) 511 return "No atoms found\nfor file " + filePath + "\ntype " + name; 512 fixBaseIndices(); 513 return asc; 514 } 515 516 /** 517 * @param e 518 */ setError(Throwable e)519 private void setError(Throwable e) { 520 String s = e.getMessage(); 521 if (line == null) 522 asc.errorMessage = "Error reading file at end of file \n" + s; 523 else 524 asc.errorMessage = "Error reading file at line " + ptLine + ":\n" + line 525 + "\n" + s; 526 e.printStackTrace(); 527 } 528 529 @SuppressWarnings("unchecked") initialize()530 private void initialize() { 531 if (htParams.containsKey("baseAtomIndex")) 532 baseAtomIndex = ((Integer) htParams.get("baseAtomIndex")).intValue(); 533 if (htParams.containsKey("baseBondIndex")) 534 baseBondIndex = ((Integer) htParams.get("baseBondIndex")).intValue(); 535 initializeSymmetry(); 536 vwr = (Viewer) htParams.remove("vwr"); // don't pass this on to user 537 if (htParams.containsKey("stateScriptVersionInt")) 538 stateScriptVersionInt = ((Integer) htParams.get("stateScriptVersionInt")) 539 .intValue(); 540 Object o = htParams.get("packingError"); 541 if (o != null) 542 packingError = ((Float) o).floatValue(); 543 else if (htParams.get("legacyJavaFloat") != null) { 544 // earlier versions were not fully JavaScript compatible 545 // because XtalSymmetry.isWithinUnitCell was giving different answers 546 // for floats (Java) as for doubles (JavaScript). 547 fixJavaFloat = false; 548 } 549 merging = htParams.containsKey("merging"); 550 getHeader = htParams.containsKey("getHeader"); 551 isSequential = htParams.containsKey("isSequential"); 552 readerName = (String) htParams.get("readerName"); 553 if (htParams.containsKey("outputChannel")) 554 out = (OC) htParams.get("outputChannel"); 555 //parameterData = (String) htParams.get("parameterData"); 556 if (htParams.containsKey("vibrationNumber")) 557 desiredVibrationNumber = ((Integer) htParams.get("vibrationNumber")) 558 .intValue(); 559 else if (htParams.containsKey("modelNumber")) 560 desiredModelNumber = ((Integer) htParams.get("modelNumber")).intValue(); 561 applySymmetryToBonds = htParams.containsKey("applySymmetryToBonds"); 562 bsFilter = (requiresBSFilter ? (BS) htParams.get("bsFilter") : null); 563 setFilter(null); 564 fillRange = htParams.get("fillRange"); 565 if (strSupercell != null) { 566 if (!checkFilterKey("NOPACK")) 567 forcePacked = true; 568 } 569 o = htParams.get("supercell"); 570 if (o instanceof P3) { 571 P3 s = ptSupercell = (P3) o; // only used by CASTEP phonon reader now 572 strSupercell = ((int) s.x) + "a," +((int) s.y) + "b," + ((int) s.z) + "c"; 573 } else if (o instanceof String) { 574 strSupercell = (String) o; 575 } 576 // ptFile < 0 indicates just one file being read 577 // ptFile >= 0 indicates multiple files are being loaded 578 // if the file is not the first read in the LOAD command, then 579 // we look to see if it was loaded using LOAD ... "..." COORD .... 580 int ptFile = (htParams.containsKey("ptFile") ? ((Integer) htParams 581 .get("ptFile")).intValue() : -1); 582 isTrajectory = htParams.containsKey("isTrajectory"); 583 if (ptFile > 0 && htParams.containsKey("firstLastSteps")) { 584 Object val = ((Lst<Object>) htParams.get("firstLastSteps")) 585 .get(ptFile - 1); 586 if (val instanceof BS) { 587 bsModels = (BS) val; 588 } else { 589 firstLastStep = (int[]) val; 590 } 591 } else if (htParams.containsKey("firstLastStep")) { 592 firstLastStep = (int[]) htParams.get("firstLastStep"); 593 } else if (htParams.containsKey("bsModels")) { 594 bsModels = (BS) htParams.get("bsModels"); 595 } 596 useFileModelNumbers = htParams.containsKey("useFileModelNumbers") || checkFilterKey("USEFILEMODELNUMBERS"); 597 if (htParams.containsKey("templateAtomCount")) 598 templateAtomCount = ((Integer) htParams.get("templateAtomCount")) 599 .intValue(); 600 if (bsModels != null || firstLastStep != null) 601 desiredModelNumber = Integer.MIN_VALUE; 602 if (bsModels == null && firstLastStep != null) { 603 if (firstLastStep[0] < 0) 604 firstLastStep[0] = 0; 605 if (firstLastStep[2] == 0 || firstLastStep[1] < firstLastStep[0]) 606 firstLastStep[1] = -1; 607 if (firstLastStep[2] < 1) 608 firstLastStep[2] = 1; 609 bsModels = BSUtil.newAndSetBit(firstLastStep[0]); 610 if (firstLastStep[1] > firstLastStep[0]) { 611 for (int i = firstLastStep[0]; i <= firstLastStep[1]; i += firstLastStep[2]) 612 bsModels.set(i); 613 } 614 } 615 if (bsModels != null && (firstLastStep == null || firstLastStep[1] != -1)) 616 lastModelNumber = bsModels.length(); 617 618 symmetryRange = (htParams.containsKey("symmetryRange") ? ((Float) htParams 619 .get("symmetryRange")).floatValue() : 0); 620 paramsLattice = (T3) htParams.get("lattice"); 621 paramsCentroid = htParams.containsKey("centroid"); 622 paramsPacked = htParams.containsKey("packed"); 623 initializeSymmetryOptions(); 624 //this flag FORCES symmetry -- generally if coordinates are not fractional, 625 //we may note the unit cell, but we do not apply symmetry 626 //with this flag, we convert any nonfractional coordinates to fractional 627 //if a unit cell is available. 628 629 if (htParams.containsKey("spaceGroupIndex")) { 630 // three options include: 631 // = -1: normal -- use operators if present or name if not 632 // = -2: user is supplying operators or name 633 // >=0: spacegroup fully determined 634 // = -999: ignore -- just the operators 635 636 desiredSpaceGroupIndex = ((Integer) htParams.get("spaceGroupIndex")) 637 .intValue(); 638 if (desiredSpaceGroupIndex == -2) 639 sgName = (String) htParams.get("spaceGroupName"); 640 ignoreFileSpaceGroupName = (desiredSpaceGroupIndex == -2 || desiredSpaceGroupIndex >= 0); 641 ignoreFileSymmetryOperators = (desiredSpaceGroupIndex != -1); 642 } 643 if (htParams.containsKey("unitCellOffset")) { 644 fileScaling = P3.new3(1, 1, 1); 645 fileOffset = (P3) htParams.get("unitCellOffset"); 646 fileOffsetFractional = P3.newP(fileOffset); 647 unitCellOffsetFractional = htParams 648 .containsKey("unitCellOffsetFractional"); 649 } 650 if (htParams.containsKey("unitcell")) { 651 float[] fParams = (float[]) htParams.get("unitcell"); 652 if (merging) 653 setFractionalCoordinates(true); 654 if (fParams.length == 9) { 655 // these are vectors 656 addExplicitLatticeVector(0, fParams, 0); 657 addExplicitLatticeVector(1, fParams, 3); 658 addExplicitLatticeVector(2, fParams, 6); 659 } else { 660 setUnitCell(fParams[0], fParams[1], fParams[2], fParams[3], fParams[4], 661 fParams[5]); 662 } 663 ignoreFileUnitCell = iHaveUnitCell; 664 if (merging && !iHaveUnitCell) 665 setFractionalCoordinates(false); 666 // with appendNew == false and UNITCELL parameter, we assume fractional coordinates 667 } 668 domains = htParams.get("domains"); 669 validation = htParams.get("validation"); 670 dssr = htParams.get("dssr"); 671 isConcatenated = htParams.containsKey("concatenate"); 672 } 673 initializeSymmetryOptions()674 protected void initializeSymmetryOptions() { 675 latticeCells = new int[4]; 676 doApplySymmetry = false; 677 T3 pt = paramsLattice; 678 if (pt == null || pt.length() == 0) { 679 if (!forcePacked && strSupercell == null) 680 return; 681 pt = P3.new3(1, 1, 1); 682 } 683 latticeCells[0] = (int) pt.x; 684 latticeCells[1] = (int) pt.y; 685 latticeCells[2] = (int) pt.z; 686 if (pt instanceof T4) 687 latticeCells[3] = (int) ((T4) pt).w; 688 doCentroidUnitCell = paramsCentroid; 689 if (doCentroidUnitCell && (latticeCells[2] == -1 || latticeCells[2] == 0)) 690 latticeCells[2] = 1; 691 boolean isPacked = forcePacked || paramsPacked; 692 centroidPacked = doCentroidUnitCell && isPacked; 693 doPackUnitCell = !doCentroidUnitCell && (isPacked || latticeCells[2] < 0); 694 doApplySymmetry = (latticeCells[0] > 0 && latticeCells[1] > 0); 695 //allows for {1 1 1} or {1 1 -1} or {555 555 0|1|-1} (-1 being "packed") 696 if (!doApplySymmetry) 697 latticeCells = new int[3]; 698 } 699 700 protected boolean haveModel; 701 doGetModel(int modelNumber, String title)702 public boolean doGetModel(int modelNumber, String title) { 703 if (title != null && nameRequired != null && nameRequired.length() > 0 704 && title.toUpperCase().indexOf(nameRequired) < 0) 705 return false; 706 // modelNumber is 1-based, but firstLastStep is 0-based 707 boolean isOK = (bsModels == null ? desiredModelNumber < 1 708 || modelNumber == desiredModelNumber 709 : modelNumber > lastModelNumber ? false : modelNumber > 0 710 && bsModels.get(modelNumber - 1) 711 || haveModel 712 && firstLastStep != null 713 && firstLastStep[1] < 0 714 && (firstLastStep[2] < 2 || (modelNumber - 1 - firstLastStep[0]) 715 % firstLastStep[2] == 0)); 716 if (isOK && desiredModelNumber == 0) 717 discardPreviousAtoms(); 718 haveModel |= isOK; 719 if (isOK) 720 doProcessLines = true; 721 return isOK; 722 } 723 discardPreviousAtoms()724 protected void discardPreviousAtoms() { 725 asc.discardPreviousAtoms(); 726 } 727 728 private String previousSpaceGroup; 729 private float[] previousUnitCell; 730 initializeSymmetry()731 protected final void initializeSymmetry() { 732 previousSpaceGroup = sgName; 733 previousUnitCell = unitCellParams; 734 iHaveUnitCell = ignoreFileUnitCell; 735 if (!ignoreFileUnitCell) { 736 unitCellParams = new float[26]; 737 //0-5 a b c alpha beta gamma 738 //6-21 m00 m01... m33 cartesian-->fractional 739 //22-24 supercell.x supercell.y supercell.z 740 //25 scaling 741 for (int i = 25; --i >= 0;) 742 unitCellParams[i] = Float.NaN; 743 unitCellParams[25] = latticeScaling; 744 symmetry = null; 745 } 746 if (!ignoreFileSpaceGroupName) 747 sgName = "unspecified!"; 748 doCheckUnitCell = false; 749 } 750 newAtomSet(String name)751 protected void newAtomSet(String name) { 752 if (asc.iSet >= 0) { 753 asc.newAtomSet(); 754 asc.setCollectionName("<collection of " 755 + (asc.iSet + 1) + " models>"); 756 } else { 757 asc.setCollectionName(name); 758 } 759 asc.setModelInfoForSet("name", name, Math.max(0, asc.iSet)); 760 asc.setAtomSetName(name); 761 } 762 cloneLastAtomSet(int ac, P3[] pts)763 protected int cloneLastAtomSet(int ac, P3[] pts) throws Exception { 764 int lastAtomCount = asc.getLastAtomSetAtomCount(); 765 asc.cloneLastAtomSetFromPoints(ac, pts); 766 if (asc.haveUnitCell) { 767 iHaveUnitCell = true; 768 doCheckUnitCell = true; 769 sgName = previousSpaceGroup; 770 unitCellParams = previousUnitCell; 771 } 772 return lastAtomCount; 773 } 774 setSpaceGroupName(String name)775 public void setSpaceGroupName(String name) { 776 if (ignoreFileSpaceGroupName || name == null) 777 return; 778 String s = name.trim(); 779 if (s.length() == 0 || s.equals("HM:") || s.equals(sgName)) 780 return; 781 if (!s.equals("P1")) 782 Logger.info("Setting space group name to " + s); 783 sgName = s; 784 } 785 setSymmetryOperator(String xyz)786 public int setSymmetryOperator(String xyz) { 787 if (ignoreFileSymmetryOperators) 788 return -1; 789 int isym = asc.getXSymmetry().addSpaceGroupOperation(xyz, true); 790 if (isym < 0) 791 Logger.warn("Skippings symmetry operation " + xyz); 792 iHaveSymmetryOperators = true; 793 return isym; 794 } 795 796 private int nMatrixElements = 0; 797 initializeCartesianToFractional()798 private void initializeCartesianToFractional() { 799 for (int i = 0; i < 16; i++) 800 if (!Float.isNaN(unitCellParams[6 + i])) 801 return; //just do this once 802 for (int i = 0; i < 16; i++) 803 unitCellParams[6 + i] = ((i % 5 == 0 ? 1 : 0)); 804 nMatrixElements = 0; 805 } 806 clearUnitCell()807 public void clearUnitCell() { 808 if (ignoreFileUnitCell) 809 return; 810 for (int i = 6; i < 22; i++) 811 unitCellParams[i] = Float.NaN; 812 checkUnitCell(6); 813 } 814 815 public float[] ucItems; setUnitCellItem(int i, float x)816 public void setUnitCellItem(int i, float x) { 817 if (ignoreFileUnitCell) 818 return; 819 if (i == 0 && x == 1 && !allow_a_len_1 || i == 3 && x == 0) { 820 if (ucItems == null) 821 ucItems = new float[6]; 822 ucItems[i] = x; 823 return; 824 } 825 if (ucItems != null && i < 6) 826 ucItems[i] = x; 827 828 if (!Float.isNaN(x) && i >= 6 && Float.isNaN(unitCellParams[6])) 829 initializeCartesianToFractional(); 830 unitCellParams[i] = x; 831 if (debugging) { 832 Logger.debug("setunitcellitem " + i + " " + x); 833 } 834 if (i < 6 || Float.isNaN(x)) 835 iHaveUnitCell = checkUnitCell(6); 836 else if (++nMatrixElements == 12) 837 iHaveUnitCell = checkUnitCell(22); 838 } 839 840 protected M3 matUnitCellOrientation; 841 setUnitCell(float a, float b, float c, float alpha, float beta, float gamma)842 public void setUnitCell(float a, float b, float c, float alpha, float beta, 843 float gamma) { 844 if (ignoreFileUnitCell) 845 return; 846 clearUnitCell(); 847 unitCellParams[0] = a; 848 unitCellParams[1] = b; 849 unitCellParams[2] = c; 850 if (alpha != 0) 851 unitCellParams[3] = alpha; 852 if (beta != 0) 853 unitCellParams[4] = beta; 854 if (gamma != 0) 855 unitCellParams[5] = gamma; 856 iHaveUnitCell = checkUnitCell(6); 857 } 858 addExplicitLatticeVector(int i, float[] xyz, int i0)859 public void addExplicitLatticeVector(int i, float[] xyz, int i0) { 860 if (ignoreFileUnitCell) 861 return; 862 if (i == 0) 863 for (int j = 0; j < 6; j++) 864 unitCellParams[j] = 0; 865 i = 6 + i * 3; 866 unitCellParams[i++] = xyz[i0++]; 867 unitCellParams[i++] = xyz[i0++]; 868 unitCellParams[i] = xyz[i0]; 869 if (Float.isNaN(unitCellParams[0])) { 870 for (i = 0; i < 6; i++) 871 unitCellParams[i] = -1; 872 } 873 iHaveUnitCell = checkUnitCell(15); 874 } 875 checkUnitCell(int n)876 private boolean checkUnitCell(int n) { 877 for (int i = 0; i < n; i++) 878 if (Float.isNaN(unitCellParams[i])) 879 return false; 880 if (n == 22 && unitCellParams[0] == 1) { 881 if (unitCellParams[1] == 1 882 && unitCellParams[2] == 1 883 && unitCellParams[6] == 1 884 && unitCellParams[11] == 1 885 && unitCellParams[16] == 1 886 ) 887 return false; 888 // this is an mmCIF or PDB case for NMR models having 889 // CRYST1 1.000 1.000 1.000 90.00 90.00 90.00 P 1 1 890 // ORIGX1 1.000000 0.000000 0.000000 0.00000 891 // ORIGX2 0.000000 1.000000 0.000000 0.00000 892 // ORIGX3 0.000000 0.000000 1.000000 0.00000 893 // SCALE1 1.000000 0.000000 0.000000 0.00000 894 // SCALE2 0.000000 1.000000 0.000000 0.00000 895 // SCALE3 0.000000 0.000000 1.000000 0.00000 896 } 897 if (doApplySymmetry) { 898 getSymmetry(); 899 doConvertToFractional = !fileCoordinatesAreFractional; 900 } 901 //if (but not only if) applying symmetry do we force conversion 902 // checkUnitCellOffset(); 903 return true; 904 } 905 getSymmetry()906 public SymmetryInterface getSymmetry() { 907 if (!iHaveUnitCell) 908 return null; 909 if (symmetry == null) { 910 getNewSymmetry().setUnitCell(unitCellParams, false); 911 checkUnitCellOffset(); 912 } 913 if (symmetry == null) // cif file with no symmetry triggers exception on LOAD {1 1 1} 914 iHaveUnitCell = false; 915 else 916 symmetry.setSpaceGroupName(sgName); 917 return symmetry; 918 } checkUnitCellOffset()919 private void checkUnitCellOffset() { 920 if (fileOffsetFractional == null || symmetry == null) 921 return; 922 fileOffset.setT(fileOffsetFractional); 923 if (unitCellOffsetFractional != fileCoordinatesAreFractional) { 924 if (unitCellOffsetFractional) 925 symmetry.toCartesian(fileOffset, false); 926 else 927 symmetry.toFractional(fileOffset, false); 928 } 929 } 930 fractionalizeCoordinates(boolean toFrac)931 protected void fractionalizeCoordinates(boolean toFrac) { 932 if (getSymmetry() == null) 933 return; 934 Atom[] a = asc.atoms; 935 if (toFrac) 936 for (int i = asc.ac; --i >= 0;) 937 symmetry.toFractional(a[i], false); 938 else 939 for (int i = asc.ac; --i >= 0;) 940 symmetry.toCartesian(a[i], false); 941 setFractionalCoordinates(toFrac); 942 } getNewSymmetry()943 protected SymmetryInterface getNewSymmetry() { 944 return symmetry = (Symmetry) getInterface("org.jmol.symmetry.Symmetry"); 945 } 946 setFractionalCoordinates(boolean TF)947 public void setFractionalCoordinates(boolean TF) { 948 iHaveFractionalCoordinates = fileCoordinatesAreFractional = TF; 949 checkUnitCellOffset(); 950 } 951 952 /////////// FILTER ///////////////// 953 954 protected BS bsFilter; 955 public String filter; 956 public boolean haveAtomFilter; 957 private boolean filterAltLoc; 958 private boolean filterGroup3; 959 private boolean filterChain; 960 private boolean filterAtomName; 961 private boolean filterAtomType; 962 private String filterAtomTypeStr; 963 private String filterAtomNameTerminator = ";"; 964 private boolean filterElement; 965 protected boolean filterHetero; 966 private boolean filterEveryNth; 967 String filterSymop; 968 private int filterN; 969 private int nFiltered; 970 private boolean doSetOrientation; 971 protected boolean doCentralize; 972 protected boolean addVibrations; 973 protected boolean useAltNames; 974 protected boolean ignoreStructure; 975 protected boolean isDSSP1; 976 protected boolean allowPDBFilter; 977 public boolean doReadMolecularOrbitals; 978 protected boolean reverseModels; 979 private String nameRequired; 980 public boolean doCentroidUnitCell; 981 public boolean centroidPacked; 982 public String strSupercell; 983 984 985 public boolean allow_a_len_1 = false; 986 987 // ALL: "CENTER" "REVERSEMODELS" 988 // ALL: "SYMOP=n" 989 // MANY: "NOVIB" "NOMO" 990 // CASTEP: "CHARGE=HIRSH q={i,j,k};" 991 // CIF: "ASSEMBLY n" 992 // CRYSTAL: "CONV" (conventional), "INPUT" 993 // CSF, SPARTAN: "NOORIENT" 994 // GAMESS-US: "CHARGE=LOW" 995 // JME, MOL: "NOMIN" 996 // MOL: "2D" 997 // Molden: "INPUT" "GEOM" "NOGEOM" 998 // MopacArchive: "NOCENTER" 999 // MOReaders: "NBOCHARGES" 1000 // P2N: "ALTNAME" 1001 // PDB: "BIOMOLECULE n;" "NOSYMMETRY" "CONF n" 1002 // Spartan: "INPUT", "ESPCHARGES" 1003 // 1004 setFilterAtomTypeStr(String s)1005 protected void setFilterAtomTypeStr(String s) { 1006 // PDB reader TYPE=... 1007 filterAtomTypeStr = s; 1008 filterAtomNameTerminator = "\0"; 1009 } 1010 setFilter(String filter0)1011 protected void setFilter(String filter0) { 1012 if (filter0 == null) { 1013 filter0 = (String) htParams.get("filter"); 1014 } else { 1015 // from PDB REMARK350() 1016 bsFilter = null; 1017 } 1018 if (filter0 != null) 1019 filter0 = filter0.toUpperCase(); 1020 filter = filter0; 1021 doSetOrientation = !checkFilterKey("NOORIENT"); 1022 doCentralize = (!checkFilterKey("NOCENTER") && checkFilterKey("CENTER")); 1023 addVibrations = !checkFilterKey("NOVIB"); 1024 ignoreStructure = checkFilterKey("DSSP"); 1025 isDSSP1 = checkFilterKey("DSSP1"); 1026 doReadMolecularOrbitals = !checkFilterKey("NOMO"); 1027 useAltNames = checkFilterKey("ALTNAME"); 1028 reverseModels = checkFilterKey("REVERSEMODELS"); 1029 allow_a_len_1 =checkFilterKey("TOPOS"); 1030 1031 if (filter == null) 1032 return; 1033 if (checkFilterKey("HETATM")) { 1034 filterHetero = true; 1035 filter = PT.rep(filter, "HETATM", "HETATM-Y"); 1036 } 1037 if (checkFilterKey("ATOM")) { 1038 filterHetero = true; 1039 filter = PT.rep(filter, "ATOM", "HETATM-N"); 1040 } 1041 1042 // can't use getFilter() here because form includes a semicolon: 1043 // cell=a+b,a-b,c;0,1/2,1/2 1044 if (checkFilterKey("CELL=")) 1045 strSupercell = filter.substring(filter.indexOf("CELL=") + 5).toLowerCase(); // must be last filter option 1046 nameRequired = PT.getQuotedAttribute(filter, "NAME"); 1047 if (nameRequired != null) { 1048 if (nameRequired.startsWith("'")) 1049 nameRequired = PT.split(nameRequired, "'")[1]; 1050 else if (nameRequired.startsWith("\"")) 1051 nameRequired = PT.split(nameRequired, "\"")[1]; 1052 filter = PT.rep(filter, nameRequired, ""); 1053 filter0 = filter = PT.rep(filter, "NAME=", ""); 1054 } 1055 filterAtomName = checkFilterKey("*.") || checkFilterKey("!."); 1056 if (filter.startsWith("_") || filter.indexOf(";_") >= 0) 1057 filterElement = checkFilterKey("_"); 1058 1059 filterGroup3 = checkFilterKey("["); 1060 filterChain = checkFilterKey(":"); 1061 filterAltLoc = checkFilterKey("%"); 1062 filterEveryNth = checkFilterKey("/="); 1063 if (filterEveryNth) 1064 filterN = parseIntAt(filter, filter.indexOf("/=") + 2); 1065 else if (filter.startsWith("=") || filter.indexOf(";=") >= 0) 1066 filterAtomType = checkFilterKey("="); 1067 if (filterN == Integer.MIN_VALUE) 1068 filterEveryNth = false; 1069 haveAtomFilter = filterAtomName || filterAtomType || filterElement 1070 || filterGroup3 || filterChain || filterAltLoc || filterHetero 1071 || filterEveryNth || checkFilterKey("/="); 1072 if (bsFilter == null) { 1073 // bsFilter is usually null, but from MDTOP it gets set to indicate 1074 // which atoms were selected by the filter. This then 1075 // gets used by COORD files to load just those coordinates 1076 // and it returns the bitset of filtered atoms 1077 bsFilter = new BS(); 1078 htParams.put("bsFilter", bsFilter); 1079 filter = (";" + filter + ";").replace(',', ';'); 1080 String s = getFilter("LATTICESCALING="); 1081 if (s != null && unitCellParams.length > 25) 1082 unitCellParams[25] = latticeScaling = parseFloatStr(s); 1083 s = getFilter("SYMOP="); 1084 if (s != null) 1085 filterSymop = " " + s + " "; 1086 Logger.info("filtering with " + filter); 1087 if (haveAtomFilter) { 1088 int ipt; 1089 filter1 = filter; 1090 if ((ipt = filter.indexOf("|")) >= 0) { 1091 filter1 = filter.substring(0, ipt).trim() + ";"; 1092 filter2 = ";" + filter.substring(ipt).trim(); 1093 } 1094 } 1095 } 1096 } 1097 1098 private String filter1, filter2; 1099 getFilter(String key)1100 public String getFilter(String key) { 1101 int pt = (filter == null ? -1 : filter.indexOf(key)); 1102 return (pt < 0 ? null : filter.substring(pt + key.length(), filter.indexOf(";", pt))); 1103 } 1104 checkFilterKey(String key)1105 public boolean checkFilterKey(String key) { 1106 return (filter != null && filter.indexOf(key) >= 0); 1107 } 1108 1109 /** 1110 * @param key 1111 * @return true if the key existed; filter is set null if this is the only key 1112 * 1113 */ 1114 checkAndRemoveFilterKey(String key)1115 public boolean checkAndRemoveFilterKey(String key) { 1116 if (!checkFilterKey(key)) 1117 return false; 1118 filter = PT.rep(filter, key, ""); 1119 // allows for "!" and ";" 1120 if (filter.length() < 3) 1121 filter = null; 1122 return true; 1123 } 1124 1125 1126 /** 1127 * @param atom 1128 * @param iAtom 1129 * @return true if we want this atom 1130 */ filterAtom(Atom atom, int iAtom)1131 protected boolean filterAtom(Atom atom, int iAtom) { 1132 if (!haveAtomFilter) 1133 return true; 1134 // cif, mdtop, pdb, gromacs, pqr 1135 boolean isOK = checkFilter(atom, filter1); 1136 if (filter2 != null) 1137 isOK |= checkFilter(atom, filter2); 1138 if (isOK && filterEveryNth) 1139 isOK = (((nFiltered++) % filterN) == 0); 1140 bsFilter.setBitTo(iAtom >= 0 ? iAtom : asc.ac, isOK); 1141 return isOK; 1142 } 1143 1144 /** 1145 * 1146 * @param atom 1147 * @param f 1148 * @return true if a filter is found 1149 */ checkFilter(Atom atom, String f)1150 private boolean checkFilter(Atom atom, String f) { 1151 return (!filterGroup3 || atom.group3 == null || !filterReject(f, "[", 1152 atom.group3.toUpperCase() + "]")) 1153 && (!filterAtomName || allowAtomName(atom.atomName, f)) 1154 && (filterAtomTypeStr == null || atom.atomName == null 1155 || atom.atomName.toUpperCase().indexOf("\0" + filterAtomTypeStr) >= 0) 1156 && (!filterElement || atom.elementSymbol == null || !filterReject(f, "_", 1157 atom.elementSymbol.toUpperCase() + ";")) 1158 && (!filterChain || atom.chainID == 0 || !filterReject(f, ":", "" 1159 + vwr.getChainIDStr(atom.chainID))) 1160 && (!filterAltLoc || atom.altLoc == '\0' || !filterReject( 1161 f, "%", "" + atom.altLoc)) 1162 && (!filterHetero || !allowPDBFilter || !filterReject(f, "HETATM", 1163 atom.isHetero ? "-Y" : "-N")); 1164 } 1165 rejectAtomName(String name)1166 public boolean rejectAtomName(String name) { 1167 return filterAtomName && !allowAtomName(name, filter); 1168 } 1169 allowAtomName(String atomName, String f)1170 private boolean allowAtomName(String atomName, String f) { 1171 return (atomName == null || !filterReject(f, ".", 1172 atomName.toUpperCase() + filterAtomNameTerminator)); 1173 } 1174 filterReject(String f, String code, String atomCode)1175 protected boolean filterReject(String f, String code, String atomCode) { 1176 return (f.indexOf(code) >= 0 1177 && (f.indexOf("!" + code) >= 0) == 1178 (f.indexOf(code + atomCode) >= 0) 1179 ); 1180 } 1181 set2D()1182 protected void set2D() { 1183 // MOL and JME - sets for ALL MODELS, but just for ModelLoader. 1184 // It is quite possible that multiple 2D models cannot be loaded as 3D 1185 asc.setInfo("is2D", Boolean.TRUE); 1186 if (!checkFilterKey("NOMIN")) 1187 asc.setInfo("doMinimize", 1188 Boolean.TRUE); 1189 appendLoadNote("This model is 2D. Its 3D structure will be generated."); 1190 } 1191 doGetVibration(int vibrationNumber)1192 public boolean doGetVibration(int vibrationNumber) { 1193 // vibrationNumber is 1-based 1194 return addVibrations 1195 && (desiredVibrationNumber <= 0 || vibrationNumber == desiredVibrationNumber); 1196 } 1197 1198 private M3 matRot; 1199 1200 public MSInterface ms; 1201 setTransform(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)1202 public void setTransform(float x1, float y1, float z1, float x2, float y2, 1203 float z2, float x3, float y3, float z3) { 1204 if (matRot != null || !doSetOrientation) 1205 return; 1206 matRot = new M3(); 1207 V3 v = V3.new3(x1, y1, z1); 1208 // rows in Sygress/CAChe and Spartan become columns here 1209 v.normalize(); 1210 matRot.setColumnV(0, v); 1211 v.set(x2, y2, z2); 1212 v.normalize(); 1213 matRot.setColumnV(1, v); 1214 v.set(x3, y3, z3); 1215 v.normalize(); 1216 matRot.setColumnV(2, v); 1217 asc.setInfo("defaultOrientationMatrix", M3.newM3(matRot)); 1218 // first two matrix column vectors define quaternion X and XY plane 1219 Quat q = Quat.newM(matRot); 1220 asc.setInfo("defaultOrientationQuaternion", q); 1221 Logger.info("defaultOrientationMatrix = " + matRot); 1222 1223 } 1224 1225 ///////////////////////////// 1226 setAtomCoordXYZ(Atom atom, float x, float y, float z)1227 public void setAtomCoordXYZ(Atom atom, float x, float y, float z) { 1228 atom.set(x, y, z); 1229 setAtomCoord(atom); 1230 } 1231 setAtomCoordScaled(Atom atom, String[] tokens, int i, float f)1232 public Atom setAtomCoordScaled(Atom atom, String[] tokens, int i, float f) { 1233 if (atom == null) 1234 atom = asc.addNewAtom(); 1235 setAtomCoordXYZ(atom, parseFloatStr(tokens[i]) * f, 1236 parseFloatStr(tokens[i + 1]) * f, parseFloatStr(tokens[i + 2]) * f); 1237 return atom; 1238 } 1239 setAtomCoordTokens(Atom atom, String[] tokens, int i)1240 protected void setAtomCoordTokens(Atom atom, String[] tokens, int i) { 1241 setAtomCoordXYZ(atom, parseFloatStr(tokens[i]), parseFloatStr(tokens[i + 1]), 1242 parseFloatStr(tokens[i + 2])); 1243 } 1244 addAtomXYZSymName(String[] tokens, int i, String sym, String name)1245 public Atom addAtomXYZSymName(String[] tokens, int i, String sym, String name) { 1246 Atom atom = asc.addNewAtom(); 1247 if (sym != null) 1248 atom.elementSymbol = sym; 1249 if (name != null) 1250 atom.atomName = name; 1251 setAtomCoordTokens(atom, tokens, i); 1252 return atom; 1253 } 1254 setAtomCoord(Atom atom)1255 public void setAtomCoord(Atom atom) { 1256 // fileScaling is used by the PLOT command to 1257 // put data into PDB format, preserving name/residue information, 1258 // and still get any xyz data into the allotted column space. 1259 boolean mustFractionalize = (doConvertToFractional && !fileCoordinatesAreFractional && getSymmetry() != null); 1260 if (fileScaling != null) { 1261 atom.x = atom.x * fileScaling.x + fileOffset.x; 1262 atom.y = atom.y * fileScaling.y + fileOffset.y; 1263 atom.z = atom.z * fileScaling.z + fileOffset.z; 1264 } 1265 if (mustFractionalize) { 1266 if (!symmetry.haveUnitCell()) 1267 symmetry.setUnitCell(unitCellParams, false); 1268 symmetry.toFractional(atom, false); 1269 iHaveFractionalCoordinates = true; 1270 } 1271 if (fixJavaFloat && fileCoordinatesAreFractional) 1272 PT.fixPtFloats(atom, PT.FRACTIONAL_PRECISION); 1273 doCheckUnitCell = true; 1274 } 1275 addSites(Map<String, Map<String, Object>> htSites)1276 public void addSites(Map<String, Map<String, Object>> htSites) { 1277 asc.setCurrentModelInfo("pdbSites", htSites); 1278 String sites = ""; 1279 for (Map.Entry<String, Map<String, Object>> entry : htSites.entrySet()) { 1280 String name = entry.getKey(); 1281 Map<String, Object> htSite = entry.getValue(); 1282 char ch; 1283 for (int i = name.length(); --i >= 0;) 1284 if (!PT.isLetterOrDigit(ch = name.charAt(i)) && ch != '\'') 1285 name = name.substring(0, i) + "_" + name.substring(i + 1); 1286 String groups = (String) htSite.get("groups"); 1287 if (groups.length() == 0) 1288 continue; 1289 addSiteScript("@site_" + name + " " + groups); 1290 addSiteScript("site_" + name + " = [\"" + PT.rep(groups, ",", "\",\"") + "\"]"); 1291 sites += ",\"site_" + name + "\""; 1292 } 1293 if (sites.length() > 0) 1294 addSiteScript("site_list = [" + sites.substring(1) + "]"); 1295 } 1296 applySymmetryAndSetTrajectory()1297 public void applySymmetryAndSetTrajectory() throws Exception { 1298 // overridden in many readers 1299 applySymTrajASCR(); 1300 } 1301 1302 public boolean vibsFractional = false; 1303 applySymTrajASCR()1304 public SymmetryInterface applySymTrajASCR() throws Exception { 1305 if (forcePacked) 1306 initializeSymmetryOptions(); 1307 SymmetryInterface sym = (iHaveUnitCell && doCheckUnitCell ? asc 1308 .getXSymmetry().applySymmetryFromReader(getSymmetry()) : null); 1309 if (sym == null) 1310 asc.setTensors(); 1311 if (isTrajectory) 1312 asc.setTrajectory(); 1313 if (moreUnitCellInfo != null) { 1314 asc.setCurrentModelInfo("moreUnitCellInfo", moreUnitCellInfo); 1315 moreUnitCellInfo = null; 1316 } 1317 finalizeSubclassSymmetry(sym != null); 1318 //if (sym != null && ptSupercell != null) 1319 //asc.getXSymmetry().finalizeUnitCell(ptSupercell); 1320 initializeSymmetry(); 1321 return sym; 1322 } 1323 1324 /** 1325 * @param haveSymmetry 1326 * @throws Exception 1327 */ finalizeSubclassSymmetry(boolean haveSymmetry)1328 protected void finalizeSubclassSymmetry(boolean haveSymmetry) throws Exception { 1329 } 1330 doPreSymmetry()1331 protected void doPreSymmetry() throws Exception { 1332 } 1333 1334 @SuppressWarnings("unchecked") finalizeMOData(Map<String, Object> moData)1335 public void finalizeMOData(Map<String, Object> moData) { 1336 asc.setCurrentModelInfo("moData", moData); 1337 if (moData == null) 1338 return; 1339 Lst<Map<String, Object>> orbitals = (Lst<Map<String, Object>>) moData 1340 .get("mos"); 1341 if (orbitals != null) 1342 Logger.info(orbitals.size() + " molecular orbitals read in model " 1343 + asc.atomSetCount); 1344 } 1345 getElementSymbol(int elementNumber)1346 public static String getElementSymbol(int elementNumber) { 1347 return JmolAdapter.getElementSymbol(elementNumber); 1348 } 1349 1350 /** 1351 * fills an array with a pre-defined number of lines of token data, 1352 * skipping blank lines in the process 1353 * 1354 * @param data 1355 * @param minLineLen TODO 1356 * @throws Exception 1357 */ fillDataBlock(String[][] data, int minLineLen)1358 protected void fillDataBlock(String[][] data, int minLineLen) throws Exception { 1359 int nLines = data.length; 1360 for (int i = 0; i < nLines; i++) { 1361 data[i] = PT.getTokens(discardLinesUntilNonBlank()); 1362 if (data[i].length < minLineLen) 1363 --i; 1364 } 1365 1366 } 1367 1368 /** 1369 * fills a double[3][3] 1370 * 1371 * @param tokens or null if to read each line for three values (as last 3 on line) 1372 * @param pt initial index; if tokens == null, then negative index is from end of each line 1373 * @return double[3][3] 1374 * @throws Exception 1375 */ fill3x3(String[] tokens, int pt)1376 protected double[][] fill3x3(String[] tokens, int pt) throws Exception { 1377 double[][] a = new double[3][3]; 1378 boolean needTokens = (tokens == null); 1379 int pt0 = pt; 1380 for (int i = 0; i < 3; i++) { 1381 if (needTokens || pt >= tokens.length) { 1382 while ((tokens = PT.getTokens(rd())).length < 3){} 1383 pt = (pt0 < 0 ? tokens.length + pt0 : pt0); 1384 } 1385 for (int j = 0; j < 3; j++) 1386 a[i][j] = Double.valueOf(tokens[pt++]).doubleValue(); 1387 } 1388 return a; 1389 } 1390 1391 /** 1392 * fills a float array with string data from a file 1393 * @param s string data containing floats 1394 * @param width column width or 0 to read tokens 1395 * @param data result data to be filled 1396 * @return data 1397 * @throws Exception 1398 */ fillFloatArray(String s, int width, float[] data)1399 protected float[] fillFloatArray(String s, int width, float[] data) 1400 throws Exception { 1401 String[] tokens = new String[0]; 1402 int pt = 0; 1403 for (int i = 0; i < data.length; i++) { 1404 while (tokens != null && pt >= tokens.length) { 1405 if (s == null) 1406 s = rd(); 1407 if (width == 0) { 1408 tokens = PT.getTokens(s); 1409 } else { 1410 tokens = new String[s.length() / width]; 1411 for (int j = 0; j < tokens.length; j++) 1412 tokens[j] = s.substring(j * width, (j + 1) * width); 1413 } 1414 s = null; 1415 pt = 0; 1416 } 1417 if (tokens == null) 1418 break; 1419 data[i] = parseFloatStr(tokens[pt++]); 1420 } 1421 return data; 1422 } 1423 1424 /** 1425 * Extracts a block of frequency data from a file. This block may be of two 1426 * types -- either X Y Z across a row or each of X Y Z on a separate line. 1427 * Data is presumed to be in fixed FORTRAN-like column format, not 1428 * space-separated columns. 1429 * 1430 * @param iAtom0 1431 * the first atom to be assigned a frequency 1432 * @param ac 1433 * the number of atoms to be assigned 1434 * @param modelAtomCount 1435 * the number of atoms in each model 1436 * @param ignore 1437 * the frequencies to ignore because the user has selected only certain 1438 * vibrations to be read or for whatever reason; length serves to set 1439 * the number of frequencies to be read 1440 * @param isWide 1441 * when TRUE, this is a table that has X Y Z for each mode within the 1442 * same row; when FALSE, this is a table that has X Y Z for each mode 1443 * on a separate line. 1444 * @param col0 1445 * the column in which data starts 1446 * @param colWidth 1447 * the width of the data columns 1448 * @param atomIndexes 1449 * an array either null or indicating exactly which atoms get the 1450 * frequencies (used by CrystalReader) 1451 * @param minLineLen 1452 * TODO 1453 * @param data 1454 * TODO 1455 * @throws Exception 1456 */ fillFrequencyData(int iAtom0, int ac, int modelAtomCount, boolean[] ignore, boolean isWide, int col0, int colWidth, int[] atomIndexes, int minLineLen, String[][] data)1457 protected void fillFrequencyData(int iAtom0, int ac, int modelAtomCount, 1458 boolean[] ignore, boolean isWide, int col0, 1459 int colWidth, int[] atomIndexes, 1460 int minLineLen, String[][] data) 1461 throws Exception { 1462 boolean withSymmetry = (ac != 0 && modelAtomCount != ac && data == null); 1463 if (ac == 0 && atomIndexes != null) 1464 ac = atomIndexes.length; 1465 int nLines = (isWide ? ac : ac * 3); 1466 int nFreq = ignore.length; 1467 if (data == null) { 1468 data = new String[nLines][]; 1469 fillDataBlockFixed(data, col0, colWidth, minLineLen); 1470 } else if (!isWide) { 1471 // Gaussian high precision - get atom index at ptNonblank + 1 1472 int ptNonblank = minLineLen; 1473 fillDataBlockFixed(data, col0, colWidth, -ptNonblank); 1474 if (data[0] == null) 1475 return; 1476 iAtom0 += parseIntAt(line, ptNonblank - 5) - 1; 1477 } 1478 for (int i = 0, atomPt = 0; i < nLines; i++, atomPt++) { 1479 String[] values = data[i]; 1480 String[] valuesY = (isWide ? null : data[++i]); 1481 String[] valuesZ = (isWide ? null : data[++i]); 1482 int dataPt = values.length - (isWide ? nFreq * 3 : nFreq) - 1; 1483 for (int j = 0, jj = 0; jj < nFreq; jj++) { 1484 ++dataPt; 1485 String x = values[dataPt]; 1486 if (x.charAt(0) == ')') // AMPAC reader! 1487 x = x.substring(1); 1488 float vx = parseFloatStr(x); 1489 float vy = parseFloatStr(isWide ? values[++dataPt] : valuesY[dataPt]); 1490 float vz = parseFloatStr(isWide ? values[++dataPt] : valuesZ[dataPt]); 1491 if (ignore[jj]) 1492 continue; 1493 int iAtom = (atomIndexes == null ? atomPt : atomIndexes[atomPt]); 1494 if (iAtom < 0) 1495 continue; 1496 iAtom += iAtom0 + modelAtomCount * j++; 1497 if (debugging) 1498 Logger.debug("atom " + iAtom + " vib" + j + ": " + vx + " " + vy 1499 + " " + vz); 1500 asc.addVibrationVectorWithSymmetry(iAtom, vx, vy, vz, withSymmetry); 1501 } 1502 } 1503 } 1504 1505 /** 1506 * Fills an array with a predefined number of lines of data that is 1507 * arranged in fixed FORTRAN-like column format. 1508 * 1509 * Used exclusively for frequency data 1510 * 1511 * @param data 1512 * @param col0 1513 * @param colWidth 1514 * @param minLineLen or -ptNonblank 1515 * @throws Exception 1516 */ fillDataBlockFixed(String[][] data, int col0, int colWidth, int minLineLen)1517 protected void fillDataBlockFixed(String[][] data, int col0, int colWidth, int minLineLen) 1518 throws Exception { 1519 if (colWidth == 0) { 1520 fillDataBlock(data, minLineLen); 1521 return; 1522 } 1523 int nLines = data.length; 1524 for (int i = 0; i < nLines; i++) { 1525 discardLinesUntilNonBlank(); 1526 // neg minLineLen is a nonblank pt 1527 if (minLineLen < 0 && line.charAt(-minLineLen) == ' ') { 1528 data[0] = null; 1529 return; 1530 } 1531 int nFields = (line.length() - col0 + 1) / colWidth; // Dmol reader is one short 1532 data[i] = new String[nFields]; 1533 for (int j = 0, start = col0; j < nFields; j++, start += colWidth) 1534 data[i][j] = line.substring(start, Math.min(line.length(), start + colWidth)); 1535 } 1536 } 1537 readLines(int nLines)1538 protected String readLines(int nLines) throws Exception { 1539 for (int i = nLines; --i >= 0;) 1540 rd(); 1541 return line; 1542 } 1543 discardLinesUntilStartsWith(String startsWith)1544 public String discardLinesUntilStartsWith(String startsWith) 1545 throws Exception { 1546 while (rd() != null && !line.startsWith(startsWith)) { 1547 } 1548 return line; 1549 } 1550 discardLinesUntilContains(String containsMatch)1551 public String discardLinesUntilContains(String containsMatch) 1552 throws Exception { 1553 while (rd() != null && line.indexOf(containsMatch) < 0) { 1554 } 1555 return line; 1556 } 1557 discardLinesUntilContains2(String s1, String s2)1558 public String discardLinesUntilContains2(String s1, String s2) 1559 throws Exception { 1560 while (rd() != null && line.indexOf(s1) < 0 && line.indexOf(s2) < 0) { 1561 } 1562 return line; 1563 } 1564 discardLinesUntilBlank()1565 public String discardLinesUntilBlank() throws Exception { 1566 while (rd() != null && line.trim().length() != 0) { 1567 } 1568 return line; 1569 } 1570 discardLinesUntilNonBlank()1571 public String discardLinesUntilNonBlank() throws Exception { 1572 while (rd() != null && line.trim().length() == 0) { 1573 } 1574 return line; 1575 } 1576 checkLineForScript(String line)1577 protected void checkLineForScript(String line) { 1578 this.line = line; 1579 checkCurrentLineForScript(); 1580 } 1581 checkCurrentLineForScript()1582 public void checkCurrentLineForScript() { 1583 if (line.endsWith("#noautobond")) { 1584 line = line.substring(0, line.lastIndexOf('#')).trim(); 1585 asc.setNoAutoBond(); 1586 } 1587 int pt = line.indexOf("jmolscript:"); 1588 if (pt >= 0) { 1589 String script = line.substring(pt + 11, line.length()); 1590 if (script.indexOf("#") >= 0) { 1591 script = script.substring(0, script.indexOf("#")); 1592 } 1593 addJmolScript(script); 1594 line = line.substring(0, pt).trim(); 1595 } 1596 } 1597 1598 private String previousScript; 1599 addJmolScript(String script)1600 public void addJmolScript(String script) { 1601 Logger.info("#jmolScript: " + script); 1602 if (previousScript == null) 1603 previousScript = ""; 1604 else if (!previousScript.endsWith(";")) 1605 previousScript += ";"; 1606 previousScript += script; 1607 asc.setInfo("jmolscript", 1608 previousScript); 1609 } 1610 1611 private String siteScript; 1612 addSiteScript(String script)1613 protected void addSiteScript(String script) { 1614 if (siteScript == null) 1615 siteScript = ""; 1616 else if (!siteScript.endsWith(";")) 1617 siteScript += ";"; 1618 siteScript += script; 1619 asc.setInfo("sitescript", 1620 siteScript); // checked in ScriptEvaluator.load() 1621 } 1622 rd()1623 public String rd() throws Exception { 1624 return RL(); 1625 } 1626 RL()1627 public String RL() throws Exception { 1628 prevline = line; 1629 line = reader.readLine(); 1630 if (out != null && line != null) 1631 out.append(line).append("\n"); 1632 ptLine++; 1633 if (debugging && line != null) 1634 Logger.info(line); 1635 return line; 1636 } 1637 getStrings(String sinfo, int nFields, int width)1638 final static protected String[] getStrings(String sinfo, int nFields, 1639 int width) { 1640 String[] fields = new String[nFields]; 1641 for (int i = 0, pt = 0; i < nFields; i++, pt += width) 1642 fields[i] = sinfo.substring(pt, pt + width); 1643 return fields; 1644 } 1645 1646 // parser functions are static, so they need notstatic counterparts 1647 getTokens()1648 public String[] getTokens() { 1649 return PT.getTokens(line); 1650 } 1651 getTokensFloat(String s, float[] f, int n)1652 public static float[] getTokensFloat(String s, float[] f, int n) { 1653 if (f == null) 1654 f = new float[n]; 1655 PT.parseFloatArrayDataN(PT.getTokens(s), f, n); 1656 return f; 1657 } 1658 parseFloat()1659 protected float parseFloat() { 1660 return PT.parseFloatNext(line, next); 1661 } 1662 parseFloatStr(String s)1663 public float parseFloatStr(String s) { 1664 next[0] = 0; 1665 return PT.parseFloatNext(s, next); 1666 } 1667 parseFloatRange(String s, int iStart, int iEnd)1668 protected float parseFloatRange(String s, int iStart, int iEnd) { 1669 next[0] = iStart; 1670 return PT.parseFloatRange(s, iEnd, next); 1671 } 1672 parseInt()1673 protected int parseInt() { 1674 return PT.parseIntNext(line, next); 1675 } 1676 parseIntStr(String s)1677 public int parseIntStr(String s) { 1678 next[0] = 0; 1679 return PT.parseIntNext(s, next); 1680 } 1681 parseIntAt(String s, int iStart)1682 public int parseIntAt(String s, int iStart) { 1683 next[0] = iStart; 1684 return PT.parseIntNext(s, next); 1685 } 1686 parseIntRange(String s, int iStart, int iEnd)1687 protected int parseIntRange(String s, int iStart, int iEnd) { 1688 next[0] = iStart; 1689 return PT.parseIntRange(s, iEnd, next); 1690 } 1691 parseToken()1692 protected String parseToken() { 1693 return PT.parseTokenNext(line, next); 1694 } 1695 parseTokenStr(String s)1696 protected String parseTokenStr(String s) { 1697 next[0] = 0; 1698 return PT.parseTokenNext(s, next); 1699 } 1700 parseTokenNext(String s)1701 protected String parseTokenNext(String s) { 1702 return PT.parseTokenNext(s, next); 1703 } 1704 parseTokenRange(String s, int iStart, int iEnd)1705 protected String parseTokenRange(String s, int iStart, int iEnd) { 1706 next[0] = iStart; 1707 return PT.parseTokenRange(s, iEnd, next); 1708 } 1709 1710 /** 1711 * get all integers after letters 1712 * negative entries are spaces (1Xn) 1713 * 1714 * @param s 1715 * @return Vector of integers 1716 */ getFortranFormatLengths(String s)1717 protected static Lst<Integer> getFortranFormatLengths(String s) { 1718 Lst<Integer> vdata = new Lst<Integer>(); 1719 int n = 0; 1720 int c = 0; 1721 int factor = 1; 1722 boolean inN = false; 1723 boolean inCount = true; 1724 s += ","; 1725 for (int i = 0; i < s.length(); i++) { 1726 char ch = s.charAt(i); 1727 switch (ch) { 1728 case '.': 1729 inN = false; 1730 continue; 1731 case ',': 1732 for (int j = 0; j < c; j++) 1733 vdata.addLast(Integer.valueOf(n * factor)); 1734 inN = false; 1735 inCount = true; 1736 c = 0; 1737 continue; 1738 case 'X': 1739 n = c; 1740 c = 1; 1741 factor = -1; 1742 continue; 1743 1744 } 1745 boolean isDigit = PT.isDigit(ch); 1746 if (isDigit) { 1747 if (inN) 1748 n = n * 10 + ch - '0'; 1749 else if (inCount) 1750 c = c * 10 + ch - '0'; 1751 } else if (PT.isLetter(ch)) { 1752 n = 0; 1753 inN = true; 1754 inCount = false; 1755 factor = 1; 1756 } else { 1757 inN = false; 1758 } 1759 } 1760 return vdata; 1761 } 1762 1763 /** 1764 * read three vectors, as for unit cube definitions 1765 * allows for non-numeric data preceding the number block 1766 * 1767 * @param isBohr 1768 * @return three vectors 1769 * @throws Exception 1770 * 1771 */ read3Vectors(boolean isBohr)1772 protected V3[] read3Vectors(boolean isBohr) throws Exception { 1773 V3[] vectors = new V3[3]; 1774 float[] f = new float[3]; 1775 for (int i = 0; i < 3; i++) { 1776 if (i > 0 || Float.isNaN(parseFloatStr(line))) { 1777 rd(); 1778 if (i == 0 && line != null) { 1779 i = -1; 1780 continue; 1781 } 1782 } 1783 fillFloatArray(line, 0, f); 1784 vectors[i] = new V3(); 1785 vectors[i].setA(f); 1786 if (isBohr) 1787 vectors[i].scale(ANGSTROMS_PER_BOHR); 1788 } 1789 return vectors; 1790 } 1791 1792 /** 1793 * allow 13C, 15N, 2H, etc. for isotopes 1794 * 1795 * @param atom 1796 * @param str 1797 */ setElementAndIsotope(Atom atom, String str)1798 protected void setElementAndIsotope(Atom atom, String str) { 1799 int isotope = parseIntStr(str); 1800 if (isotope == Integer.MIN_VALUE) { 1801 atom.elementSymbol = str; 1802 } else { 1803 str = str.substring(("" + isotope).length()); 1804 atom.elementNumber = (short) (str.length() == 0 ? isotope 1805 : ((isotope << 7) + JmolAdapter.getElementNumber(str))); 1806 } 1807 } 1808 finalizeModelSet()1809 public void finalizeModelSet() { 1810 // PyMOL reader only 1811 } 1812 setChainID(Atom atom, String label)1813 public void setChainID(Atom atom, String label) { 1814 atom.chainID = vwr.getChainID(label, true); 1815 } 1816 1817 @Override readNextLine()1818 public String readNextLine() throws Exception { 1819 // from CifDataReader, DSSRDataReader 1820 if (rd() != null && line.indexOf("#jmolscript:") >= 0) 1821 checkCurrentLineForScript(); 1822 return line; 1823 } 1824 appendUunitCellInfo(String info)1825 public void appendUunitCellInfo(String info) { 1826 if (moreUnitCellInfo == null) 1827 moreUnitCellInfo = new Lst<String>(); 1828 moreUnitCellInfo.addLast(info); 1829 appendLoadNote(info); 1830 } 1831 getInterface(String className)1832 public Object getInterface(String className) { 1833 Object o = Interface.getInterface(className, vwr, "file"); 1834 if (o == null) 1835 throw new NullPointerException("Interface"); 1836 return o; 1837 } 1838 forceSymmetry(boolean andPack)1839 public void forceSymmetry(boolean andPack) { 1840 if (andPack) 1841 doPackUnitCell = andPack; 1842 if (!doApplySymmetry) { 1843 doApplySymmetry = true; 1844 latticeCells[0] = 1; 1845 latticeCells[1] = 1; 1846 latticeCells[2] = 1; 1847 } 1848 } 1849 1850 } 1851