Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: CanonizerUtil with NOSTEREO yielded to a wrong result #165

Merged
merged 1 commit into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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