1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2021-11-14 13:32:58 -0600 (Sun, 14 Nov 2021) $ 4 * $Revision: 22254 $ 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 import java.util.Collections; 28 import java.util.Hashtable; 29 import java.util.Map; 30 import java.util.Properties; 31 32 import javajs.util.AU; 33 import javajs.util.Lst; 34 import javajs.util.P3; 35 import javajs.util.V3; 36 37 import org.jmol.api.Interface; 38 import org.jmol.api.SymmetryInterface; 39 import javajs.util.BS; 40 41 import org.jmol.util.BSUtil; 42 import org.jmol.util.Logger; 43 44 @SuppressWarnings("unchecked") 45 public class AtomSetCollection { 46 47 AtomSetCollectionReader reader; 48 49 public BS bsAtoms; // required for CIF reader 50 51 public String fileTypeName; 52 53 String collectionName; 54 setCollectionName(String collectionName)55 public void setCollectionName(String collectionName) { 56 if (collectionName != null 57 && (collectionName = collectionName.trim()).length() > 0) 58 this.collectionName = collectionName; 59 } 60 61 public Map<String, Object> atomSetInfo = new Hashtable<String, Object>(); 62 63 private final static String[] globalBooleans = { 64 "someModelsHaveFractionalCoordinates", "someModelsHaveSymmetry", 65 "someModelsHaveUnitcells", "someModelsHaveCONECT", "isPDB", "someModelsHaveDomains", "someModelsHaveValidations" }; 66 67 public final static int GLOBAL_FRACTCOORD = 0; 68 public final static int GLOBAL_SYMMETRY = 1; 69 public final static int GLOBAL_UNITCELLS = 2; 70 public final static int GLOBAL_CONECT = 3; 71 public final static int GLOBAL_ISPDB = 4; 72 public final static int GLOBAL_DOMAINS = 5; 73 public final static int GLOBAL_VALIDATIONS = 6; 74 75 clearGlobalBoolean(int globalIndex)76 public void clearGlobalBoolean(int globalIndex) { 77 atomSetInfo.remove(globalBooleans[globalIndex]); 78 } 79 setGlobalBoolean(int globalIndex)80 public void setGlobalBoolean(int globalIndex) { 81 setInfo(globalBooleans[globalIndex], Boolean.TRUE); 82 } 83 getGlobalBoolean(int globalIndex)84 boolean getGlobalBoolean(int globalIndex) { 85 return (atomSetInfo.get(globalBooleans[globalIndex]) == Boolean.TRUE); 86 } 87 88 public Atom[] atoms = new Atom[256]; 89 public int ac; 90 public Bond[] bonds = new Bond[256]; 91 public int bondCount; 92 public Structure[] structures = new Structure[16]; 93 public int structureCount; 94 public int atomSetCount; 95 public int iSet = -1; 96 97 98 private int[] atomSetNumbers = new int[16]; 99 private int[] atomSetAtomIndexes = new int[16]; 100 private int[] atomSetAtomCounts = new int[16]; 101 private int[] atomSetBondCounts = new int[16]; 102 private Map<String, Object>[] atomSetAuxiliaryInfo = new Hashtable[16]; 103 104 public String errorMessage; 105 106 public boolean coordinatesAreFractional; 107 boolean isTrajectory; 108 private int trajectoryStepCount = 0; 109 110 private Lst<P3[]> trajectorySteps; 111 private Lst<V3[]> vibrationSteps; 112 private Lst<String> trajectoryNames; 113 114 public boolean doFixPeriodic; 115 public boolean allowMultiple; // set false only in CastepReader for a phonon file 116 117 private Lst<AtomSetCollectionReader> readerList; 118 119 public boolean atomMapAnyCase; 120 AtomSetCollection(String fileTypeName, AtomSetCollectionReader reader, AtomSetCollection[] array, Lst<?> list)121 public AtomSetCollection(String fileTypeName, AtomSetCollectionReader reader, 122 AtomSetCollection[] array, Lst<?> list) { 123 124 // merging files 125 126 this.fileTypeName = fileTypeName; 127 this.reader = reader; 128 allowMultiple = (reader == null || reader.desiredVibrationNumber < 0); 129 // set the default PATH properties as defined in the SmarterJmolAdapter 130 Properties p = new Properties(); 131 p.put("PATH_KEY", SmarterJmolAdapter.PATH_KEY); 132 p.put("PATH_SEPARATOR", SmarterJmolAdapter.PATH_SEPARATOR); 133 setInfo("properties", p); 134 Integer modelIndex = (reader == null ? null : (Integer) reader.htParams.get("appendToModelIndex")); 135 if (modelIndex != null) 136 this.setInfo("appendToModelIndex",modelIndex); 137 if (array != null) { 138 int n = 0; 139 readerList = new Lst<AtomSetCollectionReader>(); 140 for (int i = 0; i < array.length; i++) 141 if (array[i] != null && (array[i].ac > 0 || array[i].reader != null 142 && array[i].reader.mustFinalizeModelSet)) 143 appendAtomSetCollection(n++, array[i]); 144 if (n > 1) 145 setInfo("isMultiFile", Boolean.TRUE); 146 } else if (list != null) { 147 // (from zipped zip files) 148 setInfo("isMultiFile", Boolean.TRUE); 149 appendAtomSetCollectionList(list); 150 } 151 } 152 appendAtomSetCollectionList(Lst<?> list)153 private void appendAtomSetCollectionList(Lst<?> list) { 154 int n = list.size(); 155 if (n == 0) { 156 errorMessage = "No file found!"; 157 return; 158 } 159 160 for (int i = 0; i < n; i++) { 161 Object o = list.get(i); 162 if (o instanceof Lst) 163 appendAtomSetCollectionList((Lst<?>) o); 164 else 165 appendAtomSetCollection(i, (AtomSetCollection) o); 166 } 167 } 168 setTrajectory()169 public void setTrajectory() { 170 if (!isTrajectory) 171 trajectorySteps = new Lst<P3[]>(); 172 isTrajectory = true; 173 int n = (bsAtoms == null ? ac : bsAtoms.cardinality()); 174 if (n <= 1) 175 return; 176 P3[] trajectoryStep = new P3[n]; 177 boolean haveVibrations = (n > 0 && atoms[0].vib != null && !Float 178 .isNaN(atoms[0].vib.z)); 179 V3[] vibrationStep = (haveVibrations ? new V3[n] : null); 180 P3[] prevSteps = (trajectoryStepCount == 0 ? null : (P3[]) trajectorySteps 181 .get(trajectoryStepCount - 1)); 182 for (int i = 0, ii = 0; i < ac; i++) { 183 if (bsAtoms != null && !bsAtoms.get(i)) 184 continue; 185 P3 pt = P3.newP(atoms[i]); 186 if (doFixPeriodic && prevSteps != null) 187 pt = fixPeriodic(pt, prevSteps[i]); 188 trajectoryStep[ii] = pt; 189 if (haveVibrations) 190 vibrationStep[ii] = atoms[i].vib; 191 ii++; 192 } 193 if (haveVibrations) { 194 if (vibrationSteps == null) { 195 vibrationSteps = new Lst<V3[]>(); 196 for (int i = 0; i < trajectoryStepCount; i++) 197 vibrationSteps.addLast(null); 198 } 199 vibrationSteps.addLast(vibrationStep); 200 } 201 202 trajectorySteps.addLast(trajectoryStep); 203 trajectoryStepCount++; 204 } 205 206 /** 207 * Appends an AtomSetCollection 208 * 209 * @param collectionIndex 210 * collection index for new model number 211 * @param collection 212 * AtomSetCollection to append 213 */ appendAtomSetCollection(int collectionIndex, AtomSetCollection collection)214 public void appendAtomSetCollection(int collectionIndex, 215 AtomSetCollection collection) { 216 217 // List readers that will need calls to finalizeModelSet(); 218 if (collection.reader != null && collection.reader.mustFinalizeModelSet) 219 readerList.addLast(collection.reader); 220 // Initializations 221 int existingAtomsCount = ac; 222 223 // auxiliary info 224 setInfo("loadState", collection.atomSetInfo.get("loadState")); 225 226 // append to bsAtoms if necessary (CIF reader molecular mode) 227 if (collection.bsAtoms != null) { 228 if (bsAtoms == null) 229 bsAtoms = new BS(); 230 for (int i = collection.bsAtoms 231 .nextSetBit(0); i >= 0; i = collection.bsAtoms.nextSetBit(i + 1)) 232 bsAtoms.set(existingAtomsCount + i); 233 } 234 235 // Clone each AtomSet 236 int clonedAtoms = 0; 237 int atomSetCount0 = atomSetCount; 238 for (int atomSetNum = 0; atomSetNum < collection.atomSetCount; atomSetNum++) { 239 newAtomSet(); 240 // must fix referencing for someModelsHaveCONECT business 241 Map<String, Object> info = atomSetAuxiliaryInfo[iSet] = collection.atomSetAuxiliaryInfo[atomSetNum]; 242 int[] atomInfo = (int[]) info.get("PDB_CONECT_firstAtom_count_max"); 243 if (atomInfo != null) 244 atomInfo[0] += existingAtomsCount; 245 setCurrentModelInfo("title", collection.collectionName); 246 setAtomSetName(collection.getAtomSetName(atomSetNum)); 247 for (int atomNum = 0; atomNum < collection.atomSetAtomCounts[atomSetNum]; atomNum++) { 248 if (bsAtoms != null) 249 bsAtoms.set(ac); 250 newCloneAtom(collection.atoms[clonedAtoms]); 251 clonedAtoms++; 252 } 253 254 // numbers 255 atomSetNumbers[iSet] = (collectionIndex < 0 ? iSet + 1 256 : ((collectionIndex + 1) * 1000000) 257 + collection.atomSetNumbers[atomSetNum]); 258 259 // Note -- this number is used for Model.modelNumber. It is a combination of 260 // file number * 1000000 + PDB MODEL NUMBER, which could be anything. 261 // Adding the file number here indicates that we have multiple files. 262 // But this will all be adjusted in ModelLoader.finalizeModels(). BH 11/2007 263 264 } 265 // Clone bonds 266 for (int bondNum = 0; bondNum < collection.bondCount; bondNum++) { 267 Bond bond = collection.bonds[bondNum]; 268 addNewBondWithOrder(bond.atomIndex1 + existingAtomsCount, 269 bond.atomIndex2 + existingAtomsCount, bond.order); 270 } 271 // Set globals 272 for (int i = globalBooleans.length; --i >= 0;) 273 if (collection.getGlobalBoolean(i)) 274 setGlobalBoolean(i); 275 276 // Add structures 277 for (int i = 0; i < collection.structureCount; i++) { 278 Structure s = collection.structures[i]; 279 addStructure(s); 280 s.modelStartEnd[0] += atomSetCount0; 281 s.modelStartEnd[1] += atomSetCount0; 282 } 283 } 284 setNoAutoBond()285 public void setNoAutoBond() { 286 setInfo("noAutoBond", Boolean.TRUE); 287 } 288 freeze(boolean reverseModels)289 void freeze(boolean reverseModels) { 290 if (atomSetCount == 1 && collectionName == null) 291 collectionName = (String) getAtomSetAuxiliaryInfoValue(0, "name"); 292 //Logger.debug("AtomSetCollection.freeze; ac = " + ac); 293 if (reverseModels) 294 reverseAtomSets(); 295 if (trajectoryStepCount > 1) 296 finalizeTrajectory(); 297 getList(true); 298 getList(false); 299 for (int i = 0; i < atomSetCount; i++) { 300 setModelInfoForSet("initialAtomCount", 301 Integer.valueOf(atomSetAtomCounts[i]), i); 302 setModelInfoForSet("initialBondCount", 303 Integer.valueOf(atomSetBondCounts[i]), i); 304 } 305 } 306 reverseAtomSets()307 private void reverseAtomSets() { 308 reverseArray(atomSetAtomIndexes); 309 reverseArray(atomSetNumbers); 310 reverseArray(atomSetAtomCounts); 311 reverseArray(atomSetBondCounts); 312 reverseList(trajectorySteps); 313 reverseList(trajectoryNames); 314 reverseList(vibrationSteps); 315 reverseObject(atomSetAuxiliaryInfo); 316 for (int i = 0; i < ac; i++) 317 atoms[i].atomSetIndex = atomSetCount - 1 - atoms[i].atomSetIndex; 318 for (int i = 0; i < structureCount; i++) { 319 int m = structures[i].modelStartEnd[0]; 320 if (m >= 0) { 321 structures[i].modelStartEnd[0] = atomSetCount - 1 322 - structures[i].modelStartEnd[1]; 323 structures[i].modelStartEnd[1] = atomSetCount - 1 - m; 324 } 325 } 326 for (int i = 0; i < bondCount; i++) 327 bonds[i].atomSetIndex = atomSetCount - 1 328 - atoms[bonds[i].atomIndex1].atomSetIndex; 329 reverseSets(bonds, bondCount); 330 //getAtomSetAuxiliaryInfo("PDB_CONECT_firstAtom_count_max" ?? 331 Lst<Atom>[] lists = AU.createArrayOfArrayList(atomSetCount); 332 for (int i = 0; i < atomSetCount; i++) 333 lists[i] = new Lst<Atom>(); 334 for (int i = 0; i < ac; i++) 335 lists[atoms[i].atomSetIndex].addLast(atoms[i]); 336 int[] newIndex = new int[ac]; 337 int n = ac; 338 for (int i = atomSetCount; --i >= 0;) 339 for (int j = lists[i].size(); --j >= 0;) { 340 Atom a = atoms[--n] = lists[i].get(j); 341 newIndex[a.index] = n; 342 a.index = n; 343 } 344 for (int i = 0; i < bondCount; i++) { 345 bonds[i].atomIndex1 = newIndex[bonds[i].atomIndex1]; 346 bonds[i].atomIndex2 = newIndex[bonds[i].atomIndex2]; 347 } 348 for (int i = 0; i < atomSetCount; i++) { 349 int[] conect = (int[]) getAtomSetAuxiliaryInfoValue(i, 350 "PDB_CONECT_firstAtom_count_max"); 351 if (conect == null) 352 continue; 353 conect[0] = newIndex[conect[0]]; 354 conect[1] = atomSetAtomCounts[i]; 355 } 356 } 357 reverseSets(AtomSetObject[] o, int n)358 private void reverseSets(AtomSetObject[] o, int n) { 359 Lst<AtomSetObject>[] lists = AU.createArrayOfArrayList(atomSetCount); 360 for (int i = 0; i < atomSetCount; i++) 361 lists[i] = new Lst<AtomSetObject>(); 362 for (int i = 0; i < n; i++) { 363 int index = o[i].atomSetIndex; 364 if (index < 0) 365 return; 366 lists[o[i].atomSetIndex].addLast(o[i]); 367 } 368 for (int i = atomSetCount; --i >= 0;) 369 for (int j = lists[i].size(); --j >= 0;) 370 o[--n] = lists[i].get(j); 371 } 372 reverseObject(Object[] o)373 private void reverseObject(Object[] o) { 374 int n = atomSetCount; 375 for (int i = n / 2; --i >= 0;) 376 AU.swap(o, i, n - 1 - i); 377 } 378 reverseList(Lst<?> list)379 private static void reverseList(Lst<?> list) { 380 if (list == null) 381 return; 382 Collections.reverse(list); 383 } 384 reverseArray(int[] a)385 private void reverseArray(int[] a) { 386 int n = atomSetCount; 387 for (int i = n / 2; --i >= 0;) 388 AU.swapInt(a, i, n - 1 - i); 389 } 390 getList(boolean isAltLoc)391 private void getList(boolean isAltLoc) { 392 int i; 393 for (i = ac; --i >= 0;) 394 if (atoms[i] != null 395 && (isAltLoc ? atoms[i].altLoc : atoms[i].insertionCode) != '\0') 396 break; 397 if (i < 0) 398 return; 399 String[] lists = new String[atomSetCount]; 400 for (i = 0; i < atomSetCount; i++) 401 lists[i] = ""; 402 int pt; 403 for (i = 0; i < ac; i++) { 404 if (atoms[i] == null) 405 continue; 406 char id = (isAltLoc ? atoms[i].altLoc : atoms[i].insertionCode); 407 if (id != '\0' && lists[pt = atoms[i].atomSetIndex].indexOf(id) < 0) 408 lists[pt] += id; 409 } 410 String type = (isAltLoc ? "altLocs" : "insertionCodes"); 411 for (i = 0; i < atomSetCount; i++) 412 if (lists[i].length() > 0) 413 setModelInfoForSet(type, lists[i], i); 414 } 415 finish()416 void finish() { 417 if (reader != null) 418 reader.finalizeModelSet(); 419 else if (readerList != null) 420 for (int i = 0; i < readerList.size(); i++) 421 readerList.get(i).finalizeModelSet(); 422 atoms = null; 423 atomSetAtomCounts = new int[16]; 424 atomSetAuxiliaryInfo = new Hashtable[16]; 425 atomSetInfo = new Hashtable<String, Object>(); 426 atomSetCount = 0; 427 atomSetNumbers = new int[16]; 428 atomSymbolicMap = new Hashtable<String, Atom>(); 429 bonds = null; 430 iSet = -1; 431 readerList = null; 432 xtalSymmetry = null; 433 structures = new Structure[16]; 434 structureCount = 0; 435 trajectorySteps = null; 436 vibrationSteps = null; 437 } 438 discardPreviousAtoms()439 public void discardPreviousAtoms() { 440 for (int i = ac; --i >= 0;) 441 atoms[i] = null; 442 ac = 0; 443 atomSymbolicMap.clear(); 444 atomMapAnyCase = false; 445 atomSetCount = 0; 446 iSet = -1; 447 for (int i = atomSetAuxiliaryInfo.length; --i >= 0;) { 448 atomSetAtomCounts[i] = 0; 449 atomSetBondCounts[i] = 0; 450 atomSetAuxiliaryInfo[i] = null; 451 } 452 } 453 removeCurrentAtomSet()454 public void removeCurrentAtomSet() { 455 if (iSet < 0) 456 return; 457 int ai = atomSetAtomIndexes[iSet]; 458 if (bsAtoms != null) 459 bsAtoms.clearBits(ai, ac); 460 ac = ai; 461 atomSetAtomCounts[iSet] = 0; 462 iSet--; 463 atomSetCount--; 464 reader.doCheckUnitCell = false; 465 } 466 getHydrogenAtomCount()467 public int getHydrogenAtomCount() { 468 int n = 0; 469 for (int i = 0; i < ac; i++) 470 if (atoms[i].elementNumber == 1 || atoms[i].elementSymbol.equals("H")) 471 n++; 472 return n; 473 } 474 newCloneAtom(Atom atom)475 public Atom newCloneAtom(Atom atom) { 476 Atom clone = atom.getClone(); 477 addAtom(clone); 478 return clone; 479 } 480 481 // FIX ME This should really also clone the other things pertaining 482 // to an atomSet, like the bonds (which probably should be remade...) 483 // but also the atomSetProperties and atomSetName... cloneFirstAtomSet(int atomCount)484 public int cloneFirstAtomSet(int atomCount) { 485 if (!allowMultiple) 486 return 0; 487 newAtomSet(); 488 if (atomCount == 0) 489 atomCount = atomSetAtomCounts[0]; 490 for (int i = 0; i < atomCount; ++i) 491 newCloneAtom(atoms[i]); 492 return ac; 493 } 494 cloneAtomSetWithBonds(boolean isLast)495 public void cloneAtomSetWithBonds(boolean isLast) { 496 int nBonds = atomSetBondCounts[isLast ? iSet : 0]; 497 int atomIncrement = (isLast ? cloneLastAtomSet() : cloneFirstAtomSet(0)); 498 if (atomIncrement > 0) 499 for (int i = 0; i < nBonds; i++) { 500 Bond bond = bonds[bondCount - nBonds]; 501 addNewBondWithOrder(bond.atomIndex1 + atomIncrement, bond.atomIndex2 502 + atomIncrement, bond.order); 503 } 504 } 505 cloneLastAtomSet()506 public int cloneLastAtomSet() {//throws Exception { 507 return cloneLastAtomSetFromPoints(0, null); 508 } 509 cloneLastAtomSetFromPoints(int ac, P3[] pts)510 public int cloneLastAtomSetFromPoints(int ac, P3[] pts) { 511 if (!allowMultiple) // CASTEP reader only 512 return 0; 513 int count = (ac > 0 ? ac : getLastAtomSetAtomCount()); 514 int atomIndex = getLastAtomSetAtomIndex(); 515 newAtomSet(); 516 for (int i = 0; i < count; ++i) { 517 Atom atom = newCloneAtom(atoms[atomIndex++]); 518 if (pts != null) 519 atom.setT(pts[i]); 520 } 521 return count; 522 } 523 getLastAtomSetAtomCount()524 public int getLastAtomSetAtomCount() { 525 return atomSetAtomCounts[iSet]; 526 } 527 getLastAtomSetAtomIndex()528 public int getLastAtomSetAtomIndex() { 529 //Logger.debug("atomSetCount=" + atomSetCount); 530 return ac - atomSetAtomCounts[iSet]; 531 } 532 addNewAtom()533 public Atom addNewAtom() { 534 return addAtom(new Atom()); 535 } 536 addAtom(Atom atom)537 public Atom addAtom(Atom atom) { 538 if (ac == atoms.length) { 539 if (ac > 200000) 540 atoms = (Atom[]) AU.ensureLength(atoms, ac + 50000); 541 else 542 atoms = (Atom[]) AU.doubleLength(atoms); 543 } 544 if (atomSetCount == 0) 545 newAtomSet(); 546 atom.index = ac; 547 atoms[ac++] = atom; 548 atom.atomSetIndex = iSet; 549 atom.atomSite = atomSetAtomCounts[iSet]++; 550 return atom; 551 } 552 addAtomWithMappedName(Atom atom)553 public void addAtomWithMappedName(Atom atom) { 554 String atomName = addAtom(atom).atomName; 555 if (atomName != null) 556 atomSymbolicMap.put(atomName, atom); 557 } 558 addAtomWithMappedSerialNumber(Atom atom)559 public void addAtomWithMappedSerialNumber(Atom atom) { 560 int atomSerial = addAtom(atom).atomSerial; 561 if (atomSerial != Integer.MIN_VALUE) 562 atomSymbolicMap.put("" + atomSerial, atom); 563 } 564 getAtomFromName(String atomName)565 public Atom getAtomFromName(String atomName) { 566 return atomSymbolicMap.get(atomName); 567 } 568 setAtomMapAnyCase()569 public void setAtomMapAnyCase() { 570 atomMapAnyCase = true; 571 Map<String, Atom> newMap = new Hashtable<String, Atom>(); 572 newMap.putAll(atomSymbolicMap); 573 for (Map.Entry<String, Atom> e : atomSymbolicMap.entrySet()) { 574 String name = e.getKey(); 575 String uc = name.toUpperCase(); 576 if (!uc.equals(name)) 577 newMap.put(uc, e.getValue()); 578 } 579 atomSymbolicMap = newMap; 580 } 581 getAtomIndex(String name)582 public int getAtomIndex(String name) { 583 Atom a = atomSymbolicMap.get(name); 584 if (a == null && atomMapAnyCase) 585 a = atomSymbolicMap.get(name.toUpperCase()); 586 return (a == null ? -1 : a.index); 587 } 588 addNewBondWithOrder(int atomIndex1, int atomIndex2, int order)589 public Bond addNewBondWithOrder(int atomIndex1, int atomIndex2, int order) { 590 Bond b = null; 591 if (atomIndex1 >= 0 && atomIndex1 < ac && atomIndex2 >= 0 592 && atomIndex2 < ac && atomIndex1 != atomIndex2) { 593 b = new Bond(atomIndex1, atomIndex2, order); 594 addBond(b); 595 } 596 return b; 597 } 598 addNewBondFromNames(String atomName1, String atomName2, int order)599 public void addNewBondFromNames(String atomName1, String atomName2, int order) { 600 addNewBondWithOrderA(getAtomFromName(atomName1), getAtomFromName(atomName2), order); 601 } 602 addNewBondWithOrderA(Atom atom1, Atom atom2, int order)603 public void addNewBondWithOrderA(Atom atom1, Atom atom2, 604 int order) { 605 if (atom1 != null && atom2 != null) 606 addNewBondWithOrder(atom1.index, atom2.index, order); 607 } 608 addBond(Bond bond)609 public void addBond(Bond bond) { 610 if (trajectoryStepCount > 0) 611 return; 612 if (bond.atomIndex1 < 0 || bond.atomIndex2 < 0 613 || bond.order < 0 614 || bond.atomIndex1 == bond.atomIndex2 615 || 616 //do not allow bonds between models 617 atoms[bond.atomIndex1].atomSetIndex != atoms[bond.atomIndex2].atomSetIndex) { 618 if (Logger.debugging) { 619 Logger.debug(">>>>>>BAD BOND:" + bond.atomIndex1 + "-" 620 + bond.atomIndex2 + " order=" + bond.order); 621 } 622 return; 623 } 624 if (bondCount == bonds.length) 625 bonds = (Bond[]) AU.arrayCopyObject(bonds, bondCount + 1024); 626 bonds[bondCount++] = bond; 627 atomSetBondCounts[iSet]++; 628 } 629 630 public BS bsStructuredModels; 631 finalizeStructures()632 public void finalizeStructures() { 633 if (structureCount == 0) 634 return; 635 bsStructuredModels = new BS(); 636 Map<String, Integer> map = new Hashtable<String, Integer>(); 637 for (int i = 0; i < structureCount; i++) { 638 Structure s = structures[i]; 639 if (s.modelStartEnd[0] == -1) { 640 s.modelStartEnd[0] = 0; 641 s.modelStartEnd[1] = atomSetCount - 1; 642 } 643 bsStructuredModels.setBits(s.modelStartEnd[0], s.modelStartEnd[1] + 1); 644 if (s.strandCount == 0) 645 continue; 646 String key = s.structureID + " " + s.modelStartEnd[0]; 647 Integer v = map.get(key); 648 int count = (v == null ? 0 : v.intValue()) + 1; 649 map.put(key, Integer.valueOf(count)); 650 } 651 for (int i = 0; i < structureCount; i++) { 652 Structure s = structures[i]; 653 if (s.strandCount == 1) 654 s.strandCount = map.get(s.structureID + " " + s.modelStartEnd[0]) 655 .intValue(); 656 } 657 } 658 addStructure(Structure structure)659 public void addStructure(Structure structure) { 660 if (structureCount == structures.length) 661 structures = (Structure[]) AU.arrayCopyObject(structures, 662 structureCount + 32); 663 structures[structureCount++] = structure; 664 } 665 addVibrationVectorWithSymmetry(int iatom, float vx, float vy, float vz, boolean withSymmetry)666 public void addVibrationVectorWithSymmetry(int iatom, float vx, float vy, 667 float vz, boolean withSymmetry) { 668 if (!withSymmetry) { 669 addVibrationVector(iatom, vx, vy, vz); 670 return; 671 } 672 int atomSite = atoms[iatom].atomSite; 673 int atomSetIndex = atoms[iatom].atomSetIndex; 674 for (int i = iatom; i < ac && atoms[i].atomSetIndex == atomSetIndex; i++) { 675 //TODO check this: Shouldn't we be symmetrizing here? 676 if (atoms[i].atomSite == atomSite) 677 addVibrationVector(i, vx, vy, vz); 678 } 679 } 680 addVibrationVector(int iatom, float x, float y, float z)681 public V3 addVibrationVector(int iatom, float x, float y, float z) { 682 if (!allowMultiple) 683 iatom = iatom % ac; 684 return (atoms[iatom].vib = V3.new3(x, y, z)); 685 } 686 setCoordinatesAreFractional(boolean tf)687 public void setCoordinatesAreFractional(boolean tf) { 688 coordinatesAreFractional = tf; 689 setCurrentModelInfo("coordinatesAreFractional", Boolean.valueOf(tf)); 690 if (tf) 691 setGlobalBoolean(GLOBAL_FRACTCOORD); 692 } 693 694 public boolean haveAnisou; 695 setAnisoBorU(Atom atom, float[] data, int type)696 public void setAnisoBorU(Atom atom, float[] data, int type) { 697 haveAnisou = true; 698 atom.anisoBorU = data; 699 data[6] = type; 700 } 701 setU(Atom atom, int i, float val)702 public void setU(Atom atom, int i, float val) { 703 // Ortep Type 8: D = 2pi^2, C = 2, a*b* 704 float[] data = atom.anisoBorU; 705 if (data == null) 706 setAnisoBorU(atom, data = new float[8], 8); 707 data[i] = val; 708 } 709 710 public int baseSymmetryAtomCount; 711 public boolean checkLatticeOnly; 712 713 public XtalSymmetry xtalSymmetry; 714 getXSymmetry()715 public XtalSymmetry getXSymmetry() { 716 if (xtalSymmetry == null) 717 xtalSymmetry = ((XtalSymmetry) Interface 718 .getOption("adapter.smarter.XtalSymmetry", reader.vwr, "file")).set(reader); 719 return xtalSymmetry; 720 } 721 getSymmetry()722 public SymmetryInterface getSymmetry() { 723 return getXSymmetry().getSymmetry(); 724 } 725 setSymmetry(SymmetryInterface symmetry)726 public SymmetryInterface setSymmetry(SymmetryInterface symmetry) { 727 return (symmetry == null ? null : getXSymmetry().setSymmetry(symmetry)); 728 } 729 setTensors()730 public void setTensors() { 731 if (haveAnisou) 732 getXSymmetry().setTensors(); 733 } 734 735 int bondIndex0; 736 737 public boolean checkSpecial = true; 738 739 public Map<String, Atom> atomSymbolicMap = new Hashtable<String, Atom>(); 740 741 public boolean haveUnitCell; 742 743 public int vibScale; 744 setInfo(String key, Object value)745 public void setInfo(String key, Object value) { 746 if (value == null) 747 atomSetInfo.remove(key); 748 else 749 atomSetInfo.put(key, value); 750 } 751 752 /** 753 * Sets the partial atomic charges based on asc auxiliary info 754 * 755 * @param auxKey 756 * The auxiliary key name that contains the charges 757 * @return true if the data exist; false if not 758 */ 759 setAtomSetCollectionPartialCharges(String auxKey)760 public boolean setAtomSetCollectionPartialCharges(String auxKey) { 761 if (!atomSetInfo.containsKey(auxKey)) 762 return false; 763 Lst<Float> atomData = (Lst<Float>) atomSetInfo.get(auxKey); 764 int n = atomData.size(); 765 for (int i = ac; --i >= 0;) 766 atoms[i].partialCharge = atomData.get(i % n).floatValue(); 767 Logger.info("Setting partial charges type " + auxKey); 768 return true; 769 } 770 mapPartialCharge(String atomName, float charge)771 public void mapPartialCharge(String atomName, float charge) { 772 getAtomFromName(atomName).partialCharge = charge; 773 } 774 775 //////////////////////////////////////////////////////////////// 776 // atomSet stuff 777 //////////////////////////////////////////////////////////////// 778 fixPeriodic(P3 pt, P3 pt0)779 private static P3 fixPeriodic(P3 pt, P3 pt0) { 780 pt.x = fixPoint(pt.x, pt0.x); 781 pt.y = fixPoint(pt.y, pt0.y); 782 pt.z = fixPoint(pt.z, pt0.z); 783 return pt; 784 } 785 fixPoint(float x, float x0)786 private static float fixPoint(float x, float x0) { 787 while (x - x0 > 0.9) { 788 x -= 1; 789 } 790 while (x - x0 < -0.9) { 791 x += 1; 792 } 793 return x; 794 } 795 finalizeTrajectoryAs(Lst<P3[]> trajectorySteps, Lst<V3[]> vibrationSteps)796 public void finalizeTrajectoryAs(Lst<P3[]> trajectorySteps, 797 Lst<V3[]> vibrationSteps) { 798 this.trajectorySteps = trajectorySteps; 799 this.vibrationSteps = vibrationSteps; 800 trajectoryStepCount = trajectorySteps.size(); 801 finalizeTrajectory(); 802 } 803 finalizeTrajectory()804 private void finalizeTrajectory() { 805 if (trajectoryStepCount == 0) 806 return; 807 //reset atom positions to original trajectory 808 809 810 P3[] trajectory = trajectorySteps.get(0); 811 812 813 V3[] vibrations = (vibrationSteps == null ? null : vibrationSteps.get(0)); 814 int n = (bsAtoms == null ? ac : bsAtoms.cardinality()); 815 if (vibrationSteps != null && vibrations != null && vibrations.length < n 816 || trajectory.length < n) { 817 errorMessage = "File cannot be loaded as a trajectory"; 818 return; 819 } 820 V3 v = new V3(); 821 for (int i = 0, ii = 0; i < ac; i++) { 822 if (bsAtoms != null && !bsAtoms.get(i)) 823 continue; 824 if (vibrationSteps != null) 825 atoms[i].vib = (vibrations == null ? v : vibrations[ii]); 826 if (trajectory[ii] != null) 827 atoms[i].setT(trajectory[ii]); 828 ii++; 829 } 830 setInfo("trajectorySteps", trajectorySteps); 831 if (vibrationSteps != null) 832 setInfo("vibrationSteps", vibrationSteps); 833 if (ac == 0) 834 ac = trajectory.length; 835 } 836 837 /** 838 * Create a new atoms set, clearing the atom map 839 * 840 */ newAtomSet()841 public void newAtomSet() { 842 newAtomSetClear(true); 843 } 844 845 /** 846 * Create a new atom set, optionally clearing the atom map. 847 * 848 * @param doClearMap set to false only in CastepReader 849 */ newAtomSetClear(boolean doClearMap)850 public void newAtomSetClear(boolean doClearMap) { 851 852 // we call reader.discardPreviousAtoms here because it may be 853 // overridden to do more than we do here (such as in BasisFunctionReader). 854 if (!allowMultiple && iSet >= 0) 855 reader.discardPreviousAtoms(); 856 bondIndex0 = bondCount; 857 if (isTrajectory) 858 reader.discardPreviousAtoms(); 859 iSet = atomSetCount++; 860 if (atomSetCount > atomSetNumbers.length) { 861 atomSetAtomIndexes = AU.doubleLengthI(atomSetAtomIndexes); 862 atomSetAtomCounts = AU.doubleLengthI(atomSetAtomCounts); 863 atomSetBondCounts = AU.doubleLengthI(atomSetBondCounts); 864 atomSetAuxiliaryInfo = (Map<String, Object>[]) AU 865 .doubleLength(atomSetAuxiliaryInfo); 866 } 867 atomSetAtomIndexes[iSet] = ac; 868 if (atomSetCount + trajectoryStepCount > atomSetNumbers.length) { 869 atomSetNumbers = AU.doubleLengthI(atomSetNumbers); 870 } 871 if (isTrajectory) { 872 atomSetNumbers[iSet + trajectoryStepCount] = atomSetCount 873 + trajectoryStepCount; 874 } else { 875 atomSetNumbers[iSet] = atomSetCount; 876 } 877 if (doClearMap) { // false for CASTEP reader only 878 atomSymbolicMap.clear(); 879 atomMapAnyCase = false; 880 } 881 setCurrentModelInfo("title", collectionName); 882 } 883 getAtomSetAtomIndex(int i)884 public int getAtomSetAtomIndex(int i) { 885 if (i < 0) 886 System.out.println("??"); 887 return atomSetAtomIndexes[i]; 888 } 889 getAtomSetAtomCount(int i)890 public int getAtomSetAtomCount(int i) { 891 return atomSetAtomCounts[i]; 892 } 893 getAtomSetBondCount(int i)894 public int getAtomSetBondCount(int i) { 895 return atomSetBondCounts[i]; 896 } 897 898 /** 899 * Sets the name for the current AtomSet 900 * 901 * @param atomSetName 902 * The name to be associated with the current AtomSet 903 */ setAtomSetName(String atomSetName)904 public void setAtomSetName(String atomSetName) { 905 if (atomSetName == null) 906 return; 907 if (isTrajectory) { 908 setTrajectoryName(atomSetName); 909 return; 910 } 911 String name0 = (iSet < 0 ? null : getAtomSetName(iSet)); 912 setModelInfoForSet("name", atomSetName, iSet); 913 if (reader != null && atomSetName.length() > 0 && !atomSetName.equals(name0)) 914 reader.appendLoadNote(atomSetName); 915 // TODO -- trajectories could have different names. Need this for vibrations? 916 if (!allowMultiple) 917 setCollectionName(atomSetName); 918 } 919 setTrajectoryName(String name)920 private void setTrajectoryName(String name) { 921 if (trajectoryStepCount == 0) 922 return; 923 if (trajectoryNames == null) { 924 trajectoryNames = new Lst<String>(); 925 } 926 for (int i = trajectoryNames.size(); i < trajectoryStepCount; i++) 927 trajectoryNames.addLast(null); 928 trajectoryNames.set(trajectoryStepCount - 1, name); 929 } 930 931 /** 932 * Sets the number for the current AtomSet 933 * 934 * @param atomSetNumber 935 * The number for the current AtomSet. 936 */ setCurrentAtomSetNumber(int atomSetNumber)937 public void setCurrentAtomSetNumber(int atomSetNumber) { 938 setAtomSetNumber(iSet + (isTrajectory ? trajectoryStepCount : 0), 939 atomSetNumber); 940 } 941 setAtomSetNumber(int index, int atomSetNumber)942 public void setAtomSetNumber(int index, int atomSetNumber) { 943 atomSetNumbers[index] = atomSetNumber; 944 } 945 946 /** 947 * Sets a property for the current AtomSet used specifically for creating 948 * directories and plots of frequencies and molecular energies 949 * 950 * @param key 951 * The key for the property 952 * @param value 953 * The value to be associated with the key 954 */ setAtomSetModelProperty(String key, String value)955 public void setAtomSetModelProperty(String key, String value) { 956 setAtomSetModelPropertyForSet(key, value, iSet); 957 } 958 959 /** 960 * Sets the a property for the an AtomSet 961 * 962 * @param key 963 * The key for the property 964 * @param value 965 * The value for the property 966 * @param atomSetIndex 967 * The index of the AtomSet to get the property 968 */ setAtomSetModelPropertyForSet(String key, String value, int atomSetIndex)969 public void setAtomSetModelPropertyForSet(String key, String value, 970 int atomSetIndex) { 971 // lazy instantiation of the Properties object 972 Properties p = (Properties) getAtomSetAuxiliaryInfoValue(atomSetIndex, 973 "modelProperties"); 974 if (p == null) 975 setModelInfoForSet("modelProperties", p = new Properties(), 976 atomSetIndex); 977 p.put(key, value); 978 if (key.startsWith(".")) //.PATH will not be usable in Jmol 979 p.put(key.substring(1), value); 980 } 981 982 /** 983 * @param key 984 * @param data 985 * @param atomSetIndex 986 * @param isGroup 987 */ setAtomProperties(String key, Object data, int atomSetIndex, boolean isGroup)988 public void setAtomProperties(String key, Object data, int atomSetIndex, boolean isGroup) { 989 if (data instanceof String && !((String) data).endsWith("\n")) 990 data = data + "\n"; 991 if (atomSetIndex < 0) 992 atomSetIndex = iSet; 993 Map<String, Object> p = (Map<String, Object>) getAtomSetAuxiliaryInfoValue( 994 atomSetIndex, "atomProperties"); 995 if (p == null) 996 setModelInfoForSet("atomProperties", 997 p = new Hashtable<String, Object>(), atomSetIndex); 998 p.put(key, data); 999 } 1000 1001 /** 1002 * Sets the partial atomic charges based on atomSet auxiliary info 1003 * 1004 * @param auxKey 1005 * The auxiliary key name that contains the charges 1006 * @return true if the data exist; false if not 1007 */ 1008 setAtomSetPartialCharges(String auxKey)1009 boolean setAtomSetPartialCharges(String auxKey) { 1010 if (!atomSetAuxiliaryInfo[iSet].containsKey(auxKey)) { 1011 return false; 1012 } 1013 Lst<Float> atomData = (Lst<Float>) getAtomSetAuxiliaryInfoValue(iSet, 1014 auxKey); 1015 for (int i = atomData.size(); --i >= 0;) { 1016 atoms[i].partialCharge = atomData.get(i).floatValue(); 1017 } 1018 return true; 1019 } 1020 getAtomSetAuxiliaryInfoValue(int index, String key)1021 public Object getAtomSetAuxiliaryInfoValue(int index, String key) { 1022 return atomSetAuxiliaryInfo[index >= 0 ? index : iSet].get(key); 1023 } 1024 1025 /** 1026 * Sets auxiliary information for the AtomSet 1027 * 1028 * @param key 1029 * The key for the property 1030 * @param value 1031 * The value to be associated with the key 1032 */ setCurrentModelInfo(String key, Object value)1033 public void setCurrentModelInfo(String key, Object value) { 1034 setModelInfoForSet(key, value, iSet); 1035 } 1036 1037 /** 1038 * Sets auxiliary information for an AtomSet 1039 * 1040 * @param key 1041 * The key for the property 1042 * @param value 1043 * The value for the property 1044 * @param atomSetIndex 1045 * The index of the AtomSet to get the property 1046 */ setModelInfoForSet(String key, Object value, int atomSetIndex)1047 public void setModelInfoForSet(String key, Object value, 1048 int atomSetIndex) { 1049 if (atomSetIndex < 0) 1050 return; 1051 if (atomSetAuxiliaryInfo[atomSetIndex] == null) 1052 atomSetAuxiliaryInfo[atomSetIndex] = new Hashtable<String, Object>(); 1053 if (value == null) 1054 atomSetAuxiliaryInfo[atomSetIndex].remove(key); 1055 else 1056 atomSetAuxiliaryInfo[atomSetIndex].put(key, value); 1057 } 1058 getAtomSetNumber(int atomSetIndex)1059 int getAtomSetNumber(int atomSetIndex) { 1060 return atomSetNumbers[atomSetIndex >= atomSetCount ? 0 : atomSetIndex]; 1061 } 1062 getAtomSetName(int atomSetIndex)1063 String getAtomSetName(int atomSetIndex) { 1064 if (trajectoryNames != null && atomSetIndex < trajectoryNames.size()) 1065 return trajectoryNames.get(atomSetIndex); 1066 if (atomSetIndex >= atomSetCount) 1067 atomSetIndex = atomSetCount - 1; 1068 return (String) getAtomSetAuxiliaryInfoValue(atomSetIndex, "name"); 1069 } 1070 getAtomSetAuxiliaryInfo(int atomSetIndex)1071 public Map<String, Object> getAtomSetAuxiliaryInfo(int atomSetIndex) { 1072 int i = (atomSetIndex >= atomSetCount ? atomSetCount - 1 1073 : atomSetIndex); 1074 return (i < 0 ? null : atomSetAuxiliaryInfo[i]); 1075 } 1076 1077 //// for XmlChem3dReader, but could be for CUBE 1078 setAtomSetEnergy(String energyString, float value)1079 public void setAtomSetEnergy(String energyString, float value) { 1080 if (iSet < 0) 1081 return; 1082 Logger.info("Energy for model " + (iSet + 1) + " = " + energyString); 1083 setCurrentModelInfo("EnergyString", energyString); 1084 setCurrentModelInfo("Energy", Float.valueOf(value)); 1085 setAtomSetModelProperty("Energy", "" + value); 1086 } 1087 setAtomSetFrequency(int mode, String pathKey, String label, String freq, String units)1088 public String setAtomSetFrequency(int mode, String pathKey, String label, 1089 String freq, String units) { 1090 setAtomSetModelProperty("FreqValue", freq); 1091 freq += " " + (units == null ? "cm^-1" : units); 1092 String name = (label == null ? "" : label + " ") + freq; 1093 setAtomSetName(name); 1094 setAtomSetModelProperty("Frequency", freq); 1095 setAtomSetModelProperty("Mode", "" + mode); 1096 setModelInfoForSet("vibrationalMode", Integer.valueOf(mode), iSet); 1097 if (label != null) 1098 setAtomSetModelProperty("FrequencyLabel", label); 1099 setAtomSetModelProperty(SmarterJmolAdapter.PATH_KEY, (pathKey == null ? "" 1100 : pathKey + SmarterJmolAdapter.PATH_SEPARATOR + "Frequencies") 1101 + "Frequencies"); 1102 return name; 1103 } 1104 getBondList()1105 public String[][] getBondList() { 1106 String[][] info = new String[bondCount][]; 1107 for (int i = 0; i < bondCount; i++) { 1108 info[i] = new String[] { atoms[bonds[i].atomIndex1].atomName, 1109 atoms[bonds[i].atomIndex2].atomName, "" + bonds[i].order }; 1110 } 1111 return info; 1112 } 1113 centralize()1114 public void centralize() { 1115 P3 pt = new P3(); 1116 for (int i = 0; i < atomSetCount; i++) { 1117 int n = atomSetAtomCounts[i]; 1118 int atom0 = atomSetAtomIndexes[i]; 1119 pt.set(0, 0, 0); 1120 for (int j = atom0 + n; --j >= atom0;) 1121 pt.add(atoms[j]); 1122 pt.scale(1f / n); 1123 for (int j = atom0 + n; --j >= atom0;) 1124 atoms[j].sub(pt); 1125 } 1126 } 1127 mergeTrajectories(AtomSetCollection a)1128 void mergeTrajectories(AtomSetCollection a) { 1129 if (!isTrajectory || !a.isTrajectory || vibrationSteps != null) 1130 return; 1131 for (int i = 0; i < a.trajectoryStepCount; i++) 1132 trajectorySteps.add(trajectoryStepCount++, a.trajectorySteps.get(i)); 1133 setInfo("trajectorySteps", trajectorySteps); 1134 setInfo("ignoreUnitCell", a.atomSetInfo.get("ignoreUnitCell")); 1135 } 1136 1137 /** 1138 * note that sets must be iterated from LAST to FIRST 1139 * not a general method -- would mess up if we had unit cells 1140 * 1141 * @param imodel 1142 */ removeAtomSet(int imodel)1143 public void removeAtomSet(int imodel) { 1144 if (bsAtoms == null) 1145 bsAtoms = BSUtil.newBitSet2(0, ac); 1146 int i0 = atomSetAtomIndexes[imodel]; 1147 int nAtoms = atomSetAtomCounts[imodel]; 1148 int i1 = i0 + nAtoms; 1149 bsAtoms.clearBits(i0, i1); 1150 for (int i = i1; i < ac; i++) 1151 atoms[i].atomSetIndex--; 1152 for (int i = imodel + 1; i < atomSetCount; i++) { 1153 atomSetAuxiliaryInfo[i - 1] = atomSetAuxiliaryInfo[i]; 1154 atomSetAtomIndexes[i - 1] = atomSetAtomIndexes[i]; 1155 atomSetBondCounts[i - 1] = atomSetBondCounts[i]; 1156 atomSetAtomCounts[i - 1] = atomSetAtomCounts[i]; 1157 atomSetNumbers[i - 1] = atomSetNumbers[i]; 1158 } 1159 for (int i = 0; i < bondCount; i++) 1160 bonds[i].atomSetIndex = atoms[bonds[i].atomIndex1].atomSetIndex; 1161 atomSetAuxiliaryInfo[--atomSetCount] = null; 1162 int n = 0; 1163 for (int i = 0; i < structureCount; i++) { 1164 Structure s = structures[i]; 1165 if (s.modelStartEnd[0] == imodel && s.modelStartEnd[1] == imodel) { 1166 structures[i] = null; 1167 n++; 1168 } 1169 } 1170 if (n > 0) { 1171 Structure[] ss = new Structure[structureCount - n]; 1172 for (int i = 0, pt = 0; i < structureCount; i++) 1173 if (structures[i] != null) 1174 ss[pt++] = structures[i]; 1175 structures = ss; 1176 } 1177 } 1178 removeLastUnselectedAtoms()1179 public void removeLastUnselectedAtoms() { 1180 int n = ac; 1181 int nremoved = 0; 1182 int i0 = getLastAtomSetAtomIndex(); 1183 int nnow = 0; 1184 for (int i = i0; i < n; i++) { 1185 if (!bsAtoms.get(i)) { 1186 nremoved++; 1187 ac--; 1188 atoms[i] = null; 1189 continue; 1190 } 1191 if (nremoved > 0) { 1192 atoms[atoms[i].index = i - nremoved] = atoms[i]; 1193 atoms[i] = null; 1194 } 1195 nnow++; 1196 } 1197 atomSetAtomCounts[iSet] = nnow; 1198 if (nnow == 0) { 1199 iSet--; 1200 atomSetCount--; 1201 } else { 1202 bsAtoms.setBits(i0, i0 + nnow); 1203 } 1204 } 1205 checkNoEmptyModel()1206 public void checkNoEmptyModel() { 1207 while (atomSetCount > 0 && atomSetAtomCounts[atomSetCount - 1] == 0) 1208 atomSetCount--; 1209 } 1210 1211 1212 } 1213