Revision: 21678 http://sourceforge.net/p/jmol/code/21678 Author: hansonr Date: 2017-07-26 01:53:43 +0000 (Wed, 26 Jul 2017) Log Message: -----------
Modified Paths: -------------- trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java trunk/Jmol/src/org/jmol/viewer/Jmol.properties Modified: trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java =================================================================== --- trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java 2017-07-24 02:43:49 UTC (rev 21677) +++ trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java 2017-07-26 01:53:43 UTC (rev 21678) @@ -145,6 +145,7 @@ * * code history: * + * 7/25/17 Jmol 14.20.4 consolidates all ene determinations; moves auxiliary descriptor generation to prior to Rule 3 (850 lines) * 7/23/17 Jmol 14.20.4 adds Rule 6; rewrite/consolidate spiro, C3, double spiran code (853 lines) * 7/19/17 Jmol 14.20.3 fixing Rule 2 (880 lines) * 7/13/17 Jmol 14.20.3 more thorough spiro testing (858 lines) @@ -563,8 +564,10 @@ static Integer zero = Integer.valueOf(0); - public String getRuleName() { - return JC.getCIPRuleName(currentRule); + final static String[] ruleNames = {"", "1a", "1b", "2", "3", "4a", "4b", "4c", "5", "6"}; // Logger only + + public String getRuleName(int rule) { // Logger only + return ruleNames[rule]; // Logger only } /** @@ -1199,27 +1202,28 @@ } } if (cipAtom.setNode()) { - for (currentRule = RULE_1a; currentRule <= RULE_6; currentRule++) { - + for (currentRule = RULE_1a; currentRule <= RULE_6; currentRule++) { if (Logger.debugging) - Logger.info("-Rule " + getRuleName() + " CIPChirality for " + Logger.info("-Rule " + getRuleName(currentRule) + " CIPChirality for " + cipAtom + "-----"); // Logger switch (currentRule) { + case RULE_3: + // We need to create auxiliary descriptors PRIOR to Rule 3. + cipAtom.createAuxiliaryDescriptors(null, null); + break; case RULE_4a: + if (cipAtom.rule4Type == 0) { + // We can skip Rules 4a - 5 if there are no chirality centers. + currentRule = RULE_5; + continue; + } + //$FALL-THROUGH$ case RULE_4c: - if (currentRule == RULE_4a) { - cipAtom.createRule4AuxiliaryData(null, null); - if (cipAtom.rule4Type == 0) { - // we can skip Rules 4a - 5 if there are no chirality centers - break; - } - } - // we need to presort with no tie-breaking for Rules 4 + // We need to presort with no tie-breaking for Rules 4a and 4c. cipAtom.sortSubstituents(Integer.MIN_VALUE); break; - case RULE_5: - break; case RULE_6: + // We only need to do Rule 6 under certain conditions. if (!cipAtom.setupRule6(false)) continue; break; @@ -1279,13 +1283,13 @@ } if (Logger.debugging) Logger.info(atom + " " + JC.getCIPChiralityName(rs) + " by Rule " - + getRuleName() + "\n----------------------------------"); // Logger + + getRuleName(currentRule) + "\n----------------------------------"); // Logger return rs; } } } } catch (Throwable e) { - System.out.println(e + " in CIPChirality"); + System.out.println(e + " in CIPChirality " + currentRule); /** * @j2sNative alert(e); */ @@ -2029,7 +2033,7 @@ boolean sortSubstituents(int sphere) { // runs about 20% faster with this check - if (nPriorities == (sphere == 0 ? 4 : 3)) + if (nPriorities == (sphere < 1 ? 4 : 3)) return true; // Note that this method calls breakTie and is called recursively from breakTie. @@ -2066,7 +2070,7 @@ if (Logger.debuggingHigh) { Logger.info(root + "---sortSubstituents---" + this); for (int i = 0; i < 4; i++) { // Logger - Logger.info(getRuleName() + ": " + this + "[" + i + "]=" + Logger.info(getRuleName(currentRule) + ": " + this + "[" + i + "]=" + atoms[i].myPath + " " + Integer.toHexString(prevPrior[i])); // Logger } Logger.info("---" + nPriorities); @@ -2304,19 +2308,6 @@ } /** - * Sort for auxiliary chirality determination - * - * @param maxRule - * @return TIED or deciding rule RULE_1a - RULE_5 - */ - private int sortToRule(int maxRule, boolean skipRules4And5) { - for (int i = RULE_1a; i <= maxRule; i++) - if ((!skipRules4And5 || i < RULE_4a || i > RULE_5) &&sortByRule(i)) - return i; - return 0; - } - - /** * Sort by a given rule, preserving currentRule, which could be 4 or 5 * * @param rule @@ -2326,8 +2317,10 @@ int current = currentRule; currentRule = rule; int rule6ref = root.rule6refIndex; + if (rule6ref != -1) + System.out.println("?????"); if (rule == RULE_6) - setupRule6(true); + setupRule6(true); boolean isChiral = sortSubstituents(0); root.rule6refIndex = rule6ref; currentRule = current; @@ -2427,56 +2420,29 @@ * @return 0 (TIED), -1 (A_WINS), or 1 (B_WINS) */ private int checkRule3(CIPAtom b) { - int za, zb; return isDuplicate || b.isDuplicate || !parent.isAlkeneAtom2 || !b.parent.isAlkeneAtom2 || !parent.alkeneParent.isEvenEne - || !b.parent.alkeneParent.isEvenEne ? IGNORE - : parent == b.parent ? sign(breakTie(b, 0)) : (za = parent - .getRule3auxEZ()) < (zb = b.parent.getRule3auxEZ()) ? A_WINS - : za > zb ? B_WINS : TIED; + || !b.parent.alkeneParent.isEvenEne || parent == b.parent ? TIED + : parent.auxEZ < b.parent.auxEZ ? A_WINS + : parent.auxEZ > b.parent.auxEZ ? B_WINS : TIED; } /** - * Check auxiliary Z by temporarily setting return path. + * Determine the winner on one end of an alkene or cumulene + * and return also the rule by which that was determined. * - * This method uses CIPAtom.clone() effectively to create a second - * independent path that is checked without messing up the currently - * expanding node graph. - * - * Note that one path from the current atom is in reverse - the path back to - * the root atom. This must be reconstructed, because until this point we - * have not carried out many of the necessary comparisons. - * - * @return one of [STEREO_Z, STEREO_E, STEREO_BOTH_EZ] - */ - private int getRule3auxEZ() { - - // "this" is the second atom of the alkene, checked as the parent of one of its ligands, - // as there is no need to do this test until we are on the branch that includes - // the atom after the alkene. - - return alkeneParent.auxEZ = (auxEZ != UNDETERMINED ? auxEZ - : (auxEZ = getAuxEneWinnerChirality(alkeneParent, this, false, RULE_3)) == NO_CHIRALITY ? (auxEZ = STEREO_BOTH_EZ) - : auxEZ); - } - - /** - * Determine the winner on one end of an alkene or cumulene, accepting a max - * rule of RULE_3 or RULE_5, depending upon the application - * * @param end1 * @param end2 * @param isAxial - * @param maxRule - * RULE_3 or RULE_5 + * @param retRule2 return for rule found for child end (furthest from root) * @return one of: {NO_CHIRALITY | STEREO_Z | STEREO_E | STEREO_M | * STEREO_P} */ private int getAuxEneWinnerChirality(CIPAtom end1, CIPAtom end2, - boolean isAxial, int maxRule) { - CIPAtom winner1 = getAuxEneEndWinner(end1, end1.nextSP2, maxRule); + boolean isAxial, int[] retRule2) { + CIPAtom winner1 = getAuxEneEndWinner(end1, end1.nextSP2, null); CIPAtom winner2 = (winner1 == null || winner1.atom == null ? null - : getAuxEneEndWinner(end2, end2.nextSP2, maxRule)); + : getAuxEneEndWinner(end2, end2.nextSP2, retRule2)); return getEneChirality(winner1, end1, end2, winner2, isAxial, false); } @@ -2487,36 +2453,31 @@ * * @param end * @param prevSP2 - * @param maxRule + * @param retRule + * return for deciding rule * @return higher-priority atom, or null if they are equivalent */ - private CIPAtom getAuxEneEndWinner(CIPAtom end, CIPAtom prevSP2, int maxRule) { + private CIPAtom getAuxEneEndWinner(CIPAtom end, CIPAtom prevSP2, + int[] retRule) { CIPAtom atom1 = (CIPAtom) end.clone(); if (atom1.parent != prevSP2) atom1.addReturnPath(prevSP2, end); - else if (maxRule == RULE_5) - atom1.rule4List = end.rule4List; + atom1.rule4List = end.rule4List; CIPAtom a; - for (int i = RULE_1a; i <= maxRule; i++) - if ((a = atom1.getTopSorted(i)) != null) - return (a.atom == null ? null : a); - return null; - } - - /** - * Return top-priority non-sp2-duplicated atom. - * - * @param rule - * - * @return highest-priority non-duplicated atom - */ - private CIPAtom getTopSorted(int rule) { - if (sortByRule(rule)) - for (int i = 0; i < 4; i++) { - CIPAtom a = atoms[i]; - if (!a.multipleBondDuplicate) - return priorities[i] == priorities[i + 1] ? null : atoms[i]; + for (int rule = RULE_1a; rule <= RULE_6; rule++) { + if (atom1.sortByRule(rule)) { + for (int i = 0; i < 4; i++) { + a = atom1.atoms[i]; + if (!a.multipleBondDuplicate) { + if (atom1.priorities[i] != atom1.priorities[i + 1]) { + if (retRule != null) + retRule[0] = rule; + return (a.atom == null ? null : a); + } + } + } } + } return null; } @@ -2896,30 +2857,25 @@ * * @return collective string, with setting of rule4List */ - String createRule4AuxiliaryData(CIPAtom node1, CIPAtom[] ret) { + String createAuxiliaryDescriptors(CIPAtom node1, CIPAtom[] ret) { String retThread = ""; char c = '~'; if (atom == null) return "" + c; - rule4List = new String[4]; // full list based on atoms[] - if (nPriorities == 0 && !isSet) { - setNode(); - if (!isAlkene && !isDuplicate && !isTerminal) - sortToRule(RULE_3, false); - } + setNode(); int rs = -1, nRS = 0; CIPAtom[] ret1 = new CIPAtom[1]; boolean skipRules4And5 = false; boolean prevIsChiral = true; - // have to allow two same f because could be a C3-symmetric subunit - boolean allowTwoSame = (node1 != null && !isAlkene && nPriorities == 1); + // have to allow two same because could be a C3-symmetric subunit + boolean allowTwoSame = (!isAlkene && nPriorities == (node1 == null ? 2 : 1)); for (int i = 0; i < 4; i++) { CIPAtom a = atoms[i]; if (a != null && !a.isDuplicate && !a.isTerminal) { a.priority = priorities[i]; ret1[0] = null; - String rsPath = a.createRule4AuxiliaryData(node1 == null ? a : node1, + String rsPath = a.createAuxiliaryDescriptors(node1 == null ? a : node1, ret1); if (ret1[0] != null) { a.nextChiralBranch = ret1[0]; @@ -2934,12 +2890,13 @@ retThread = rsPath; prevIsChiral = true; } else { - if (!allowTwoSame && !prevIsChiral && priorities[i] == priorities[i - 1]) { + if (!allowTwoSame && !prevIsChiral + && priorities[i] == priorities[i - 1]) { // actually, I don't think priorities are set yet. if (node1 == null) { for (; i >= 0; --i) rule4List[i] = null; - // two groups have the same priority, and neither has a stereocenter + // two groups have the same priority, and neither has a stereocenter } return "~"; } @@ -2965,9 +2922,8 @@ break; } if (isAlkene) { - if (!isBranch && alkeneChild != null) { - // must be alkeneParent -- first C of an alkene -- this is where C/T is recorded - boolean isSeqCT = (ret != null && ret[0] == alkeneChild); + if (alkeneChild != null) { + // must be alkeneParent -- first C of an alkene -- this is where C/T is recorded // All odd cumulenes need to be checked. // If it is an alkene or even cumulene, we must do an auxiliary check // only if it is not already a defined stereochemistry, because in that @@ -2974,9 +2930,10 @@ // case we have a simple E/Z (c/t), and there is no need to check AND // it does not contribute to the Mata sequence (similar to r/s or m/p). // - if (!isEvenEne || (auxEZ == STEREO_BOTH_EZ || auxEZ == UNDETERMINED) - && alkeneChild.bondCount >= 2 && !isKekuleAmbiguous) { - rs = getAuxEneWinnerChirality(this, alkeneChild, !isEvenEne, RULE_5); + + if (!isEvenEne || (auxEZ == STEREO_BOTH_EZ || auxEZ == UNDETERMINED) && !isKekuleAmbiguous && alkeneChild.bondCount >= 2) { + int[] rule2 = (isEvenEne ? new int[1] : null); + rs = getAuxEneWinnerChirality(this, alkeneChild, !isEvenEne, rule2); // // Note that we can have C/T (rule4Type = R/S): // @@ -2988,28 +2945,44 @@ // // flips sense upon planar inversion; determination was Rule 5. // - // Normalize M/P and E/Z to R/S - switch (rs) { - case STEREO_M: - case STEREO_Z: - rs = STEREO_R; - c = 'R'; - break; - case STEREO_P: - case STEREO_E: - rs = STEREO_S; - c = 'S'; - break; - } - if (rs != NO_CHIRALITY) { - auxChirality = c; - rule4Type = rs; - retThread = ""; - if (isSeqCT) { - nextChiralBranch = alkeneChild; - ret[0] = this; + // and ALSO we can have c/t here that has not been discovered yet + // + // + // SR x + // \ / + // == + // / \ + // SS root + + if (rs == NO_CHIRALITY) { + auxEZ = alkeneChild.auxEZ = STEREO_BOTH_EZ; + } else { + if (rule2 != null && rule2[0] != RULE_5) { + // This is the case of a 3b issue + //System.out.println(this + "root needs 3b " + root + getRuleName(rule2[0]) + " " + rs); + auxEZ = alkeneChild.auxEZ = rs; + } else if (!isBranch) { + // Normalize M/P and E/Z to R/S + switch (rs) { + case STEREO_M: + case STEREO_Z: + rs = STEREO_R; + c = 'R'; + break; + case STEREO_P: + case STEREO_E: + rs = STEREO_S; + c = 'S'; + break; + } + auxChirality = c; + rule4Type = rs; + retThread = ""; + if (ret != null && ret[0] == alkeneChild) { + nextChiralBranch = alkeneChild; + ret[0] = this; + } } - } } } @@ -3027,8 +3000,11 @@ } } } - int rule = atom1.sortToRule(RULE_6, skipRules4And5); - if (rule < RULE_1a) { + int rule = RULE_1a; + for (; rule <= RULE_6; rule++) + if ((!skipRules4And5 || rule < RULE_4a || rule > RULE_5) && atom1.sortByRule(rule)) + break; + if (rule > RULE_6) { c = '~'; } else { rs = atom1.checkHandedness(); @@ -3149,6 +3125,7 @@ a.rule4Count = null; a.rule4List = null; a.rootRule4Paths = null; + a.auxEZ = UNDETERMINED; a.priority = 0; for (int i = 0; i < 4; i++) if (atoms[i] != null) Modified: trunk/Jmol/src/org/jmol/viewer/Jmol.properties =================================================================== --- trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2017-07-24 02:43:49 UTC (rev 21677) +++ trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2017-07-26 01:53:43 UTC (rev 21678) @@ -58,6 +58,7 @@ Jmol.___JmolVersion="14.20.4" +bug fix: CIPChirality fix for BH64_075; adding Rule 3b. bug fix: CIPChirality new Rule 6 covers all special spiro cases and C3-symmetry bug fix: SMILES generator fails for [2H]/C=C/[1H] bug fix: write SDF fails for 1H. (Must use M ISO.) 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