use of io.github.dan2097.jnainchi.InchiBond in project cdk by cdk.
the class InChIGenerator method generateInchiFromCDKAtomContainer.
/**
* <p>Reads atoms, bonds etc from atom container and converts to format
* InChI library requires, then places call for the library to generate
* the InChI.
*
* @param atomContainer AtomContainer to generate InChI for.
* @param ignore Ignore aromatic bonds
* @throws CDKException
*/
private void generateInchiFromCDKAtomContainer(IAtomContainer atomContainer, boolean ignore) throws CDKException {
this.atomContainer = atomContainer;
Iterator<IAtom> atoms = atomContainer.atoms().iterator();
// Check for 3d coordinates
boolean all3d = true;
boolean all2d = true;
while (atoms.hasNext()) {
IAtom atom = atoms.next();
if (atom.getPoint3d() == null) {
all3d = false;
}
if (atom.getPoint2d() == null) {
all2d = false;
}
}
Map<IAtom, InchiAtom> atomMap = new HashMap<>();
atoms = atomContainer.atoms().iterator();
while (atoms.hasNext()) {
IAtom atom = atoms.next();
// Get coordinates
// Use 3d if possible, otherwise 2d or none
double x, y, z;
if (all3d) {
Point3d p = atom.getPoint3d();
x = p.x;
y = p.y;
z = p.z;
} else if (all2d) {
Point2d p = atom.getPoint2d();
x = p.x;
y = p.y;
z = 0.0;
} else {
x = 0.0;
y = 0.0;
z = 0.0;
}
// Chemical element symbol
String el = atom.getSymbol();
// Generate InChI atom
InchiAtom iatom = new InchiAtom(el, x, y, z);
input.addAtom(iatom);
atomMap.put(atom, iatom);
// Check if charged
int charge = atom.getFormalCharge();
if (charge != 0) {
iatom.setCharge(charge);
}
// Check whether isotopic
Integer isotopeNumber = atom.getMassNumber();
if (isotopeNumber != null) {
iatom.setIsotopicMass(isotopeNumber);
}
// Check for implicit hydrogens
// atom.getHydrogenCount() returns number of implict hydrogens, not
// total number
// Ref: Posting to cdk-devel list by Egon Willighagen 2005-09-17
Integer implicitH = atom.getImplicitHydrogenCount();
// set implicit hydrogen count, -1 tells the inchi to determine it
iatom.setImplicitHydrogen(implicitH != null ? implicitH : -1);
// Check if radical
int count = atomContainer.getConnectedSingleElectronsCount(atom);
if (count == 1) {
iatom.setRadical(InchiRadical.DOUBLET);
} else if (count == 2) {
Enum spin = atom.getProperty(CDKConstants.SPIN_MULTIPLICITY);
if (spin != null) {
// a little brittle
if (spin.name().equals("DivalentSinglet"))
iatom.setRadical(InchiRadical.SINGLET);
else
iatom.setRadical(InchiRadical.TRIPLET);
} else {
iatom.setRadical(InchiRadical.TRIPLET);
}
} else if (count != 0) {
throw new CDKException("Unrecognised radical type");
}
}
// Process bonds
for (IBond bond : atomContainer.bonds()) {
// Assumes 2 centre bond
InchiAtom at0 = atomMap.get(bond.getBegin());
InchiAtom at1 = atomMap.get(bond.getEnd());
// Get bond order
InchiBondType order;
Order bo = bond.getOrder();
if (!ignore && bond.isAromatic()) {
order = InchiBondType.ALTERN;
} else if (bo == Order.SINGLE) {
order = InchiBondType.SINGLE;
} else if (bo == Order.DOUBLE) {
order = InchiBondType.DOUBLE;
} else if (bo == Order.TRIPLE) {
order = InchiBondType.TRIPLE;
} else {
throw new CDKException("Failed to generate InChI: Unsupported bond type");
}
// Create InChI bond
// Check for bond stereo definitions
IBond.Stereo display = bond.getStereo();
final InchiBondStereo iDisplay;
switch(display) {
case UP:
iDisplay = InchiBondStereo.SINGLE_1UP;
break;
case UP_INVERTED:
iDisplay = InchiBondStereo.SINGLE_2UP;
break;
case DOWN:
iDisplay = InchiBondStereo.SINGLE_1DOWN;
break;
case DOWN_INVERTED:
iDisplay = InchiBondStereo.SINGLE_2DOWN;
break;
case UP_OR_DOWN:
iDisplay = InchiBondStereo.SINGLE_1EITHER;
break;
case UP_OR_DOWN_INVERTED:
iDisplay = InchiBondStereo.SINGLE_2EITHER;
break;
case E_OR_Z:
iDisplay = InchiBondStereo.DOUBLE_EITHER;
break;
default:
iDisplay = InchiBondStereo.NONE;
break;
}
/*
TODO: old code would set single/double either if no display?
*/
InchiBond ibond = new InchiBond(at0, at1, order, iDisplay);
input.addBond(ibond);
}
// Process tetrahedral stereo elements
for (IStereoElement stereoElem : atomContainer.stereoElements()) {
if (stereoElem instanceof ITetrahedralChirality) {
ITetrahedralChirality chirality = (ITetrahedralChirality) stereoElem;
IAtom[] surroundingAtoms = chirality.getLigands();
Stereo stereoType = chirality.getStereo();
InchiAtom atC = atomMap.get(chirality.getChiralAtom());
InchiAtom at0 = atomMap.get(surroundingAtoms[0]);
InchiAtom at1 = atomMap.get(surroundingAtoms[1]);
InchiAtom at2 = atomMap.get(surroundingAtoms[2]);
InchiAtom at3 = atomMap.get(surroundingAtoms[3]);
InchiStereoParity p;
if (stereoType == Stereo.ANTI_CLOCKWISE) {
p = InchiStereoParity.ODD;
} else if (stereoType == Stereo.CLOCKWISE) {
p = InchiStereoParity.EVEN;
} else {
throw new CDKException("Unknown tetrahedral chirality");
}
InchiStereo jniStereo = InchiStereo.createTetrahedralStereo(atC, at0, at1, at2, at3, p);
input.addStereo(jniStereo);
} else if (stereoElem instanceof IDoubleBondStereochemistry) {
IDoubleBondStereochemistry dbStereo = (IDoubleBondStereochemistry) stereoElem;
IBond[] surroundingBonds = dbStereo.getBonds();
if (surroundingBonds[0] == null || surroundingBonds[1] == null)
throw new CDKException("Cannot generate an InChI with incomplete double bond info");
org.openscience.cdk.interfaces.IDoubleBondStereochemistry.Conformation stereoType = dbStereo.getStereo();
IBond stereoBond = dbStereo.getStereoBond();
InchiAtom at0;
InchiAtom at1;
InchiAtom at2;
InchiAtom at3;
// create a double bond stereochemistry
if (stereoBond.contains(surroundingBonds[0].getBegin())) {
// first atom is A
at1 = atomMap.get(surroundingBonds[0].getBegin());
at0 = atomMap.get(surroundingBonds[0].getEnd());
} else {
// first atom is X
at0 = atomMap.get(surroundingBonds[0].getBegin());
at1 = atomMap.get(surroundingBonds[0].getEnd());
}
if (stereoBond.contains(surroundingBonds[1].getBegin())) {
// first atom is B
at2 = atomMap.get(surroundingBonds[1].getBegin());
at3 = atomMap.get(surroundingBonds[1].getEnd());
} else {
// first atom is Y
at2 = atomMap.get(surroundingBonds[1].getEnd());
at3 = atomMap.get(surroundingBonds[1].getBegin());
}
InchiStereoParity p;
if (stereoType == org.openscience.cdk.interfaces.IDoubleBondStereochemistry.Conformation.TOGETHER) {
p = InchiStereoParity.ODD;
} else if (stereoType == org.openscience.cdk.interfaces.IDoubleBondStereochemistry.Conformation.OPPOSITE) {
p = InchiStereoParity.EVEN;
} else {
throw new CDKException("Unknown double bond stereochemistry");
}
input.addStereo(InchiStereo.createDoubleBondStereo(at0, at1, at2, at3, p));
} else if (stereoElem instanceof ExtendedTetrahedral) {
ExtendedTetrahedral extendedTetrahedral = (ExtendedTetrahedral) stereoElem;
Stereo winding = extendedTetrahedral.winding();
// The periphals (p<i>) and terminals (t<i>) are refering to
// the following atoms. The focus (f) is also shown.
//
// p0 p2
// \ /
// t0 = f = t1
// / \
// p1 p3
IAtom focus = extendedTetrahedral.getFocus();
IAtom[] terminals = extendedTetrahedral.findTerminalAtoms(atomContainer);
IAtom[] peripherals = extendedTetrahedral.peripherals();
// InChI only supports length 2
if (ExtendedTetrahedral.getLength(atomContainer, focus) > 2)
continue;
// InChI API is particualar about the input, each terminal atom
// needs to be present in the list of neighbors and they must
// be at index 1 and 2 (i.e. in the middle). This is true even
// of explict atoms. For the implicit atoms, the terminals may
// be in the peripherals allready and so we correct the winding
// and reposition as needed.
List<IBond> t0Bonds = onlySingleBonded(atomContainer.getConnectedBondsList(terminals[0]));
List<IBond> t1Bonds = onlySingleBonded(atomContainer.getConnectedBondsList(terminals[1]));
// with the terminal atom - the configuration does not change
if (t0Bonds.size() == 2) {
IAtom replace = t0Bonds.remove(0).getOther(terminals[0]);
for (int i = 0; i < peripherals.length; i++) if (replace == peripherals[i])
peripherals[i] = terminals[0];
}
if (t1Bonds.size() == 2) {
IAtom replace = t1Bonds.remove(0).getOther(terminals[1]);
for (int i = 0; i < peripherals.length; i++) if (replace == peripherals[i])
peripherals[i] = terminals[1];
}
// the neighbor attached to each terminal atom that we will
// define the configuration of
IAtom t0Neighbor = t0Bonds.get(0).getOther(terminals[0]);
IAtom t1Neighbor = t1Bonds.get(0).getOther(terminals[1]);
// everytime we exchange atoms the configuration inverts
for (int i = 0; i < peripherals.length; i++) {
if (i != 0 && t0Neighbor.equals(peripherals[i])) {
swap(peripherals, i, 0);
winding = winding.invert();
} else if (i != 1 && terminals[0].equals(peripherals[i])) {
swap(peripherals, i, 1);
winding = winding.invert();
} else if (i != 2 && terminals[1].equals(peripherals[i])) {
swap(peripherals, i, 2);
winding = winding.invert();
} else if (i != 3 && t1Neighbor.equals(peripherals[i])) {
swap(peripherals, i, 3);
winding = winding.invert();
}
}
InchiStereoParity parity;
if (winding == Stereo.ANTI_CLOCKWISE)
parity = InchiStereoParity.ODD;
else if (winding == Stereo.CLOCKWISE)
parity = InchiStereoParity.EVEN;
else
throw new CDKException("Unknown extended tetrahedral chirality");
input.addStereo(InchiStereo.createAllenalStereo(atomMap.get(focus), atomMap.get(peripherals[0]), atomMap.get(peripherals[1]), atomMap.get(peripherals[2]), atomMap.get(peripherals[3]), parity));
}
}
output = JnaInchi.toInchi(input, options);
}
use of io.github.dan2097.jnainchi.InchiBond in project cdk by cdk.
the class InChIToStructure method generateAtomContainerFromInchi.
/**
* Gets structure from InChI, and converts InChI library data structure
* into an IAtomContainer.
*
* @throws CDKException
*/
protected void generateAtomContainerFromInchi(IChemObjectBuilder builder) throws CDKException {
InchiInput input = output.getInchiInput();
// molecule = new AtomContainer();
molecule = builder.newInstance(IAtomContainer.class);
Map<InchiAtom, IAtom> inchiCdkAtomMap = new HashMap<>();
List<InchiAtom> atoms = input.getAtoms();
for (int i = 0; i < atoms.size(); i++) {
InchiAtom iAt = atoms.get(i);
IAtom cAt = builder.newInstance(IAtom.class);
inchiCdkAtomMap.put(iAt, cAt);
cAt.setID("a" + i);
cAt.setAtomicNumber(Elements.ofString(iAt.getElName()).number());
// Ignore coordinates - all zero - unless aux info was given... but
// the CDK doesn't have an API to provide that
// InChI does not have unset properties so we set charge,
// hydrogen count (implicit) and isotopic mass
cAt.setFormalCharge(iAt.getCharge());
cAt.setImplicitHydrogenCount(iAt.getImplicitHydrogen());
int isotopicMass = iAt.getIsotopicMass();
if (isotopicMass != 0) {
if (isotopicMass > ISOTOPIC_SHIFT_THRESHOLD) {
try {
int massNumber = Isotopes.getInstance().getMajorIsotope(cAt.getAtomicNumber()).getMassNumber();
cAt.setMassNumber(massNumber + (isotopicMass - ISOTOPIC_SHIFT_FLAG));
} catch (IOException e) {
throw new CDKException("Could not load Isotopes data", e);
}
} else {
cAt.setMassNumber(isotopicMass);
}
}
molecule.addAtom(cAt);
cAt = molecule.getAtom(molecule.getAtomCount() - 1);
addHydrogenIsotopes(builder, cAt, 2, iAt.getImplicitDeuterium());
addHydrogenIsotopes(builder, cAt, 3, iAt.getImplicitTritium());
}
List<InchiBond> bonds = input.getBonds();
for (InchiBond iBo : bonds) {
IBond cBo = builder.newInstance(IBond.class);
IAtom atO = inchiCdkAtomMap.get(iBo.getStart());
IAtom atT = inchiCdkAtomMap.get(iBo.getEnd());
cBo.setAtoms(new IAtom[] { atO, atT });
InchiBondType type = iBo.getType();
switch(type) {
case SINGLE:
cBo.setOrder(IBond.Order.SINGLE);
break;
case DOUBLE:
cBo.setOrder(IBond.Order.DOUBLE);
break;
case TRIPLE:
cBo.setOrder(IBond.Order.TRIPLE);
break;
case ALTERN:
cBo.setIsInRing(true);
break;
default:
throw new CDKException("Unknown bond type: " + type);
}
InchiBondStereo stereo = iBo.getStereo();
switch(stereo) {
case NONE:
cBo.setStereo(IBond.Stereo.NONE);
break;
case SINGLE_1DOWN:
cBo.setStereo(IBond.Stereo.DOWN);
break;
case SINGLE_1UP:
cBo.setStereo(IBond.Stereo.UP);
break;
case SINGLE_2DOWN:
cBo.setStereo(IBond.Stereo.DOWN_INVERTED);
break;
case SINGLE_2UP:
cBo.setStereo(IBond.Stereo.UP_INVERTED);
break;
case SINGLE_1EITHER:
cBo.setStereo(IBond.Stereo.UP_OR_DOWN);
break;
case SINGLE_2EITHER:
cBo.setStereo(IBond.Stereo.UP_OR_DOWN_INVERTED);
break;
}
molecule.addBond(cBo);
}
List<InchiStereo> stereos = input.getStereos();
for (InchiStereo stereo0d : stereos) {
if (stereo0d.getType() == InchiStereoType.Tetrahedral || stereo0d.getType() == InchiStereoType.Allene) {
InchiAtom central = stereo0d.getCentralAtom();
InchiAtom[] neighbours = stereo0d.getAtoms();
IAtom focus = inchiCdkAtomMap.get(central);
IAtom[] neighbors = new IAtom[] { inchiCdkAtomMap.get(neighbours[0]), inchiCdkAtomMap.get(neighbours[1]), inchiCdkAtomMap.get(neighbours[2]), inchiCdkAtomMap.get(neighbours[3]) };
ITetrahedralChirality.Stereo stereo;
// anti-clockwise
if (stereo0d.getParity() == InchiStereoParity.ODD) {
stereo = ITetrahedralChirality.Stereo.ANTI_CLOCKWISE;
} else if (stereo0d.getParity() == InchiStereoParity.EVEN) {
stereo = ITetrahedralChirality.Stereo.CLOCKWISE;
} else {
// CDK Only supports parities of + or -
continue;
}
IStereoElement stereoElement = null;
if (stereo0d.getType() == InchiStereoType.Tetrahedral) {
stereoElement = builder.newInstance(ITetrahedralChirality.class, focus, neighbors, stereo);
} else if (stereo0d.getType() == InchiStereoType.Allene) {
// The periphals (p<i>) and terminals (t<i>) are refering to
// the following atoms. The focus (f) is also shown.
//
// p0 p2
// \ /
// t0 = f = t1
// / \
// p1 p3
IAtom[] peripherals = neighbors;
IAtom[] terminals = ExtendedTetrahedral.findTerminalAtoms(molecule, focus);
// case
for (IAtom terminal : terminals) {
if (peripherals[1].equals(terminal)) {
peripherals[1] = findOtherSinglyBonded(molecule, terminal, peripherals[0]);
} else if (peripherals[2].equals(terminal)) {
peripherals[2] = findOtherSinglyBonded(molecule, terminal, peripherals[3]);
} else if (peripherals[0].equals(terminal)) {
peripherals[0] = findOtherSinglyBonded(molecule, terminal, peripherals[1]);
} else if (peripherals[3].equals(terminal)) {
peripherals[3] = findOtherSinglyBonded(molecule, terminal, peripherals[2]);
}
}
stereoElement = new ExtendedTetrahedral(focus, peripherals, stereo);
}
assert stereoElement != null;
molecule.addStereoElement(stereoElement);
} else if (stereo0d.getType() == InchiStereoType.DoubleBond) {
boolean extended = false;
InchiAtom[] neighbors = stereo0d.getAtoms();
// from JNI InChI doc
// neighbor[4] : {#X,#A,#B,#Y} in this order
// X central_atom : NO_ATOM
// \ X Y type : INCHI_StereoType_DoubleBond
// A == B \ /
// \ A == B
// Y
IAtom x = inchiCdkAtomMap.get(neighbors[0]);
IAtom a = inchiCdkAtomMap.get(neighbors[1]);
IAtom b = inchiCdkAtomMap.get(neighbors[2]);
IAtom y = inchiCdkAtomMap.get(neighbors[3]);
IBond stereoBond = molecule.getBond(a, b);
if (stereoBond == null) {
extended = true;
IBond tmp = null;
// A = C = C = B
stereoBond = ExtendedCisTrans.findCentralBond(molecule, a);
if (stereoBond == null)
// warn on invalid input?
continue;
IAtom[] ends = ExtendedCisTrans.findTerminalAtoms(molecule, stereoBond);
assert ends != null;
if (ends[0] != a)
flip(stereoBond);
} else {
if (!stereoBond.getBegin().equals(a))
flip(stereoBond);
}
int config = IStereoElement.TOGETHER;
if (stereo0d.getParity() == InchiStereoParity.EVEN)
config = IStereoElement.OPPOSITE;
if (extended) {
molecule.addStereoElement(new ExtendedCisTrans(stereoBond, new IBond[] { molecule.getBond(x, a), molecule.getBond(b, y) }, config));
} else {
molecule.addStereoElement(new DoubleBondStereochemistry(stereoBond, new IBond[] { molecule.getBond(x, a), molecule.getBond(b, y) }, config));
}
}
}
}
Aggregations