1 /* $RCSfile$
2  * $Author: egonw $
3  * $Date: 2005-11-10 09:52:44 -0600 (Thu, 10 Nov 2005) $
4  * $Revision: 4255 $
5  *
6  * Copyright (C) 2004-2005  The Jmol Development Team
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.modelsetbio;
26 
27 import java.util.Arrays;
28 
29 import java.util.Comparator;
30 import java.util.Hashtable;
31 import java.util.Map;
32 import java.util.Properties;
33 
34 
35 import javajs.util.BS;
36 import org.jmol.modelset.Atom;
37 import org.jmol.modelset.AtomCollection;
38 import org.jmol.modelset.Bond;
39 import org.jmol.modelset.Chain;
40 import org.jmol.modelset.Group;
41 import org.jmol.modelset.Model;
42 import org.jmol.modelset.ModelLoader;
43 import org.jmol.modelset.ModelSet;
44 import org.jmol.script.SV;
45 import org.jmol.script.T;
46 import org.jmol.util.BSUtil;
47 import org.jmol.util.Edge;
48 import org.jmol.util.Logger;
49 
50 import javajs.util.AU;
51 import javajs.util.Measure;
52 import javajs.util.PT;
53 import javajs.util.SB;
54 import javajs.util.P3;
55 import javajs.util.P4;
56 import javajs.util.V3;
57 import org.jmol.viewer.JC;
58 import org.jmol.viewer.Viewer;
59 import org.jmol.api.JmolAdapter;
60 import org.jmol.api.JmolAdapterAtomIterator;
61 import org.jmol.api.JmolAdapterStructureIterator;
62 import org.jmol.c.STR;
63 
64 /**
65  * a class used by ModelLoader to handle all loading
66  * of operations specific to PDB/mmCIF files. By loading
67  * only by class name, only loaded if PDB file is called.
68  *
69  * In addition, constants relating only to PDB files are here
70  * -- for coloring by chain, selecting by protein, etc.
71  *
72  *
73  */
74 public final class BioResolver implements Comparator<String[]> {
75 
76   public final static Map<String, Short> htGroup = new Hashtable<String, Short>();
77 
78   private Viewer vwr;
79 
BioResolver()80   public BioResolver() {
81     // only implemented via reflection, and only for PDB/mmCIF files
82   }
83 
84   private V3 vAB;
85   private V3 vNorm;
86   private P4 plane;
87 
88   private ModelLoader ml;
89   private ModelSet ms;
90 
91   private BS bsAddedMask;
92   private int lastSetH = Integer.MIN_VALUE;
93   private int maxSerial = 0;
94   private boolean haveHsAlready;
95 
setLoader(ModelLoader modelLoader)96   public BioResolver setLoader(ModelLoader modelLoader) {
97     ml = modelLoader;
98     bsAddedMask = null;
99     lastSetH = Integer.MIN_VALUE;
100     maxSerial = 0;
101     haveHsAlready = false;
102     if (modelLoader == null) {
103       ms = null;
104       bsAddedHydrogens = bsAtomsForHs = bsAssigned = null;
105       htBondMap = null;
106       htGroupBonds = null;
107       hNames = null;
108     } else {
109       Group.specialAtomNames = specialAtomNames;
110       ms = modelLoader.ms;
111       vwr = modelLoader.ms.vwr;
112       modelLoader.specialAtomIndexes = new int[ATOMID_MAX];
113       hasCONECT = (ms.getInfoM("someModelsHaveCONECT") == Boolean.TRUE);
114     }
115     return this;
116   }
117 
setViewer(Viewer vwr)118   public BioResolver setViewer(Viewer vwr) {
119     this.vwr = vwr;
120     if (Group.standardGroupList == null) {
121       //generate a static list of common amino acids, nucleic acid bases, and solvent components
122       SB s = new SB();
123       //for menu presentation order
124       for (int i = 1; i < JC.GROUPID_WATER; i++)
125         s.append(",[").append(predefinedGroup3Names[i]).append("]");
126       s.append(allCarbohydrates);
127       group3Count = s.length() / 6;
128       Group.standardGroupList = s.toString();
129       for (int i = 0, n = predefinedGroup3Names.length; i < n; ++i)
130         addGroup3Name(predefinedGroup3Names[i].trim());
131     }
132     return this;
133   }
134 
getBioModel(int modelIndex, int trajectoryBaseIndex, String jmolData, Properties modelProperties, Map<String, Object> modelAuxiliaryInfo)135   public Model getBioModel(int modelIndex,
136                         int trajectoryBaseIndex, String jmolData,
137                         Properties modelProperties,
138                         Map<String, Object> modelAuxiliaryInfo) {
139     return new BioModel(ms, modelIndex, trajectoryBaseIndex,
140         jmolData, modelProperties, modelAuxiliaryInfo);
141   }
142 
distinguishAndPropagateGroup(Chain chain, String group3, int seqcode, int firstAtomIndex, int lastAtomIndex, int[] specialAtomIndexes, Atom[] atoms)143   public Group distinguishAndPropagateGroup(Chain chain, String group3,
144                                             int seqcode, int firstAtomIndex,
145                                             int lastAtomIndex,
146                                             int[] specialAtomIndexes,
147                                             Atom[] atoms) {
148     /*
149      * called by finalizeGroupBuild()
150      *
151      * first: build array of special atom names, for example "CA" for the alpha
152      * carbon is assigned #2 see JmolConstants.specialAtomNames[] the special
153      * atoms all have IDs based on Atom.lookupSpecialAtomID(atomName) these will
154      * be the same for each conformation
155      *
156      * second: creates the monomers themselves based on this information thus
157      * building the byte offsets[] array for each monomer, indicating which
158      * position relative to the first atom in the group is which atom. Each
159      * monomer.offsets[i] then points to the specific atom of that type these
160      * will NOT be the same for each conformation
161      */
162 
163     int mask = 0;
164 
165     // clear previous specialAtomIndexes
166     for (int i = ATOMID_MAX; --i >= 0;)
167       specialAtomIndexes[i] = Integer.MIN_VALUE;
168 
169     // go last to first so that FIRST confirmation is default
170     for (int i = lastAtomIndex; i >= firstAtomIndex; --i) {
171       int specialAtomID = atoms[i].atomID;
172       if (specialAtomID <= 0)
173         continue;
174       if (specialAtomID < JC.ATOMID_DISTINGUISHING_ATOM_MAX) {
175         /*
176          * save for future option -- turns out the 1jsa bug was in relation to
177          * an author using the same group number for two different groups
178          *
179          * if ((distinguishingBits & (1 << specialAtomID) != 0) {
180          *
181          * //bh 9/21/2006: //
182          * "if the group has two of the same, that cannot be right." // Thus,
183          * for example, two C's doth not make a protein "carbonyl C"
184          * distinguishingBits = 0; break; }
185          */
186         mask |= (1 << specialAtomID);
187       }
188       specialAtomIndexes[specialAtomID] = i;
189     }
190 
191     Monomer m = null;
192     if ((mask & JC.ATOMID_PROTEIN_MASK) == JC.ATOMID_PROTEIN_MASK)
193       m = AminoMonomer.validateAndAllocate(chain, group3, seqcode,
194           firstAtomIndex, lastAtomIndex, specialAtomIndexes, atoms);
195     else if (mask == JC.ATOMID_ALPHA_ONLY_MASK)
196       m = AlphaMonomer.validateAndAllocateA(chain, group3, seqcode,
197           firstAtomIndex, lastAtomIndex, specialAtomIndexes);
198     else if (((mask & JC.ATOMID_NUCLEIC_MASK) == JC.ATOMID_NUCLEIC_MASK))
199       m = NucleicMonomer.validateAndAllocate(chain, group3, seqcode,
200           firstAtomIndex, lastAtomIndex, specialAtomIndexes);
201     else if (mask == JC.ATOMID_PHOSPHORUS_ONLY_MASK)
202       m = PhosphorusMonomer.validateAndAllocateP(chain, group3, seqcode,
203           firstAtomIndex, lastAtomIndex, specialAtomIndexes);
204     else if (checkCarbohydrate(group3))
205       m = CarbohydrateMonomer.validateAndAllocate(chain, group3, seqcode,
206           firstAtomIndex, lastAtomIndex);
207     return ( m != null && m.leadAtomIndex >= 0 ? m : null);
208   }
209 
210   //////////// ADDITION OF HYDROGEN ATOMS /////////////
211   // Bob Hanson and Erik Wyatt, Jmol 12.1.51, 7/1/2011
212 
213   /*
214    * for each group, as it is finished in the file reading:
215    *
216    * 1) get and store atom/bond information for group type
217    * 2) add placeholder (deleted) hydrogen atoms to a group
218    *
219    * in the end:
220    *
221    * 3) set multiple bonding and charges
222    * 4) determine actual number of required hydrogen atoms
223    * 5) set hydrogen atom names, atom numbers, and positions
224    * 6) undelete those atoms
225    *
226    */
227 
setHaveHsAlready(boolean b)228   public void setHaveHsAlready(boolean b) {
229     haveHsAlready = b;
230   }
231 
232   private BS bsAddedHydrogens;
233   private BS bsAtomsForHs;
234   private Map<String, String>htBondMap;
235   private Map<String, Boolean>htGroupBonds;
236   private String[] hNames;
237   private int baseBondIndex = 0;
238 
239   private boolean hasCONECT;
240 
initializeHydrogenAddition()241   public void initializeHydrogenAddition() {
242     baseBondIndex = ms.bondCount;
243     bsAddedHydrogens = new BS();
244     bsAtomsForHs = new BS();
245     htBondMap = new Hashtable<String, String>();
246     htGroupBonds = new Hashtable<String, Boolean>();
247     hNames = new String[3];
248     vAB = new V3();
249     vNorm = new V3();
250     plane = new P4();
251   }
252 
253   /**
254    * Get bonding info for double bonds and add implicit hydrogen atoms, if needed.
255    *
256    * @param adapter
257    * @param iGroup this group
258    * @param nH legacy quirk
259    */
addImplicitHydrogenAtoms(JmolAdapter adapter, int iGroup, int nH)260   public void addImplicitHydrogenAtoms(JmolAdapter adapter, int iGroup, int nH) {
261     String group3 = ml.getGroup3(iGroup);
262     int nH1;
263     if (haveHsAlready && hasCONECT
264         || group3 == null
265         || (nH1 = getStandardPdbHydrogenCount(group3)) == 0)
266       return;
267     nH = (nH1 < 0 ? -1 : nH1 + nH);
268     Object model = null;
269     int iFirst = ml.getFirstAtomIndex(iGroup);
270     int ac = ms.ac;
271     if (nH < 0) {
272       if (ac - iFirst == 1) // CA or P-only, or simple metals, also HOH, DOD
273         return;
274       model = vwr.getLigandModel(group3, "ligand_", "_data", null);
275       if (model == null)
276         return;
277       nH = adapter.getHydrogenAtomCount(model);
278       if (nH < 1)
279         return;
280     }
281     getBondInfo(adapter, group3, model);
282     ms.am[ms.at[iFirst].mi].isPdbWithMultipleBonds = true;
283     if (haveHsAlready)
284       return;
285     bsAtomsForHs.setBits(iFirst, ac);
286     bsAddedHydrogens.setBits(ac, ac + nH);
287     boolean isHetero = ms.at[iFirst].isHetero();
288     P3 xyz = P3.new3(Float.NaN, Float.NaN, Float.NaN);
289     Atom a = ms.at[iFirst];
290     for (int i = 0; i < nH; i++)
291       ms.addAtom(a.mi, a.group, 1, "H", null, 0, a.getSeqID(), 0, xyz,
292           Float.NaN, null, 0, 0, 1, 0, null, isHetero, (byte) 0, null, Float.NaN)
293           .delete(null);
294   }
295 
getBondInfo(JmolAdapter adapter, String group3, Object model)296   private void getBondInfo(JmolAdapter adapter, String group3, Object model) {
297     if (htGroupBonds.get(group3) != null)
298       return;
299     String[][] bondInfo = (model == null ? getPdbBondInfo(group3,
300         vwr.g.legacyHAddition) : getLigandBondInfo(adapter, model, group3));
301     if (bondInfo == null)
302       return;
303     htGroupBonds.put(group3, Boolean.TRUE);
304     for (int i = 0; i < bondInfo.length; i++) {
305       if (bondInfo[i] == null)
306         continue;
307       if (bondInfo[i][1].charAt(0) == 'H')
308         htBondMap.put(group3 + "." + bondInfo[i][0], bondInfo[i][1]);
309       else
310         htBondMap.put(group3 + ":" + bondInfo[i][0] + ":" + bondInfo[i][1],
311             bondInfo[i][2]);
312     }
313   }
314 
315   /**
316    * reads PDB ligand CIF info and creates a bondInfo object.
317    *
318    * @param adapter
319    * @param model
320    * @param group3
321    * @return      [[atom1, atom2, order]...]
322    */
getLigandBondInfo(JmolAdapter adapter, Object model, String group3)323   private String[][] getLigandBondInfo(JmolAdapter adapter, Object model, String group3) {
324     String[][] dataIn = adapter.getBondList(model);
325     Map<String, P3> htAtoms = new Hashtable<String, P3>();
326     JmolAdapterAtomIterator iterAtom = adapter.getAtomIterator(model);
327     while (iterAtom.hasNext())
328       htAtoms.put(iterAtom.getAtomName(), iterAtom.getXYZ());
329     String[][] bondInfo = new String[dataIn.length * 2][];
330     int n = 0;
331     for (int i = 0; i < dataIn.length; i++) {
332       String[] b = dataIn[i];
333       if (b[0].charAt(0) != 'H')
334         bondInfo[n++] = new String[] { b[0], b[1], b[2],
335             b[1].startsWith("H") ? "0" : "1" };
336       if (b[1].charAt(0) != 'H')
337         bondInfo[n++] = new String[] { b[1], b[0], b[2],
338             b[0].startsWith("H") ? "0" : "1" };
339     }
340     Arrays.sort(bondInfo, this);
341     // now look for
342     String[] t;
343     for (int i = 0; i < n;) {
344       t = bondInfo[i];
345       String a1 = t[0];
346       int nH = 0;
347       int nC = 0;
348       for (; i < n && (t = bondInfo[i])[0].equals(a1); i++) {
349         if (t[3].equals("0")) {
350           nH++;
351           continue;
352         }
353         if (t[3].equals("1"))
354           nC++;
355       }
356       int pt = i - nH - nC;
357       if (nH == 1)
358         continue;
359       switch (nC) {
360       case 1:
361         char sep = (nH == 2 ? '@' : '|');
362         for (int j = 1; j < nH; j++) {
363           bondInfo[pt][1] += sep + bondInfo[pt + j][1];
364           bondInfo[pt + j] = null;
365         }
366         continue;
367       case 2:
368         if (nH != 2)
369           continue;
370         String name = bondInfo[pt][0];
371         String name1 = bondInfo[pt + nH][1];
372         String name2 = bondInfo[pt + nH + 1][1];
373         int factor = name1.compareTo(name2);
374         Measure.getPlaneThroughPoints(htAtoms.get(name1), htAtoms.get(name), htAtoms.get(name2), vNorm, vAB,
375             plane);
376         float d = Measure.distanceToPlane(plane, htAtoms.get(bondInfo[pt][1])) * factor;
377         bondInfo[pt][1] = (d > 0 ? bondInfo[pt][1] + "@" + bondInfo[pt + 1][1]
378             :  bondInfo[pt + 1][1] + "@" + bondInfo[pt][1]);
379         bondInfo[pt + 1] = null;
380       }
381     }
382     for (int i = 0; i < n; i++) {
383       if ((t = bondInfo[i]) != null && t[1].charAt(0) != 'H' && t[0].compareTo(t[1]) > 0) {
384         bondInfo[i] = null;
385         continue;
386       }
387       if (t != null)
388         Logger.info(" ligand " + group3 + ": " + bondInfo[i][0] + " - " + bondInfo[i][1] + " order " + bondInfo[i][2]);
389     }
390     return bondInfo;
391   }
392 
393   @Override
compare(String[] a, String[] b)394   public int compare(String[] a, String[] b) {
395     return (b == null ? (a == null ? 0 : -1) : a == null ? 1 : a[0]
396         .compareTo(b[0]) < 0 ? -1 : a[0].compareTo(b[0]) > 0 ? 1 : a[3]
397         .compareTo(b[3]) < 0 ? -1 : a[3].compareTo(b[3]) > 0 ? 1 : a[1]
398         .compareTo(b[1]) < 0 ? -1 : a[1].compareTo(b[1]) > 0 ? 1 : 0);
399   }
400 
finalizeHydrogens()401   public void finalizeHydrogens() {
402     vwr.getLigandModel(null, null, null, null);
403     finalizePdbMultipleBonds();
404     addHydrogens();
405   }
406 
addHydrogens()407   private void addHydrogens() {
408     if (bsAddedHydrogens.nextSetBit(0) < 0)
409       return;
410     bsAddedMask = BSUtil.copy(bsAddedHydrogens);
411     finalizePdbCharges();
412     int[] nTotal = new int[1];
413     P3[][] pts = ms.calculateHydrogens(bsAtomsForHs, nTotal, null, AtomCollection.CALC_H_DOALL);
414     Group groupLast = null;
415     int ipt = 0;
416     Atom atom;
417     for (int i = 0; i < pts.length; i++) {
418       if (pts[i] == null || (atom = ms.at[i]) == null)
419         continue;
420       Group g = atom.group;
421       if (g != groupLast) {
422         groupLast = g;
423         ipt = g.lastAtomIndex;
424         while (bsAddedHydrogens.get(ipt))
425           ipt--;
426       }
427       String gName = atom.getGroup3(false);
428       String aName = atom.getAtomName();
429       String hName = htBondMap.get(gName + "." + aName);
430       if (hName == null)
431         continue;
432       boolean isChiral = hName.contains("@");
433       boolean isMethyl = (hName.endsWith("?") || hName.indexOf("|") >= 0);
434       int n = pts[i].length;
435       if (n == 3 && !isMethyl && hName.equals("H@H2")) {
436         hName = "H|H2|H3";
437         isMethyl = true;
438         isChiral = false;
439       }
440       if (isChiral && n == 3 || isMethyl != (n == 3)) {
441         Logger.info("Error adding H atoms to " + gName + g.getResno() + ": "
442             + pts[i].length + " atoms should not be added to " + aName);
443         continue;
444       }
445       int pt = hName.indexOf("@");
446       switch (pts[i].length) {
447       case 1:
448         if (pt > 0)
449           hName = hName.substring(0, pt);
450         setHydrogen(i, ++ipt, hName, pts[i][0]);
451         break;
452       case 2:
453         String hName1,
454         hName2;
455         float d = -1;
456         Bond[] bonds = atom.bonds;
457         if (bonds != null)
458           switch (bonds.length) {
459           case 2:
460             // could be nitrogen?
461             Atom atom1 = bonds[0].getOtherAtom(atom);
462             Atom atom2 = bonds[1].getOtherAtom(atom);
463             int factor = atom1.getAtomName().compareTo(atom2.getAtomName());
464             d = Measure.distanceToPlane(Measure.getPlaneThroughPoints(atom1, atom, atom2, vNorm, vAB,
465                 plane), pts[i][0]) * factor;
466             break;
467           }
468         if (pt < 0) {
469           Logger.info("Error adding H atoms to " + gName + g.getResno()
470               + ": expected to only need 1 H but needed 2");
471           hName1 = hName2 = "H";
472         } else if (d < 0) {
473           hName2 = hName.substring(0, pt);
474           hName1 = hName.substring(pt + 1);
475         } else {
476           hName1 = hName.substring(0, pt);
477           hName2 = hName.substring(pt + 1);
478         }
479         setHydrogen(i, ++ipt, hName1, pts[i][0]);
480         setHydrogen(i, ++ipt, hName2, pts[i][1]);
481         break;
482       case 3:
483         int pt1 = hName.indexOf('|');
484         if (pt1 >= 0) {
485           int pt2 = hName.lastIndexOf('|');
486           hNames[0] = hName.substring(0, pt1);
487           hNames[1] = hName.substring(pt1 + 1, pt2);
488           hNames[2] = hName.substring(pt2 + 1);
489         } else {
490           hNames[0] = hName.replace('?', '1');
491           hNames[1] = hName.replace('?', '2');
492           hNames[2] = hName.replace('?', '3');
493         }
494         //          Measure.getPlaneThroughPoints(pts[i][0], pts[i][1], pts[i][2], vNorm, vAB,
495         //            vAC, plane);
496         //      d = Measure.distanceToPlane(plane, atom);
497         //    int hpt = (d < 0 ? 1 : 2);
498         setHydrogen(i, ++ipt, hNames[0], pts[i][0]);
499         setHydrogen(i, ++ipt, hNames[1], pts[i][2]);
500         setHydrogen(i, ++ipt, hNames[2], pts[i][1]);
501         break;
502       }
503     }
504     deleteUnneededAtoms();
505     ms.fixFormalCharges(BSUtil.newBitSet2(ml.baseAtomIndex, ml.ms.ac));
506   }
507 
508   /**
509    * Delete hydrogen atoms that are still in bsAddedHydrogens,
510    * because they were not actually added.
511    * Also delete ligand hydrogen atoms from CO2- and PO3(2-)
512    *
513    * Note that we do this AFTER all atoms have been added. That means that
514    * this operation will not mess up atom indexing
515    *
516    */
deleteUnneededAtoms()517   private void deleteUnneededAtoms() {
518     BS bsBondsDeleted = new BS();
519     for (int i = bsAtomsForHs.nextSetBit(0); i >= 0; i = bsAtomsForHs
520         .nextSetBit(i + 1)) {
521       Atom atom = ms.at[i];
522       // specifically look for neutral HETATM O with a bond count of 2:
523       if (!atom.isHetero() || atom.getElementNumber() != 8 || atom.getFormalCharge() != 0
524           || atom.getCovalentBondCount() != 2)
525         continue;
526       Bond[] bonds = atom.bonds;
527       Atom atom1 = bonds[0].getOtherAtom(atom);
528       Atom atomH = bonds[1].getOtherAtom(atom);
529       if (atom1.getElementNumber() == 1) {
530         Atom a = atom1;
531         atom1 = atomH;
532         atomH = a;
533       }
534 
535       // Does it have an H attached?
536       if (atomH.getElementNumber() != 1)
537         continue;
538       // If so, does it have an attached atom that is doubly bonded to O?
539       // so this could be RSO4H or RPO3H2 or RCO2H
540       Bond[] bonds1 = atom1.bonds;
541       for (int j = 0; j < bonds1.length; j++) {
542         if (bonds1[j].order == 2) {
543           Atom atomO = bonds1[j].getOtherAtom(atom1);
544           if (atomO.getElementNumber() == 8) {
545             bsAddedHydrogens.set(atomH.i);
546             atomH.delete(bsBondsDeleted);
547             break;
548           }
549         }
550 
551       }
552     }
553     ms.deleteBonds(bsBondsDeleted, true);
554     deleteAtoms(bsAddedHydrogens);
555   }
556 
557   /**
558    * called from org.jmol.modelsetbio.resolver when adding hydrogens.
559    *
560    * @param bsDeletedAtoms
561    */
deleteAtoms(BS bsDeletedAtoms)562   private void deleteAtoms(BS bsDeletedAtoms) {
563     // get map
564     int[] mapOldToNew = new int[ms.ac];
565     int[] mapNewToOld = new int[ms.ac - bsDeletedAtoms.cardinality()];
566     int n = ml.baseAtomIndex;
567     Model[] models = ms.am;
568     Atom[] atoms = ms.at;
569     for (int i = ml.baseAtomIndex; i < ms.ac; i++) {
570       Atom a = atoms[i];
571       if (a == null)
572         continue;
573       models[a.mi].bsAtoms.clear(i);
574       models[a.mi].bsAtomsDeleted.clear(i);
575       if (bsDeletedAtoms.get(i)) {
576         mapOldToNew[i] = n - 1;
577         models[atoms[i].mi].act--;
578       } else {
579         mapNewToOld[n] = i;
580         mapOldToNew[i] = n++;
581       }
582     }
583     ms.msInfo.put("bsDeletedAtoms", bsDeletedAtoms);
584     // adjust group pointers
585     for (int i = ml.baseGroupIndex; i < ml.groups.length; i++) {
586       Group g = ml.groups[i];
587       if (g.firstAtomIndex >= ml.baseAtomIndex) {
588         g.firstAtomIndex = mapOldToNew[g.firstAtomIndex];
589         g.lastAtomIndex = mapOldToNew[g.lastAtomIndex];
590         if (g.leadAtomIndex >= 0)
591           g.leadAtomIndex = mapOldToNew[g.leadAtomIndex];
592       }
593     }
594     // adjust atom arrays
595     ms.adjustAtomArrays(mapNewToOld, ml.baseAtomIndex, n);
596     ms.calcBoundBoxDimensions(null, 1);
597     ms.resetMolecules();
598     ms.validateBspf(false);
599     bsAddedMask = BSUtil.deleteBits(bsAddedMask, bsDeletedAtoms);
600     //System.out.println("res bsAddedMask = " + bsAddedMask);
601     for (int i = ml.baseModelIndex; i < ms.mc; i++) {
602       fixAnnotations(i, "domains", T.domains);
603       fixAnnotations(i, "validation", T.validation);
604     }
605   }
606 
fixAnnotations(int i, String name, int type)607   private void fixAnnotations(int i, String name, int type) {
608     Object o = ml.ms.getInfo(i, name);
609     if (o != null) {
610       Object dbObj = ((BioModel) ms.am[i]).getCachedAnnotationMap(name, o);
611       if (dbObj != null)
612         vwr.getAnnotationParser(false).fixAtoms(i, (SV) dbObj, bsAddedMask, type, 20);
613     }
614   }
615 
finalizePdbCharges()616   private void finalizePdbCharges() {
617     Atom[] atoms = ms.at;
618     // fix terminal N groups as +1
619     for (int i = bsAtomsForHs.nextSetBit(0); i >= 0; i = bsAtomsForHs.nextSetBit(i + 1)) {
620       Atom a = atoms[i];
621       if (a.group.getNitrogenAtom() == a && a.getCovalentBondCount() == 1)
622         a.setFormalCharge(1);
623       if ((i = bsAtomsForHs.nextClearBit(i + 1)) < 0)
624         break;
625     }
626   }
627 
finalizePdbMultipleBonds()628   private void finalizePdbMultipleBonds() {
629     Map<String, Boolean> htKeysUsed = new Hashtable<String, Boolean>();
630     int bondCount = ms.bondCount;
631     Bond[] bonds = ms.bo;
632     for (int i = baseBondIndex; i < bondCount; i++) {
633       Atom a1 = bonds[i].atom1;
634       Atom a2 = bonds[i].atom2;
635       Group g = a1.group;
636       if (g != a2.group)
637         continue;
638       SB key = new SB().append(g.getGroup3());
639       key.append(":");
640       String n1 = a1.getAtomName();
641       String n2 = a2.getAtomName();
642       if (n1.compareTo(n2) > 0)
643         key.append(n2).append(":").append(n1);
644       else
645         key.append(n1).append(":").append(n2);
646       String skey = key.toString();
647       String type = htBondMap.get(skey);
648       if (type == null)
649         continue;
650       htKeysUsed.put(skey, Boolean.TRUE);
651       bonds[i].setOrder(PT.parseInt(type));
652     }
653 
654     for (String key : htBondMap.keySet()) {
655       if (htKeysUsed.get(key) != null)
656         continue;
657       if (key.indexOf(":") < 0) {
658         htKeysUsed.put(key, Boolean.TRUE);
659         continue;
660       }
661       String value = htBondMap.get(key);
662       Logger.info("bond " + key + " was not used; order=" + value);
663       if (htBondMap.get(key).equals("1")) {
664         htKeysUsed.put(key, Boolean.TRUE);
665         continue; // that's ok
666       }
667     }
668     Map<String, String> htKeysBad = new Hashtable<String, String>();
669     for (String key : htBondMap.keySet()) {
670       if (htKeysUsed.get(key) != null)
671         continue;
672       htKeysBad.put(key.substring(0, key.lastIndexOf(":")), htBondMap.get(key));
673     }
674     if (htKeysBad.isEmpty())
675       return;
676     for (int i = 0; i < bondCount; i++) {
677       Atom a1 = bonds[i].atom1;
678       Atom a2 = bonds[i].atom2;
679       if (a1.group == a2.group)
680         continue;
681       String value;
682       if ((value = htKeysBad.get(a1.getGroup3(false) + ":" + a1.getAtomName())) == null
683           && ((value = htKeysBad.get(a2.getGroup3(false) + ":" + a2.getAtomName())) == null))
684         continue;
685       bonds[i].setOrder(PT.parseInt(value));
686       Logger.info("assigning order " + bonds[i].order + " to bond " + bonds[i]);
687     }
688   }
689 
setHydrogen(int iTo, int iAtom, String name, P3 pt)690   private void setHydrogen(int iTo, int iAtom, String name, P3 pt) {
691     if (!bsAddedHydrogens.get(iAtom))
692       return;
693     Atom[] atoms = ms.at;
694     if (lastSetH == Integer.MIN_VALUE || atoms[iAtom].mi != atoms[lastSetH].mi)
695       maxSerial = ((int[]) ms.getInfo(atoms[lastSetH = iAtom].mi, "PDB_CONECT_firstAtom_count_max"))[2];
696     bsAddedHydrogens.clear(iAtom);
697     ms.setAtomName(iAtom, name, false);
698     atoms[iAtom].setT(pt);
699     ms.setAtomNumber(iAtom, ++maxSerial, false);
700     atoms[iAtom].atomSymmetry = atoms[iTo].atomSymmetry;
701     ml.undeleteAtom(iAtom);
702 
703     ms.bondAtoms(atoms[iTo], atoms[iAtom], Edge.BOND_COVALENT_SINGLE,
704         ms.getDefaultMadFromOrder(Edge.BOND_COVALENT_SINGLE), null, 0, true, false);
705   }
706 
fixPropertyValue(BS bsAtoms, Object data, boolean toHydrogens)707   public Object fixPropertyValue(BS bsAtoms, Object data, boolean toHydrogens) {
708     Atom[] atoms = ms.at;
709     // we aren't doing this anymore
710     // it was for TLS groups
711 //    if (data instanceof String) {
712 //      String[] sData = PT.split((String) data, "\n");
713 //      String[] newData = new String[bsAtoms.cardinality()];
714 //      String lastData = "";
715 //      for (int pt = 0, iAtom = 0, i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms
716 //          .nextSetBit(i + 1), iAtom++) {
717 //        if (atoms[i].getElementNumber() == 1) {
718 //          if (!toHydrogens)
719 //            continue;
720 //        } else {
721 //          lastData = sData[pt++];
722 //        }
723 //        newData[iAtom] = lastData;
724 //      }
725 //      return PT.join(newData, '\n', 0);
726 //    }
727     // already float data
728     float[] fData = (float[]) data;
729     float[] newData = new float[bsAtoms.cardinality()];
730     float lastData = 0;
731     for (int pt = 0, iAtom = 0, i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms
732         .nextSetBit(i + 1), iAtom++) {
733       if (atoms[i].getElementNumber() == 1) {
734         if (!toHydrogens)
735           continue;
736       } else {
737         lastData = fData[pt++];
738       }
739       newData[iAtom] = lastData;
740     }
741     return newData;
742   }
743 
allocateBioPolymer(Group[] groups, int firstGroupIndex, boolean checkConnections, int pt0)744   static BioPolymer allocateBioPolymer(Group[] groups, int firstGroupIndex,
745                                        boolean checkConnections, int pt0) {
746     Monomer previous = null;
747     int count = 0;
748     for (int i = firstGroupIndex; i < groups.length; ++i) {
749       Group group = groups[i];
750       Monomer current;
751       if (!(group instanceof Monomer)
752           || (current = (Monomer) group).bioPolymer != null || previous != null
753           && previous.getClass() != current.getClass() || checkConnections
754           && !current.isConnectedAfter(previous))
755         break;
756       previous = current;
757       count++;
758     }
759     if (count < 2)
760       return null;
761     Monomer[] monomers = new Monomer[count];
762     for (int j = 0; j < count; ++j)
763       monomers[j] = (Monomer) groups[firstGroupIndex + j];
764     if (previous instanceof AminoMonomer)
765       return new AminoPolymer(monomers, pt0);
766     if (previous instanceof AlphaMonomer)
767       return new AlphaPolymer(monomers, pt0);
768     if (previous instanceof NucleicMonomer)
769       return new NucleicPolymer(monomers);
770     if (previous instanceof PhosphorusMonomer)
771       return new PhosphorusPolymer(monomers);
772     if (previous instanceof CarbohydrateMonomer)
773       return new CarbohydratePolymer(monomers);
774     Logger
775         .error("Polymer.allocatePolymer() ... no matching polymer for monomor "
776             + previous);
777     throw new NullPointerException();
778   }
779 
780   private BS bsAssigned;
781 
782   /**
783    * Pull in all spans of helix, etc. in the file(s)
784    *
785    * We do turn first, because sometimes a group is defined twice, and this way
786    * it gets marked as helix or sheet if it is both one of those and turn.
787    *
788    * Jmol 14.3 - adds sequence ANNOTATION
789    *
790    * @param adapter
791    * @param atomSetCollection
792    */
iterateOverAllNewStructures(JmolAdapter adapter, Object atomSetCollection)793   public void iterateOverAllNewStructures(JmolAdapter adapter,
794                                           Object atomSetCollection) {
795     JmolAdapterStructureIterator iterStructure = adapter
796         .getStructureIterator(atomSetCollection);
797     if (iterStructure == null)
798       return;
799     BS bs = iterStructure.getStructuredModels();
800     if (bs != null)
801       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
802         ml.structuresDefinedInFile.set(ml.baseModelIndex + i);
803     while (iterStructure.hasNext())
804       if (iterStructure.getStructureType() != STR.TURN)
805         setStructure(iterStructure);
806 
807     // define turns LAST. (pulled by the iterator first)
808     // so that if they overlap they get overwritten:
809 
810     iterStructure = adapter.getStructureIterator(atomSetCollection);
811     while (iterStructure.hasNext())
812       if (iterStructure.getStructureType() == STR.TURN)
813         setStructure(iterStructure);
814   }
815 
816   private static STR[] types = { STR.HELIXPI, STR.HELIXALPHA,
817     STR.SHEET, STR.HELIX310, STR.TURN };
818 
819   private static int[] mytypes = {0, 2, 3, 4, 6};
820 
821   /**
822    * note that istart and iend will be adjusted.
823    *
824    * @param iterStructure
825    */
setStructure(JmolAdapterStructureIterator iterStructure)826   private void setStructure(JmolAdapterStructureIterator iterStructure) {
827     STR t = iterStructure.getSubstructureType();
828     String id = iterStructure.getStructureID();
829     int serID = iterStructure.getSerialID();
830     int count = iterStructure.getStrandCount();
831     int[] atomRange = iterStructure.getAtomIndices();
832     int[] modelRange = iterStructure.getModelIndices();
833     BS[] bsAll = iterStructure.getBSAll();
834     int m0, m1;
835     Model[] models = ms.am;
836     if (ml.isTrajectory) { //from PDB file
837       m0 = m1 = modelRange[0];
838     } else {
839       m0 = modelRange[0] + ml.baseModelIndex;
840       m1 = modelRange[1] + ml.baseModelIndex;
841     }
842     ml.structuresDefinedInFile.setBits(m0, m1 + 1);
843 
844     BS bs;
845     Model m;
846     if (bsAll != null) {
847       for (int i = m0, t0; i <= m1; i++)
848         if ((m = models[i]) instanceof BioModel)
849           for (int j = 0; j < 5; j++)
850             if ((bs = bsAll[t0 = mytypes[j]]) != null)
851               ((BioModel) m).addStructureByBS(0, t0, types[j], bs);
852       return;
853     }
854 
855     int startChainID = iterStructure.getStartChainID();
856     int startSequenceNumber = iterStructure.getStartSequenceNumber();
857     char startInsertionCode = iterStructure.getStartInsertionCode();
858     int endSequenceNumber = iterStructure.getEndSequenceNumber();
859     int endChainID = iterStructure.getEndChainID();
860     char endInsertionCode = iterStructure.getEndInsertionCode();
861     STR type = (t == STR.NOT ? STR.NONE : t);
862     int startSeqCode = Group.getSeqcodeFor(startSequenceNumber,
863         startInsertionCode);
864     int endSeqCode = Group.getSeqcodeFor(endSequenceNumber, endInsertionCode);
865     if (bsAssigned == null)
866       bsAssigned = new BS();
867     for (int i = m0, i0 = 0; i <= m1; i++)
868       if ((m = models[i]) instanceof BioModel)
869         ((BioModel) m).addSecondaryStructure(type, id, serID, count,
870             startChainID, startSeqCode, endChainID, endSeqCode,
871             (i0 = m.firstAtomIndex) + atomRange[0], i0 + atomRange[1],
872             bsAssigned);
873   }
874 
setGroupLists(int ipt)875   public void setGroupLists(int ipt) {
876     ml.group3Lists[ipt + 1] = Group.standardGroupList;
877     ml.group3Counts[ipt + 1] = new int[group3Count + 10];
878     if (ml.group3Lists[0] == null) {
879       ml.group3Lists[0] = Group.standardGroupList;
880       ml.group3Counts[0] = new int[group3Count + 10];
881     }
882   }
883 
884   /**
885    * @param g3
886    * @param max max ID (e.g. 20); can be Integer.MAX_VALUE to allow carbohydrate
887    * @return true if found
888    */
isKnownPDBGroup(String g3, int max)889   public boolean isKnownPDBGroup(String g3, int max) {
890     int pt = knownGroupID(g3);
891     return (pt > 0 ? pt < max : max == Integer.MAX_VALUE && checkCarbohydrate(g3));
892   }
893 
lookupSpecialAtomID(String name)894   public byte lookupSpecialAtomID(String name) {
895     if (htSpecialAtoms == null) {
896       htSpecialAtoms = new Hashtable<String, Byte>();
897       for (int i = specialAtomNames.length; --i >= 0;) {
898         String specialAtomName = specialAtomNames[i];
899         if (specialAtomName != null)
900           htSpecialAtoms.put(specialAtomName, Byte.valueOf((byte) i));
901       }
902     }
903     Byte boxedAtomID = htSpecialAtoms.get(name);
904     return (boxedAtomID == null ? 0 : boxedAtomID.byteValue());
905   }
906 
907   private static Map<String, String[][]> htPdbBondInfo;
908 
getPdbBondInfo(String group3, boolean isLegacy)909   private String[][] getPdbBondInfo(String group3, boolean isLegacy) {
910     if (htPdbBondInfo == null)
911       htPdbBondInfo = new Hashtable<String, String[][]>();
912     String[][] info = htPdbBondInfo.get(group3);
913     if (info != null)
914       return info;
915     int pt = knownGroupID(group3);
916     if (pt < 0 || pt > pdbBondInfo.length)
917       return null;
918     String s = pdbBondInfo[pt];
919     // unfortunately, this change is not backward compatible.
920     if (isLegacy && (pt = s.indexOf("O3'")) >= 0)
921       s = s.substring(0, pt);
922     String[] temp = PT.getTokens(s);
923     info = new String[temp.length / 2][];
924     for (int i = 0, p = 0; i < info.length; i++) {
925       String source = temp[p++];
926       String target = temp[p++];
927       // a few shortcuts here:
928       if (target.length() == 1)
929         switch (target.charAt(0)) {
930         case 'N':
931           target = "H@H2";
932           break;
933         case 'B': // CB
934           target = "HB3@HB2";
935           break;
936         case 'D': // CD
937           target = "HD3@HD2";
938           break;
939         case 'G': // CG
940           target = "HG3@HG2";
941           break;
942         case '2': // C2'
943           target = "H2'@H2''";
944           break;
945         case '5': // C5'
946           target = "H5''@H5'";
947           break;
948         }
949       if (target.charAt(0) != 'H' && source.compareTo(target) > 0) {
950         s = target;
951         target = source;
952         source = s;
953       }
954       info[i] = new String[] { source, target,
955           (target.startsWith("H") ? "1" : "2") };
956     }
957     htPdbBondInfo.put(group3, info);
958     return info;
959   }
960   /**
961    * pdbBondInfo describes in a compact way what the hydrogen atom
962    * names are for each standard amino acid. This list consists
963    * of pairs of attached atom/hydrogen atom names, with abbreviations
964    * N, C, O, B, D, G, 1, and 2 (for N, C, O, CB, CD, CG, C1', and C2', respectively)
965    * given in pdbHAttachments, above. Note that we never add HXT or NH3
966    * "?" here is for methyl groups with H1, H2, H3.
967    * "@" indicates a prochiral center, with the assignment order given here
968    *
969    */
970   public final static String[] pdbBondInfo = {
971     // added O3' HO3' O5' HO5' for nucleic and added 1 H atom for res 1 for 13.1.17
972     // this could throw off states from previous versions
973     // CH2 and NH2 labeling revised 2015.02.07
974 
975     "",
976     /*ALA*/ "N N CA HA C O CB HB?",
977     /*ARG*/ "N N CA HA C O CB B CG G CD D NE HE CZ NH1 NH1 HH11@HH12 NH2 HH22@HH21",
978     /*ASN*/ "N N CA HA C O CB B CG OD1 ND2 HD21@HD22",
979     /*ASP*/ "N N CA HA C O CB B CG OD1",
980     /*CYS*/ "N N CA HA C O CB B SG HG",
981     /*GLN*/ "N N CA HA C O CB B CG G CD OE1 NE2 HE22@HE21",
982     /*GLU*/ "N N CA HA C O CB B CG G CD OE1",
983     /*GLY*/ "N N CA HA2@HA3 C O",
984     /*HIS*/ "N N CA HA C O CB B CG CD2 ND1 CE1 ND1 HD1 CD2 HD2 CE1 HE1 NE2 HE2",
985     /*ILE*/ "N N CA HA C O CB HB CG1 HG13@HG12 CG2 HG2? CD1 HD1?",
986     /*LEU*/ "N N CA HA C O CB B CG HG CD1 HD1? CD2 HD2?",
987     /*LYS*/ "N N CA HA C O CB B CG G CD HD2@HD3 CE HE3@HE2 NZ HZ?",
988     /*MET*/ "N N CA HA C O CB B CG G CE HE?",
989     /*PHE*/ "N N CA HA C O CB B CG CD1 CD1 HD1 CD2 CE2 CD2 HD2 CE1 CZ CE1 HE1 CE2 HE2 CZ HZ",
990     /*PRO*/ "N H CA HA C O CB B CG G CD D",
991     /*SER*/ "N N CA HA C O CB B OG HG",
992     /*THR*/ "N N CA HA C O CB HB OG1 HG1 CG2 HG2?",
993     /*TRP*/ "N N CA HA C O CB B CG CD1 CD1 HD1 CD2 CE2 NE1 HE1 CE3 CZ3 CE3 HE3 CZ2 CH2 CZ2 HZ2 CZ3 HZ3 CH2 HH2",
994     /*TYR*/ "N N CA HA C O CB B CG CD1 CD1 HD1 CD2 CE2 CD2 HD2 CE1 CZ CE1 HE1 CE2 HE2 OH HH",
995     /*VAL*/ "N N CA HA C O CB HB CG1 HG1? CG2 HG2?",
996     /*ASX*/ "N N CA HA C O CB B",
997     /*GLX*/ "N N CA HA C O CB B CG G",
998     /*UNK*/ "",
999     /*G*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' H2' O2' HO2' C1' H1' C8 N7 C8 H8 C5 C4 C6 O6 N1 H1 C2 N3 N2 H22@H21 O3' HO3' O5' HO5'",
1000     /*C*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' H2' O2' HO2' C1' H1' C2 O2 N3 C4 N4 H41@H42 C5 C6 C5 H5 C6 H6 O3' HO3' O5' HO5'",
1001     /*A*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' H2' O2' HO2' C1' H1' C8 N7 C8 H8 C5 C4 C6 N1 N6 H61@H62 C2 N3 C2 H2 O3' HO3' O5' HO5'",
1002     /*T*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C2 O2 N3 H3 C4 O4 C5 C6 C7 H7? C6 H6 O3' HO3' O5' HO5'",
1003     /*U*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' H2' O2' HO2' C1' H1' C2 O2 N3 H3 C4 O4 C5 C6 C5 H5 C6 H6 O3' HO3' O5' HO5'",
1004     /*I*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' H2' O2' HO2' C1' H1' C8 N7 C8 H8 C5 C4 C6 O6 N1 H1 C2 N3 C2 H2 O3' HO3' O5' HO5'",
1005     /*DG*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C8 N7 C8 H8 C5 C4 C6 O6 N1 H1 C2 N3 N2 H22@H21 O3' HO3' O5' HO5'",
1006     /*DC*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C2 O2 N3 C4 N4 H41@H42 C5 C6 C5 H5 C6 H6 O3' HO3' O5' HO5'",
1007     /*DA*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C8 N7 C8 H8 C5 C4 C6 N1 N6 H61@H62 C2 N3 C2 H2 O3' HO3' O5' HO5'",
1008     /*DT*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C2 O2 N3 H3 C4 O4 C5 C6 C7 H7? C6 H6 O3' HO3' O5' HO5'",
1009     /*DU*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C2 O2 N3 H3 C4 O4 C5 C6 C5 H5 C6 H6 O3' HO3' O5' HO5'",
1010     /*DI*/ "P OP1 C5' 5 C4' H4' C3' H3' C2' 2 C1' H1' C8 N7 C8 H8 C5 C4 C6 O6 N1 H1 C2 N3 C2 H2 O3' HO3' O5' HO5'",
1011       };
1012   private final static int[] pdbHydrogenCount = {
1013             0,
1014     /*ALA*/ 6,
1015     /*ARG*/ 16,
1016     /*ASN*/ 7,
1017     /*ASP*/ 6,
1018     /*CYS*/ 6,
1019     /*GLN*/ 9,
1020     /*GLU*/ 8,
1021     /*GLY*/ 4,
1022     /*HIS*/ 9,
1023     /*ILE*/ 12,
1024     /*LEU*/ 12,
1025     /*LYS*/ 14,
1026     /*MET*/ 10,
1027     /*PHE*/ 10,
1028     /*PRO*/ 8,
1029     /*SER*/ 6,
1030     /*THR*/ 8,
1031     /*TRP*/ 11,
1032     /*TYR*/ 10,
1033     /*VAL*/ 10,
1034     /*ASX*/ 3,
1035     /*GLX*/ 5,
1036     /*UNK*/ 0,
1037     /*G*/ 13,
1038     /*C*/ 13,
1039     /*A*/ 13,
1040     /*T*/ -1,
1041     /*U*/ 12,
1042     /*I*/ 12,
1043     /*DG*/ 13,
1044     /*DC*/ 13,
1045     /*DA*/ 13,
1046     /*DT*/ 14,
1047     /*DU*/ 12,
1048     /*DI*/ 12,
1049   };
1050 
1051   /**
1052    *  this form is used for counting groups in ModelSet
1053    *
1054    *  GLX added for 13.1.16
1055    *
1056    */
1057   private final static String allCarbohydrates =
1058     ",[AHR],[ALL],[AMU],[ARA],[ARB],[BDF],[BDR],[BGC],[BMA]" +
1059     ",[FCA],[FCB],[FRU],[FUC],[FUL],[GAL],[GLA],[GLC],[GXL]" +
1060     ",[GUP],[LXC],[MAN],[RAM],[RIB],[RIP],[XYP],[XYS]" +
1061     ",[CBI],[CT3],[CTR],[CTT],[LAT],[MAB],[MAL],[MLR],[MTT]" +
1062     ",[SUC],[TRE],[GCU],[MTL],[NAG],[NDG],[RHA],[SOR],[SOL],[SOE]" +
1063     ",[XYL],[A2G],[LBT],[NGA],[SIA],[SLB]" +
1064     ",[AFL],[AGC],[GLB],[NAN],[RAA]"; //these 4 are deprecated in PDB
1065 
1066   // from Eric Martz; revision by Angel Herraez
knownGroupID(String group3)1067   public static short knownGroupID(String group3) {
1068     if (group3 == null || group3.length() == 0)
1069       return 0;
1070     Short boxedGroupID = htGroup.get(group3);
1071     return (boxedGroupID == null ? -1 : boxedGroupID.shortValue());
1072   }
1073   /**
1074    * @param group3 a potential group3 name
1075    * @return whether this is a carbohydrate from the list
1076    */
checkCarbohydrate(String group3)1077   private final static boolean checkCarbohydrate(String group3) {
1078     return (group3 != null
1079         && allCarbohydrates.indexOf("[" + group3.toUpperCase() + "]") >= 0);
1080   }
1081   private static int group3Count;
1082   final static char[] predefinedGroup1Names = {
1083   /* rmh
1084    *
1085    * G   Glycine   Gly                   P   Proline   Pro
1086    * A   Alanine   Ala                   V   Valine    Val
1087    * L   Leucine   Leu                   I   Isoleucine    Ile
1088    * M   Methionine    Met               C   Cysteine    Cys
1089    * F   Phenylalanine   Phe             Y   Tyrosine    Tyr
1090    * W   Tryptophan    Trp               H   Histidine   His
1091    * K   Lysine    Lys                   R   Arginine    Arg
1092    * Q   Glutamine   Gln                 N   Asparagine    Asn
1093    * E   Glutamic Acid   Glu             D   Aspartic Acid   Asp
1094    * S   Serine    Ser                   T   Threonine   Thr
1095    */
1096   '\0', //  0 this is the null group
1097 
1098   'A', // 1
1099   'R',
1100   'N',
1101   'D',
1102   'C', // 5 Cysteine
1103   'Q',
1104   'E',
1105   'G',
1106   'H',
1107   'I',
1108   'L',
1109   'K',
1110   'M',
1111   'F',
1112   'P', // 15 Proline
1113   'S',
1114   'T',
1115   'W',
1116   'Y',
1117   'V',
1118   'A', // 21 ASP/ASN ambiguous
1119   'G', // 22 GLU/GLN ambiguous
1120   '?', // 23 unknown -- 23
1121 
1122   'G', // X nucleics
1123   'C',
1124   'A',
1125   'T',
1126   'U',
1127   'I',
1128 
1129   'G', // DX nucleics
1130   'C',
1131   'A',
1132   'T',
1133   'U',
1134   'I',
1135 
1136   'G', // +X nucleics
1137   'C',
1138   'A',
1139   'T',
1140   'U',
1141   'I',
1142   };
1143 
1144 
1145   /**
1146    * MMCif, Gromacs, MdTop, Mol2 readers only
1147    * @param group3
1148    * @return true if an identified hetero group
1149    *
1150    */
isHetero(String group3)1151   public boolean isHetero(String group3) {
1152     switch (group3.length()) {
1153     case 1:
1154       group3 += "  ";
1155       break;
1156     case 2:
1157       group3 += " ";
1158       break;
1159     case 3:
1160       break;
1161     default:
1162       return true;
1163     }
1164     int pt = Group.standardGroupList.indexOf(group3);
1165     return (pt < 0 || pt / 6 + 1 >= JC.GROUPID_WATER);
1166   }
1167 
1168   public static short group3NameCount;
1169   private final static String[] predefinedGroup3Names = {
1170     // taken from PDB spec
1171     "   ", //  0 this is the null group
1172     "ALA", // 1
1173     "ARG", // 2 arginine -- hbond donor
1174     "ASN", // 3 asparagine -- hbond donor
1175     "ASP", // 4 aspartate -- hbond acceptor
1176     "CYS",
1177     "GLN", // 6 glutamine -- hbond donor
1178     "GLU", // 7 glutamate -- hbond acceptor
1179     "GLY",
1180     "HIS", // 9 histidine -- hbond ambiguous
1181     "ILE",
1182     "LEU",
1183     "LYS", // 12 lysine -- hbond donor
1184     "MET",
1185     "PHE",
1186     "PRO", // 15 proline -- no NH
1187     "SER",
1188     "THR",
1189     "TRP",
1190     "TYR", // 19 tryptophan -- hbond donor
1191     "VAL",
1192     "ASX", // 21 ASP/ASN ambiguous
1193     "GLX", // 22 GLU/GLN ambiguous
1194     "UNK", // 23 unknown -- 23
1195 
1196     // if you change these numbers you *must* update
1197     // the predefined sets below
1198 
1199     // with the deprecation of +X, we will need a new
1200     // way to handle these.
1201 
1202     "G  ", // 24 starts nucleics //0
1203     "C  ",
1204     "A  ",
1205     "T  ",
1206     "U  ",
1207     "I  ", // 29 / 5
1208 
1209     "DG ", // 30 / 6
1210     "DC ",
1211     "DA ",
1212     "DT ",
1213     "DU ",
1214     "DI ", // 35 / 11
1215 
1216     "+G ", // 36 / 12
1217     "+C ",
1218     "+A ",
1219     "+T ",
1220     "+U ",
1221     "+I ", // 41 / 17
1222     /* removed bh 7/1/2011 this is isolated inosine, not a polymer "NOS", // inosine */
1223 
1224     // solvent types: -- if these numbers change, also change GROUPID_WATER,_SOLVENT,and_SULFATE
1225 
1226     "HOH", // 42 water
1227     "DOD", // 43
1228     "WAT", // 44
1229     "UREA",// 45 urea, a cosolvent
1230     "PO4", // 46 phosphate ions  -- from here on is "ligand"
1231     "SO4", // 47 sulphate ions
1232     "UNL", // 48 unknown ligand
1233 
1234   };
1235 
1236   /*
1237    * Convert "AVG" to "ALA VAL GLY"; unknowns to UNK
1238    *
1239    */
toStdAmino3(String g1)1240   public String toStdAmino3(String g1) {
1241     if (g1.length() == 0)
1242       return "";
1243     SB s = new SB();
1244     int pt = knownGroupID("==A");
1245     if (pt < 0) {
1246       // just the amino acids
1247       for (int i = 1; i <= 20; i++) {
1248         pt = knownGroupID(predefinedGroup3Names[i]);
1249         htGroup.put("==" + predefinedGroup1Names[i], Short.valueOf((short) pt));
1250       }
1251     }
1252     for (int i = 0, n = g1.length(); i < n; i++) {
1253       char ch = g1.charAt(i);
1254       pt = knownGroupID("==" + ch);
1255       if (pt < 0)
1256         pt = 23;
1257       s.append(" ").append(predefinedGroup3Names[pt]);
1258     }
1259     return s.toString().substring(1);
1260   }
1261 
getGroupID(String g3)1262   public short getGroupID(String g3) {
1263     return getGroupIdFor(g3);
1264   }
1265 
getGroupIdFor(String group3)1266   static short getGroupIdFor(String group3) {
1267     if (group3 != null)
1268       group3 = group3.trim();
1269     short groupID = knownGroupID(group3);
1270     return (groupID == -1 ? addGroup3Name(group3) : groupID);
1271   }
1272 
1273   /**
1274    * These can overrun 3 characters; that is not significant.
1275    *
1276    * @param group3
1277    * @return  a short group ID
1278    */
addGroup3Name(String group3)1279   private synchronized static short addGroup3Name(String group3) {
1280     if (group3NameCount == Group.group3Names.length)
1281       Group.group3Names = AU.doubleLengthS(Group.group3Names);
1282     short groupID = group3NameCount++;
1283     Group.group3Names[groupID] = group3;
1284     htGroup.put(group3, Short.valueOf(groupID));
1285     return groupID;
1286   }
1287 
getStandardPdbHydrogenCount(String group3)1288   private static int getStandardPdbHydrogenCount(String group3) {
1289     int pt = knownGroupID(group3);
1290     return (pt < 0 || pt >= pdbHydrogenCount.length ? -1 : pdbHydrogenCount[pt]);
1291   }
1292   ////////////////////////////////////////////////////////////////
1293   // static stuff for group ids
1294   ////////////////////////////////////////////////////////////////
1295 
1296   private final static String[] specialAtomNames = {
1297 
1298     ////////////////////////////////////////////////////////////////
1299     // The ordering of these entries can be changed ... BUT ...
1300     // the offsets must be kept consistent with the ATOMID definitions
1301     // below.
1302     //
1303     // Used in Atom to look up special atoms. Any "*" in a PDB entry is
1304     // changed to ' for comparison here
1305     //
1306     // null is entry 0
1307     // The first 32 entries are reserved for null + 31 'distinguishing atoms'
1308     // see definitions below. 32 is magical because bits are used in an
1309     // int to distinguish groups. If we need more then we can go to 64
1310     // bits by using a long ... but code must change. See Resolver.java
1311     //
1312     // All entries from 64 on are backbone entries
1313     ////////////////////////////////////////////////////////////////
1314     null, // 0
1315 
1316     // protein backbone
1317     //
1318     "N",   //  1 - amino nitrogen        SPINE
1319     "CA",  //  2 - alpha carbon          SPINE
1320     "C",   //  3 - carbonyl carbon       SPINE
1321     "O",   //  4 - carbonyl oxygen
1322     "O1",  //  5 - carbonyl oxygen in some protein residues (4THN)
1323 
1324     // nucleic acid backbone sugar
1325     //
1326     "O5'", //  6 - sugar 5' oxygen       SPINE
1327     "C5'", //  7 - sugar 5' carbon       SPINE
1328     "C4'", //  8 - sugar ring 4' carbon  SPINE
1329     "C3'", //  9 - sugar ring 3' carbon  SPINE
1330     "O3'", // 10 - sugar 3' oxygen       SPINE
1331     "C2'", // 11 - sugar ring 2' carbon
1332     "C1'", // 12 - sugar ring 1' carbon
1333     // Phosphorus is not required for a nucleic group because
1334     // at the terminus it could have H5T or O5T ...
1335     "P",   // 13 - phosphate phosphorus  SPINE
1336 
1337     // END OF FIRST BACKBONE SET
1338 
1339     // ... But we need to distinguish phosphorus separately because
1340     // it could be found in phosphorus-only nucleic polymers
1341 
1342     "OD1",   // 14  ASP/ASN carbonyl/carbonate
1343     "OD2",   // 15  ASP carbonyl/carbonate
1344     "OE1",   // 16  GLU/GLN carbonyl/carbonate
1345     "OE2",   // 17  GLU carbonyl/carbonate
1346     "SG",    // 18  CYS sulfur
1347     // reserved for future expansion ... lipids & carbohydrates
1348     // 9/2006 -- carbohydrates are just handled as group3 codes
1349     // see below
1350     null, // 18 - 19
1351     null, null, null, null, // 20 - 23
1352     null, null, null, null, // 24 - 27
1353     null, null, null, null, // 28 - 31
1354 
1355     // nucleic acid bases
1356     //
1357     "N1",   // 32
1358     "C2",   // 33
1359     "N3",   // 34
1360     "C4",   // 35
1361     "C5",   // 36
1362     "C6",   // 37 -- currently defined as the nucleotide wing
1363             // this determines the vector for the sheet
1364             // could be changed if necessary
1365 
1366     // pyrimidine O2
1367     //
1368     "O2",   // 38
1369 
1370     // purine stuff
1371     //
1372     "N7",   // 39
1373     "C8",   // 40
1374     "N9",   // 41
1375 
1376     // nucleic acid base ring functional groups
1377     // DO NOT CHANGE THESE NUMBERS WITHOUT ALSO CHANGING
1378     // NUMBERS IN THE PREDEFINED SETS _a=...
1379 
1380     "N4",  // 42 - base ring N4, unique to C
1381     "N2",  // 43 - base amino N2, unique to G
1382     "N6",  // 44 - base amino N6, unique to A
1383     "C5M", // 45 - base methyl carbon, unique to T
1384 
1385     "O6",  // 46 - base carbonyl O6, only in G and I
1386     "O4",  // 47 - base carbonyl O4, only in T and U
1387     "S4",  // 48 - base thiol sulfur, unique to thio-U
1388 
1389     "C7", // 49 - base methyl carbon, unique to DT
1390 
1391     "H1",  // 50  - NOT backbone
1392     "H2",  // 51 - NOT backbone -- see 1jve
1393     "H3",  // 52 - NOT backbone
1394     null, null, //53
1395     null, null, null, null, null, //55
1396     null, null, null, null,       //60 - 63
1397 
1398     // everything from here on is backbone
1399 
1400     // protein backbone
1401     //
1402     "OXT", // 64 - second carbonyl oxygen, C-terminus only
1403 
1404     // protein backbone hydrogens
1405     //
1406     "H",   // 65 - amino hydrogen
1407     // these appear on the N-terminus end of 1ALE & 1LCD
1408     "1H",  // 66 - N-terminus hydrogen
1409     "2H",  // 67 - second N-terminus hydrogen
1410     "3H",  // 68 - third N-terminus hydrogen
1411     "HA",  // 69 - H on alpha carbon
1412     "1HA", // 70 - H on alpha carbon in Gly only
1413     "2HA", // 71 - 1ALE calls the two GLY hdrogens 1HA & 2HA
1414 
1415     // Terminal nuclic acid
1416 
1417     "H5T", // 72 - 5' terminus hydrogen which replaces P + O1P + O2P
1418     "O5T", // 73 - 5' terminus oxygen which replaces P + O1P + O2P
1419     "O1P", // 74 - first equivalent oxygen on phosphorus of phosphate
1420     "OP1", // 75 - first equivalent oxygen on phosphorus of phosphate -- new designation
1421     "O2P", // 76 - second equivalent oxygen on phosphorus of phosphate
1422     "OP2", // 77 - second equivalent oxygen on phosphorus of phosphate -- new designation
1423 
1424     "O4'", // 78 - sugar ring 4' oxygen ... not present in +T ... maybe others
1425     "O2'", // 79 - sugar 2' oxygen, unique to RNA
1426 
1427     // nucleic acid backbone hydrogens
1428     //
1429     "1H5'", // 80 - first  equivalent H on sugar 5' carbon
1430     "2H5'", // 81 - second  equivalent H on sugar 5' carbon
1431     "H4'",  // 82 - H on sugar ring 4' carbon
1432     "H3'",  // 83 - H on sugar ring 3' carbon
1433     "1H2'", // 84 - first equivalent H on sugar ring 2' carbon
1434     "2H2'", // 85 - second equivalent H on sugar ring 2' carbon
1435     "2HO'", // 86 - H on sugar 2' oxygen, unique to RNA
1436     "H1'",  // 87 - H on sugar ring 1' carbon
1437     "H3T",  // 88 - 3' terminus hydrogen
1438 
1439     // add as many as necessary -- backbone only
1440 
1441     "HO3'", // 89 - 3' terminus hydrogen (new)
1442     "HO5'", // 90 - 5' terminus hydrogen (new)
1443     "HA2",
1444     "HA3",
1445     "HA2",
1446     "H5'",
1447     "H5''",
1448     "H2'",
1449     "H2''",
1450     "HO2'",
1451 
1452     "O3P", // 99    - third equivalent oxygen on phosphorus of phosphate
1453     "OP3", //100    - third equivalent oxygen on phosphorus of phosphate -- new designation
1454 
1455   };
1456 
1457   public final static int ATOMID_MAX = specialAtomNames.length;
1458 
getSpecialAtomName(int atomID)1459   final static String getSpecialAtomName(int atomID) {
1460     return specialAtomNames[atomID];
1461   }
1462 
1463   private static Map<String, Byte> htSpecialAtoms;
1464 
1465   private final static int[] argbsAmino = {
1466     0xFFBEA06E, // default tan
1467     // note that these are the rasmol colors and names, not xwindows
1468     0xFFC8C8C8, // darkGrey   ALA
1469     0xFF145AFF, // blue       ARG
1470     0xFF00DCDC, // cyan       ASN
1471     0xFFE60A0A, // brightRed  ASP
1472     0xFFE6E600, // yellow     CYS
1473     0xFF00DCDC, // cyan       GLN
1474     0xFFE60A0A, // brightRed  GLU
1475     0xFFEBEBEB, // lightGrey  GLY
1476     0xFF8282D2, // paleBlue   HIS
1477     0xFF0F820F, // green      ILE
1478     0xFF0F820F, // green      LEU
1479     0xFF145AFF, // blue       LYS
1480     0xFFE6E600, // yellow     MET
1481     0xFF3232AA, // midBlue    PHE
1482     0xFFDC9682, // mauve      PRO
1483     0xFFFA9600, // orange     SER
1484     0xFFFA9600, // orange     THR
1485     0xFFB45AB4, // purple     TRP
1486     0xFF3232AA, // midBlue    TYR
1487     0xFF0F820F, // green      VAL
1488 
1489     0xFFFF69B4, // pick a new color ASP/ASN ambiguous
1490     0xFFFF69B4, // pick a new color GLU/GLN ambiguous
1491     0xFFBEA06E, // default tan UNK
1492   };
1493 
1494   private final static int[] argbsNucleic = {
1495     0xFFBEA06E, // default tan
1496     0xFFA0A0A0, // grey       P
1497     0xFF0F820F, // green      G
1498     0xFFE6E600, // yellow     C
1499     0xFFE60A0A, // brightRed  A
1500     0xFF145AFF, // blue       T
1501     0xFF00DCDC, // cyan       U
1502     0xFF00DCDC, // cyan       I
1503 
1504     0xFF0F820F, // green      DG
1505     0xFFE6E600, // yellow     DC
1506     0xFFE60A0A, // brightRed  DA
1507     0xFF145AFF, // blue       DT
1508     0xFF00DCDC, // cyan       DU
1509     0xFF00DCDC, // cyan       DI
1510 
1511     0xFF0F820F, // green      +G
1512     0xFFE6E600, // yellow     +C
1513     0xFFE60A0A, // brightRed  +A
1514     0xFF145AFF, // blue       +T
1515     0xFF00DCDC, // cyan       +U
1516     0xFF00DCDC, // cyan       +I
1517   };
1518 
1519 
1520 
1521   /**
1522    * colors used for chains
1523    *
1524    */
1525 
1526   /****************************************************************
1527    * some pastel colors
1528    *
1529    * C0D0FF - pastel blue
1530    * B0FFB0 - pastel green
1531    * B0FFFF - pastel cyan
1532    * FFC0C8 - pink
1533    * FFC0FF - pastel magenta
1534    * FFFF80 - pastel yellow
1535    * FFDEAD - navajowhite
1536    * FFD070 - pastel gold
1537 
1538    * FF9898 - light coral
1539    * B4E444 - light yellow-green
1540    * C0C000 - light olive
1541    * FF8060 - light tomato
1542    * 00FF7F - springgreen
1543    *
1544 cpk on; select atomno>100; label %i; color chain; select selected & hetero; cpk off
1545    ****************************************************************/
1546 
1547   private final static int[] argbsChainAtom = {
1548     // ' '->0 'A'->1, 'B'->2
1549     0xFFffffff, // ' ' & '0' white
1550     //
1551     0xFFC0D0FF, // skyblue
1552     0xFFB0FFB0, // pastel green
1553     0xFFFFC0C8, // pink
1554     0xFFFFFF80, // pastel yellow
1555     0xFFFFC0FF, // pastel magenta
1556     0xFFB0F0F0, // pastel cyan
1557     0xFFFFD070, // pastel gold
1558     0xFFF08080, // lightcoral
1559 
1560     0xFFF5DEB3, // wheat
1561     0xFF00BFFF, // deepskyblue
1562     0xFFCD5C5C, // indianred
1563     0xFF66CDAA, // mediumaquamarine
1564     0xFF9ACD32, // yellowgreen
1565     0xFFEE82EE, // violet
1566     0xFF00CED1, // darkturquoise
1567     0xFF00FF7F, // springgreen
1568     0xFF3CB371, // mediumseagreen
1569 
1570     0xFF00008B, // darkblue
1571     0xFFBDB76B, // darkkhaki
1572     0xFF006400, // darkgreen
1573     0xFF800000, // maroon
1574     0xFF808000, // olive
1575     0xFF800080, // purple
1576     0xFF008080, // teal
1577     0xFFB8860B, // darkgoldenrod
1578     0xFFB22222, // firebrick
1579   };
1580 
1581   private final static int[] argbsChainHetero = {
1582     // ' '->0 'A'->1, 'B'->2
1583     0xFFffffff, // ' ' & '0' white
1584     //
1585     0xFFC0D0FF - 0x00303030, // skyblue
1586     0xFFB0FFB0 - 0x00303018, // pastel green
1587     0xFFFFC0C8 - 0x00303018, // pink
1588     0xFFFFFF80 - 0x00303010, // pastel yellow
1589     0xFFFFC0FF - 0x00303030, // pastel magenta
1590     0xFFB0F0F0 - 0x00303030, // pastel cyan
1591     0xFFFFD070 - 0x00303010, // pastel gold
1592     0xFFF08080 - 0x00303010, // lightcoral
1593 
1594     0xFFF5DEB3 - 0x00303030, // wheat
1595     0xFF00BFFF - 0x00001830, // deepskyblue
1596     0xFFCD5C5C - 0x00181010, // indianred
1597     0xFF66CDAA - 0x00101818, // mediumaquamarine
1598     0xFF9ACD32 - 0x00101808, // yellowgreen
1599     0xFFEE82EE - 0x00301030, // violet
1600     0xFF00CED1 - 0x00001830, // darkturquoise
1601     0xFF00FF7F - 0x00003010, // springgreen
1602     0xFF3CB371 - 0x00081810, // mediumseagreen
1603 
1604     0xFF00008B + 0x00000030, // darkblue
1605     0xFFBDB76B - 0x00181810, // darkkhaki
1606     0xFF006400 + 0x00003000, // darkgreen
1607     0xFF800000 + 0x00300000, // maroon
1608     0xFF808000 + 0x00303000, // olive
1609     0xFF800080 + 0x00300030, // purple
1610     0xFF008080 + 0x00003030, // teal
1611     0xFFB8860B + 0x00303008, // darkgoldenrod
1612     0xFFB22222 + 0x00101010, // firebrick
1613   };
1614 
1615   private final static int[] argbsShapely = {
1616     0xFFFF00FF, // default
1617     // these are rasmol values, not xwindows colors
1618     0xFF00007C, // ARG
1619     0xFFFF7C70, // ASN
1620     0xFF8CFF8C, // ALA
1621     0xFFA00042, // ASP
1622     0xFFFFFF70, // CYS
1623     0xFFFF4C4C, // GLN
1624     0xFF660000, // GLU
1625     0xFFFFFFFF, // GLY
1626     0xFF7070FF, // HIS
1627     0xFF004C00, // ILE
1628     0xFF455E45, // LEU
1629     0xFF4747B8, // LYS
1630     0xFF534C52, // PHE
1631     0xFFB8A042, // MET
1632     0xFF525252, // PRO
1633     0xFFFF7042, // SER
1634     0xFFB84C00, // THR
1635     0xFF4F4600, // TRP
1636     0xFF8C704C, // TYR
1637     0xFFFF8CFF, // VAL
1638 
1639     0xFFFF00FF, // ASX ASP/ASN ambiguous
1640     0xFFFF00FF, // GLX GLU/GLN ambiguous
1641     0xFFFF00FF, // UNK unknown -- 23
1642 
1643     0xFFFF7070, // G
1644     0xFFFF8C4B, // C
1645     0xFFA0A0FF, // A
1646     0xFFA0FFA0, // T
1647     0xFFFF8080, // U miguel made up this color
1648     0xFF80FFFF, // I miguel made up this color
1649 
1650     0xFFFF7070, // DG
1651     0xFFFF8C4B, // DC
1652     0xFFA0A0FF, // DA
1653     0xFFA0FFA0, // DT
1654     0xFFFF8080, // DU
1655     0xFF80FFFF, // DI
1656 
1657     0xFFFF7070, // +G
1658     0xFFFF8C4B, // +C
1659     0xFFA0A0FF, // +A
1660     0xFFA0FFA0, // +T
1661     0xFFFF8080, // +U
1662     0xFF80FFFF, // +I
1663 
1664     // what to do about remediated +X names?
1665     // we will need a map
1666 
1667   };
1668 
1669   static {
1670     /**
1671      * @j2sNative
1672      *
1673      */
1674     {
1675       if (argbsShapely.length != JC.GROUPID_WATER) {
1676         Logger.error("argbsShapely wrong length");
NullPointerException()1677         throw new NullPointerException();
1678       }
1679       if (argbsAmino.length != JC.GROUPID_AMINO_MAX) {
1680         Logger.error("argbsAmino wrong length");
NullPointerException()1681         throw new NullPointerException();
1682       }
1683       if (argbsChainHetero.length != argbsChainAtom.length) {
1684         Logger.error("argbsChainHetero wrong length");
NullPointerException()1685         throw new NullPointerException();
1686       }
1687     }
1688 
1689   }
1690 
getArgbs(int tok)1691   public int[] getArgbs(int tok) {
1692     switch (tok) {
1693     case T.nucleic:
1694       return argbsNucleic;
1695     case T.amino:
1696       return argbsAmino;
1697     case T.shapely:
1698       return argbsShapely;
1699     case T.atoms:
1700       return argbsChainAtom;
1701     case T.hetero:
1702       return argbsChainHetero;
1703     }
1704     return null;
1705   }
1706 
getBioModelSet(ModelSet modelSet)1707   public BioModelSet getBioModelSet(ModelSet modelSet) {
1708     if (modelSet.bioModelset == null)
1709       modelSet.bioModelset = new BioModelSet().set(vwr, modelSet);
1710     return modelSet.bioModelset;
1711   }
1712 
1713 }
1714 
1715 
1716