Skip to content

Commit

Permalink
fix: CanonizerUtil with NOSTEREO yielded to a wrong result (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
lpatiny committed Aug 14, 2023
1 parent 5a1f27f commit 840e210
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 160 deletions.
62 changes: 62 additions & 0 deletions __tests__/canonizer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';

const OCL = require('../core');

test('canonizer chiral', () => {
const molecule = OCL.Molecule.fromSmiles('C[C@H](Cl)CC');
const idCode = molecule.getIDCode();

expect(idCode).toBe('gJPHADILuTb@');
expect(OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NORMAL)).toBe(
idCode,
);

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NOSTEREO),
).toBe('gJPHADILuP@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.BACKBONE),
).toBe('gJPHADILuP@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.TAUTOMER),
).toBe('gJPHADILuTb@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NOSTEREO_TAUTOMER),
).toBe('gJPHADILuP@');

molecule.stripStereoInformation();
expect(molecule.getIDCode()).toBe('gJPHADILuP@');
});

test('canonizer tautomer', () => {
const molecule = OCL.Molecule.fromSmiles('CC=C(O)CC');

const idCode = molecule.getIDCode();

expect(idCode).toBe('gGQ@@drsT@@');
expect(OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NORMAL)).toBe(
idCode,
);

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NOSTEREO),
).toBe('gGQ@@drsT@@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.BACKBONE),
).toBe('gGQ@@druT@@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.TAUTOMER),
).toBe('gGQ@@druTAkabXVOtfBicxX~F@');

expect(
OCL.CanonizerUtil.getIDCode(molecule, OCL.CanonizerUtil.NOSTEREO_TAUTOMER),
).toBe('gGQ@@druTAkabXVOtfBicxX~F@');

molecule.stripStereoInformation();
expect(molecule.getIDCode()).toBe('gGQ@@drsT@@');
});
5 changes: 0 additions & 5 deletions scripts/openchemlib/classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,6 @@ function changeBaseConformer(code) {
'mFrequency[bondIndex] = rotatableBond[bondIndex].getDefaultFrequencies().clone();',
'short[] frequencies = rotatableBond[bondIndex].getDefaultFrequencies();\n mFrequency[bondIndex] = Arrays.copyOf(frequencies, frequencies.length);',
);
code = replaceChecked(
code,
'\n mTorsion[bondIndex] = rotatableBond[bondIndex].getDefaultTorsions().clone();',
'',
);
return code;
}

Expand Down
35 changes: 35 additions & 0 deletions src/com/actelion/research/gwt/chemlib/com/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// to run this code: bash buildOpenChemLib && java -classpath build com.Test

package com;

import java.io.IOException;
import java.util.TreeMap;

import com.actelion.research.chem.Canonizer;
import com.actelion.research.chem.CanonizerUtil;
import com.actelion.research.chem.SmilesParser;
import com.actelion.research.chem.StereoMolecule;
import com.actelion.research.chem.contrib.HydrogenHandler;
import com.actelion.research.chem.contrib.DiastereotopicAtomID;
import com.actelion.research.chem.io.SDFileParser;
import java.io.File;


