Revision: 21654 http://sourceforge.net/p/jmol/code/21654 Author: hansonr Date: 2017-07-07 13:45:55 +0000 (Fri, 07 Jul 2017) Log Message: ----------- Jmol.___JmolVersion="14.20.2"
bug fix: write SDF and write MOL do not set atom parity field bug fix: JavaScript JSmol broken for chirality due to bug in Clazz.clone(obj) Modified Paths: -------------- trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java trunk/Jmol/src/org/jmol/viewer/Jmol.properties trunk/Jmol/src/org/jmol/viewer/PropertyManager.java Modified: trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java =================================================================== --- trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java 2017-07-06 12:38:51 UTC (rev 21653) +++ trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java 2017-07-07 13:45:55 UTC (rev 21654) @@ -141,7 +141,7 @@ * code history: * * 7/6/17 Jmol 14.20.1 major rewrite to correct and simplify logic; full validation - * for 433 structures (many duplicates) in AY236, BH64, MV64, MV116, JM, and L (839 lines) + * for 433 structures (many duplicates) in AY236, BH64, MV64, MV116, JM, and L (837 lines) * * 6/30/17 Jmol 14.20.1 major rewrite of Rule 4b (999 lines) * @@ -251,11 +251,6 @@ public class CIPChirality { - public void xxx() { - - } - - //The rules: // // P-92.1.3.1 Sequence Rule 1 has two parts: @@ -295,21 +290,27 @@ // An atom or group with descriptor 'R', 'M' and 'seqCis' has priority over its // enantiomorph 'S', 'P' or 'seqTrans', 'seqCis' or 'seqTrans'. + + // + // Implementation Notes + // + // + + // "Scoring" a vs. b involves returning 0 (TIE) or +/-n, where n>0 indicates b won, n < 0 - // indicates a won, and the |a| indicates in which shell the decision was made. - // The basic strategy is to loop through the Sequential Rules 1-5, including all parts - // exhaustively prior to applying another rule. This includes subparts: + // indicates a won, and the |n| indicates in which sphere the decision was made. + // The basic strategy is to loop through all eight sequence rules (1a, 1b, 2, 3, 4a, 4b, 4c, and 5) + // in order and exhaustively prior to applying the next rule: // // Rule 1a (atomic number -- note that this requires an aromaticity check first) // Rule 1b (duplicated atom progenitor root-distance check; revised as described above // for aromatic systems. // Rule 2 (nominal isotopic mass) - // Rule 3 (E/Z not including enantiomorphic seqCis/seqTrans designations) - // Rule 4a (chiral precedes pseudochiral precedes nonstereochemical; more of a guideline - // than a sequential rule) - // Rule 4b like precedes unlike - // Rule 4c r precedes s - // Rule 5 R precedes S; M precedes P (final determination of pseudoasymmetry descriptors) + // Rule 3 (E/Z, not including enantiomorphic seqCis/seqTrans designations) + // Rule 4a (chiral precedes pseudochiral precedes nonstereochemical) + // Rule 4b (like precedes unlike) + // Rule 4c (r precedes s) + // Rule 5 (R precedes S; M precedes P; C precedes T) // // Some nuances I've learned along the way here, some of which are still being checked: // @@ -322,7 +323,7 @@ // 4. Rule 3 requires the concept of "auxiliary" (temporary, digraph-specific) descriptors. // This concept of auxiliary descriptors is the key to not having an analysis // blow up or somehow require complex, impossible iteration. - // 5. Rule 4a needs to be addressed exhaustively prior to Rules 4b and 4c. This involves the + // 5. Rule 4a needs to be addressed exhaustively prior to Rules 4b and 4c. This involves // the initial generation of all auxiliary descriptors, including r/s and R/S at // branching points. In the course of doing this, all rules, 1-5, must be employed // at these auxiliary centers using the already-created digraph. This rule serves to @@ -329,19 +330,86 @@ // avoid the need for Rule 4b for all except the most unusual cases, where, for example, // there are two otherwise identical branches, but one can be identified as S and the // other only r or no-stereo, but not R. Thus, only branches that end up as R/R, R/S, S/S, - // r/r, r/s, s/s, or nst/nst need be investigated by Rule 4b. - // 6. Rule 4b Somehow missed in the discussion is that the reference descriptor is determined - // once and only once for each branch from the center under scrutiny. All Rules - // preceding Rule 4 can be applied to iterated subsections of a digraph. Not this one, - // nor Rule 5, though. The key is to determine one single "Mata sequence" of R and S descriptors - // for each pair of branches being considered. This same reference carries through all - // future iterations of the algorithm for that branch. + // r/r, r/s, s/s, or nst/nst comparisons need be investigated by Rule 4b. + // 6. Rule 4b This rule filters out all diastereomorphic differences that do not involve r/s issues. + // Somehow missed in the discussion is that the reference descriptor is determined + // once and only once for each branch from the center under scrutiny. The key is to + // determine two "Mata sequences" of R and S descriptors, one for each pair of branches being + // considered. This same reference carries through all future iterations of the algorithm + // for that branch. // 7. Rule 4c Again, this subrule must be invoked only after Rule 4b is completed, and again - // it is only for the root branches, not anywhere else. - // 8. Rule 5 Final setting pseudoasymmetry (r/s, m/p) can be done along the same line as Rule 4b, - // but with slightly different sorting criteria. + // it is only for the root branches, not anywhere else. It filters out any remaining + // diastereomorphic differences based on r/s/ns branch assignments. + // 8. Rule 5 Final setting of pseudoasymmetry (r/s, m/p) is done along the same line as Rule 4b, + // but in this case by setting the reference descriptor to "R" for both sequences. // + /** + * The basic idea is to switch from a tree metaphor to a "twisted strand" or + * "thread" metaphor. For example: + * + * (a) In Rule 1b, all ring-duplicates terminate on one of the nodes in the + * sequence of parent nodes going back to the root. This has nothing to do + * with branching. + * + * (b) Generation of auxiliary descriptors prior to implementation of Rule 4 + * must start from the highest sphere, proceeding toward the root. In this + * process the path leading back to the root will have no stereodescriptors, + * but that does not matter, as its priority is guaranteed to be set by Rule + * 1a. + * + * (c) All auxiliary nodes can be normalized by labeling them one of + * {R,S,r,s}; there is no need to consider them to be C/T (seqCis or + * seqTrans), M/P, or m/p, other than to immediately equate that to R/S or + * r/s. Note that C/T and M/P must be assigned to the sp2 node closest to the + * root. + * + * (d) Rule 4b can be analyzed using a "thread" metaphor in a five-step + * process: + * + * (d1) Generate a set of n threads leading from the root and terminating on + * highest-ranking stereogenic centers. All nodes must be included, not just + * stereogenic centers. + * + * (d2) Determine the reference descriptors. + * + * (d3) Sort these threads by priority (including that determined by Rule 4a) + * and reference descriptors. + * + * (d4) Note that the data can be seen as two n x m matrices, where the rows + * are the threads. Now "flatten" the data to produce two 1D sequences of + * descriptors by simply reading out the data in column order. + * + * (d5) Prioritize these two sequences, looking for the first diastereotopic + * difference. + * + * (e) Rule 5 processing is just a repeat of Rule 4b processing, where the + * reference descriptor is now set to "R". + * + * (f) Tests for root-only spiro cases must be done along with each rule's + * processing prior to continuing to the next rule. This is done by looking + * for situations where there are two sets of matched priorities. These will + * be broken by the axial nature of spiro connections. + * + * (g) A test for root-only double enantiotopic cases (RSR'S') must be done + * after Rule 5, allowing for the possibility for this test to return R/S or + * M/P, not just r/s and m/p. + * + * Jmol's threads in Step d1 are just strings. Jmol carries out Steps d1 and + * d2 simultaneously with auxiliary descriptor generation, tracking the sphere + * and priority of all auxiliary descriptors as it generates them. Sorting in + * Step d3 is done in Jmol using a simple java Array.sort(); no actual + * matrices are involved. + * + * Finally, the "like/unlike" business is replaced with a simple + * diastereotopic test. Thus, the strings are tested for the first point at + * which they are neither the same nor opposites. I like thinking about it + * this way because it focuses on what Rule 4b's role -- the identification of + * diastereomorphism. Rule 4c takes care of diasteriomorphism related to + * enantiomorphic sub-paths; Rule 5 finally takes care of any remaining + * enantiomorphic issues. + */ + // The algorithm: // // @@ -424,7 +492,7 @@ static final int DIASTEREOMERIC_B_WINS = 2; static final int ENANTIOMERIC_A_WINS = -3; static final int ENANTIOMERIC_B_WINS = 3; -static final int IGNORE = Integer.MIN_VALUE; + static final int IGNORE = Integer.MIN_VALUE; static final int CHECK_BRANCH = Integer.MIN_VALUE; static final int STEREO_UNDETERMINED = -1; @@ -450,7 +518,7 @@ static final int RULE_4c = 7; static final int RULE_5 = 8; - static String prefixString = ".........."; + static String prefixString = ".........."; // Logger only static Integer zero = Integer.valueOf(0); @@ -521,7 +589,19 @@ V3 vNorm = new V3(), vNorm2 = new V3(), vTemp = new V3(); + /** + * a testing flag that when false does not involve alkenes in + * duplicate root distance checks; AY236_215 and BH64_002,003,004,009 + * all fail if this is set false + */ boolean allowRule1bAlkenes = true; + + + /** + * return auxiliary chirality settings for all atoms when only one atom is + * selected and TESTFLAG1 has been set TRUE in Jmol + * + */ boolean setAuxiliary; public CIPChirality() { @@ -2161,6 +2241,33 @@ } /** + * Sort for auxiliary chirality determination + * + * @param maxRule + * @return TIED or deciding rule RULE_1a - RULE_5 + */ + private int sortToRule(int maxRule) { + for (int i = RULE_1a; i <= maxRule; i++) + if (sortByRule(i)) + return i; + return TIED; + } + + /** + * Sort by a given rule, preserving currentRule. + * + * @param rule + * @return true if a decision has been made + */ + private boolean sortByRule(int rule) { + int current = currentRule; + currentRule = rule; + boolean isChiral = sortSubstituents(0); + currentRule = current; + return isChiral; + } + + /** * Check this atom "A" vs a challenger "B" against the current rule. * * Note that example BB 2013 page 1258, P93.5.2.3 requires that RULE_1b be @@ -2254,6 +2361,24 @@ } /** + * The result of checking a Mata series of parallel paths may be one of + * several values. TIED here probably means something went wrong; IGNORE + * means we have two of the same chirality, for example RSRR RSRR. In that + * case, we defer to Rule 5. The idea is to handle all such business in + * Sphere 1. + * + * + * @param i + * @param j + * @return 0 (TIED), -1 (A_WINS), 1 (B_WINS), Integer.MIN_VALUE (IGNORE) + */ + private int checkRule4And5(int i, int j) { + return (rule4List[i] == null && rule4List[j] == null ? IGNORE + : rule4List[j] == null ? A_WINS : rule4List[i] == null ? B_WINS + : compareMataPair(atoms[i], atoms[j])); + } + + /** * Check auxiliary Z by temporarily setting return path. * * This method uses CIPAtom.clone() effectively to create a second @@ -2336,34 +2461,7 @@ return null; } - /** - * Sort for auxiliary chirality determination - * - * @param maxRule - * @return TIED or deciding rule RULE_1a - RULE_5 - */ - private int sortToRule(int maxRule) { - for (int i = RULE_1a; i <= maxRule; i++) - if (sortByRule(i)) - return i; - return TIED; - } - - /** - * Sort by a given rule, preserving currentRule. - * - * @param rule - * @return true if a decision has been made - */ - private boolean sortByRule(int rule) { - int current = currentRule; - currentRule = rule; - boolean isChiral = sortSubstituents(0); - currentRule = current; - return isChiral; - } - /** * * @param newParent @@ -2422,24 +2520,6 @@ } /** - * The result of checking a Mata series of parallel paths may be one of - * several values. TIED here probably means something went wrong; IGNORE - * means we have two of the same chirality, for example RSRR RSRR. In that - * case, we defer to Rule 5. The idea is to handle all such business in - * Sphere 1. - * - * - * @param i - * @param j - * @return 0 (TIED), -1 (A_WINS), 1 (B_WINS), Integer.MIN_VALUE (IGNORE) - */ - private int checkRule4And5(int i, int j) { - return (rule4List[i] == null && rule4List[j] == null ? IGNORE - : rule4List[j] == null ? A_WINS : rule4List[i] == null ? B_WINS - : compareMataPair(atoms[i], atoms[j])); - } - - /** * Chapter 9 Rules 4b and 5: like vs. unlike; for root substituents only. * * (1) Generate full set of branching stereochemical paths (rule4Paths) for @@ -2598,8 +2678,6 @@ private void getRule4Counts(Object[] counts) { if (sphere > ((Integer) counts[3]).intValue()) return; - if (setAuxiliary && auxChirality != "~") - atom.setCIPChirality(JC.getCIPChiralityCode(auxChirality)); if (rule4Type > 0) { // Accumlate the number of R and S centers of a given cumlative priority int val = sign(priorityPath.length() - (counts[0] == null ? 10000 : ((String) counts[0]).length())); @@ -2719,12 +2797,16 @@ } /** - * Comparison of two strings such as RSSR and SRSS for Rule 4b. + * Rule 4b comparison of two strings such as RSSR and SRSS, looking for + * diasteriomeric differences only. A return of IGNORE indicates that + * they are either the same or opposites and tells sortSubstituents to + * not explore more deeply. * * @param aStr * @param bStr * @param isRSTest * This is just a test for optional pathways, for example: RS|SR + * * * * @return 0 (TIED), -1 (A_WINS), 1 (B_WINS), Integer.MIN_VALUE (IGNORE) @@ -2745,8 +2827,9 @@ } /** - * Creates a list of downstream (higher-sphere) auxiliary chirality - * designators, starting with those furthest from the root. + * By far the most complex of the methods, this method creates a list of + * downstream (higher-sphere) auxiliary chirality designators, starting with + * those furthest from the root and moving in, toward the root. * * @param node1 * first node; sphere 1 @@ -2796,7 +2879,7 @@ } } } - boolean isBranch = false; + boolean isBranch = (nRS >= 2); switch (nRS) { case 0: subRS = ""; @@ -2809,7 +2892,6 @@ case 4: s = "~"; subRS = ""; - isBranch = true; if (ret != null) ret[0] = this; break; @@ -2893,6 +2975,9 @@ } auxChirality = s; } + if (setAuxiliary && auxChirality != "~") + atom.setCIPChirality(JC.getCIPChiralityCode(auxChirality)); + if (node1 == null) rule4Type = nRS; s += subRS; Modified: trunk/Jmol/src/org/jmol/viewer/Jmol.properties =================================================================== --- trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2017-07-06 12:38:51 UTC (rev 21653) +++ trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2017-07-07 13:45:55 UTC (rev 21654) @@ -56,8 +56,13 @@ # TODO: fix UNDO -Jmol.___JmolVersion="14.20.1" +Jmol.___JmolVersion="14.20.2" +bug fix: write SDF and write MOL do not set atom parity field +bug fix: JavaScript JSmol broken for chirality due to bug in Clazz.clone(obj) + +JmolVersion="14.20.1" // 2017.07.06 + new feature: BZ2 compressed file reader -- uses org.apache.tools.bzip2.CBZip2InputStream v. 1.9.6 -- Apache license Modified: trunk/Jmol/src/org/jmol/viewer/PropertyManager.java =================================================================== --- trunk/Jmol/src/org/jmol/viewer/PropertyManager.java 2017-07-06 12:38:51 UTC (rev 21653) +++ trunk/Jmol/src/org/jmol/viewer/PropertyManager.java 2017-07-07 13:45:55 UTC (rev 21654) @@ -39,11 +39,13 @@ import javajs.util.Lst; import javajs.util.M3; import javajs.util.M4; +import javajs.util.Measure; import javajs.util.OC; import javajs.util.P3; import javajs.util.PT; import javajs.util.Quat; import javajs.util.SB; +import javajs.util.T3; import javajs.util.V3; import javajs.util.XmlUtil; @@ -93,6 +95,8 @@ Viewer vwr; private Map<String, Integer> map = new Hashtable<String, Integer>(); + private T3 vNorm; + private T3 vTemp; @Override public void setViewer(Viewer vwr) { @@ -1329,7 +1333,7 @@ } else { PT.rightJustify(mol, " ", "" + nAtoms); PT.rightJustify(mol, " ", "" + nBonds); - mol.append(" 0 0 0 0 1 V2000"); + mol.append(" 0 0 0 0 999 V2000"); } if (!asJSON) mol.append("\n"); @@ -1503,11 +1507,10 @@ mol.append(" "); if (iso > 0) iso -= Elements.getNaturalIsotope(a.getElementNumber()); - mol.append(" "); - PT.rightJustify(mol, " ", "" + iso); + PT.rightJustify(mol, " ", "" + iso); PT.rightJustify(mol, " ", "" + (charge == 0 ? 0 : 4 - charge)); - mol.append(" 0 0 0 0\n"); - + mol.append(" ").append(getAtomParity(a)); + mol.append(" 0 0 0\n"); String label = (atomValues == null || asV3000 ? null : getAtomPropertyAsString(a, tokValue)); if (label != null && (label = label.trim()).length() > 0) { @@ -1517,7 +1520,38 @@ } } } + private int[] connections; + private String getAtomParity(Atom a) { + if (a.getCovalentBondCount() == 4) { + if (connections == null) { + connections = new int[4]; + vTemp = new V3(); + vNorm = new V3(); + } + Bond[] bonds = a.bonds; + int nH = 0; + for (int pt = 0, i = bonds.length; --i >= 0;) { + if (bonds[i].isCovalent()) { + Atom b = bonds[i].getOtherAtom(a); + if (b.getAtomicAndIsotopeNumber() == 1) + nH++; + connections[pt++] = b.i; + } + } + if (nH < 3) { + Arrays.sort(connections); + Atom[] atoms = vwr.ms.at; + Measure.getNormalThroughPoints(atoms[connections[0]], + atoms[connections[1]], atoms[connections[2]], vNorm, vTemp); + vTemp.sub2(atoms[connections[3]], atoms[connections[0]]); + return (vTemp.dot(vNorm) > 0 ? "1" : "2"); + } + } + return "0"; + } + + private P3 ptTemp; private String getAtomPropertyAsString(Atom a, int tok) { switch (tok & T.PROPERTYFLAGS) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Jmol-commits mailing list Jmol-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jmol-commits