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