public class Test {

public static void main(String[] args) throws Exception {

SmilesParser sp = new SmilesParser();
StereoMolecule mol = new StereoMolecule();
sp.parse(mol, "CC");

System.out.println(mol.getIDCode());
mol.stripStereoInformation();
System.out.println(mol.getIDCode());
System.out.println(CanonizerUtil.getIDCode(mol, CanonizerUtil.IDCODE_TYPE.NORMAL, false));
System.out.println(CanonizerUtil.getIDCode(mol, CanonizerUtil.IDCODE_TYPE.NOSTEREO, false));
System.out
.println(CanonizerUtil.getIDCode(mol, CanonizerUtil.IDCODE_TYPE.NOSTEREO_TAUTOMER, false));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,141 +284,10 @@ private void canFindNitrogenQualifyingForParity() {
continue;
}

if (mMol.getAtomRingBondCount(atom) != 3)
continue;

// For any neutral nitrogen with three ring bonds
// we find the smallest ring. If this is not larger than 7 members
// then we find that connBond, which does not belong to that ring.
// The we find the bridge size (atom count) to where it touches the smallest ring.
// We also find the path length from the touch point on the smallest ring back
// to the nitrogen atom.
int smallRingSize = mMol.getAtomRingSize(atom);
if (smallRingSize > 7)
continue;

RingCollection ringSet = mMol.getRingSet();
int smallRingNo = 0;
while (smallRingNo<ringSet.getSize()) {
if (ringSet.getRingSize(smallRingNo) == smallRingSize
&& ringSet.isAtomMember(smallRingNo, atom))
break;

smallRingNo++;
}

if (smallRingNo >= RingCollection.MAX_SMALL_RING_COUNT
&& smallRingNo == ringSet.getSize())
continue; // very rare case, but found with wrongly highly bridged CSD entry JORFAZ

int firstBridgeAtom = -1;
int firstBridgeBond = -1;
for (int i=0; i<3; i++) {
int connBond = mMol.getConnBond(atom, i);
if (!ringSet.isBondMember(smallRingNo, connBond)) {
firstBridgeAtom = mMol.getConnAtom(atom, i);
firstBridgeBond = connBond;
break;
}
}

boolean[] neglectBond = new boolean[mMol.getBonds()];
neglectBond[firstBridgeBond] = true;
int[] pathAtom = new int[11];
int pathLength = mMol.getPath(pathAtom, firstBridgeAtom, atom, 10, neglectBond);
if (pathLength == -1)
continue;

int bridgeAtomCount = 1;
while (!ringSet.isAtomMember(smallRingNo, pathAtom[bridgeAtomCount]))
bridgeAtomCount++;

int bondCountToBridgeHead = pathLength - bridgeAtomCount;

int bridgeHead = pathAtom[bridgeAtomCount];

if (smallRingSize == 6 && bondCountToBridgeHead == 2 && bridgeAtomCount == 3) {
if (mMol.getAtomRingBondCount(pathAtom[1]) >= 3) {
boolean isAdamantane = false;
int[] ringAtom = ringSet.getRingAtoms(smallRingNo);
for (int i=0; i<6; i++) {
if (atom == ringAtom[i]) {
int potentialOtherBridgeHeadIndex = ringSet.validateMemberIndex(smallRingNo,
(bridgeHead == ringAtom[ringSet.validateMemberIndex(smallRingNo, i+2)]) ? i-2 : i+2);
int potentialOtherBridgeHead = ringAtom[potentialOtherBridgeHeadIndex];
if (mMol.getAtomRingBondCount(potentialOtherBridgeHead) >= 3
&& mMol.getPathLength(pathAtom[1], potentialOtherBridgeHead, 2, null) == 2)
isAdamantane = true;
break;
}
}
if (isAdamantane) {
mNitrogenQualifiesForParity[atom] = true;
continue;
}
}
}

boolean bridgeHeadIsFlat = (mMol.getAtomPi(bridgeHead) == 1
|| mMol.isAromaticAtom(bridgeHead)
|| mMol.isFlatNitrogen(bridgeHead));
boolean bridgeHeadMayInvert = !bridgeHeadIsFlat
&& mMol.getAtomicNo(bridgeHead) == 7
&& mMol.getAtomCharge(bridgeHead) != 1;

if (bondCountToBridgeHead == 1) {
if (!bridgeHeadIsFlat && !bridgeHeadMayInvert && smallRingSize <= 4 && bridgeAtomCount <= 3)
mNitrogenQualifiesForParity[atom] = true;
if (mMol.isPyramidalBridgeHead(atom)) {
mNitrogenQualifiesForParity[atom] = true;
continue;
}

switch (smallRingSize) {
// case 3 is fully handled
case 4: // must be bondCountToBridgeHead == 2
if (!bridgeHeadIsFlat && !bridgeHeadMayInvert) {
if (bridgeAtomCount <= 4)
mNitrogenQualifiesForParity[atom] = true;
}
break;
case 5: // must be bondCountToBridgeHead == 2
if (bridgeHeadMayInvert) {
if (bridgeAtomCount <= 3)
mNitrogenQualifiesForParity[atom] = true;
}
else if (!bridgeHeadIsFlat) {
if (bridgeAtomCount <= 4)
mNitrogenQualifiesForParity[atom] = true;
}
break;
case 6:
if (bondCountToBridgeHead == 2) {
if (bridgeHeadIsFlat) {
if (bridgeAtomCount <= 4)
mNitrogenQualifiesForParity[atom] = true;
}
else if (!bridgeHeadMayInvert) {
if (bridgeAtomCount <= 3)
mNitrogenQualifiesForParity[atom] = true;
}
}
else if (bondCountToBridgeHead == 3) {
if (bridgeHeadIsFlat) {
if (bridgeAtomCount <= 6)
mNitrogenQualifiesForParity[atom] = true;
}
else {
if (bridgeAtomCount <= 4)
mNitrogenQualifiesForParity[atom] = true;
}
}
break;
case 7:
if (bondCountToBridgeHead == 3) {
if (bridgeAtomCount <= 3)
mNitrogenQualifiesForParity[atom] = true;
}
break;
}
}
}
}
Expand Down
Loading

0 comments on commit 840e210

Please sign in to comment.