Search in sources :

Example 1 with InchiBondStereo

use of io.github.dan2097.jnainchi.InchiBondStereo 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);
}
Also used : HashMap(java.util.HashMap) IBond(org.openscience.cdk.interfaces.IBond) InchiStereo(io.github.dan2097.jnainchi.InchiStereo) InchiBond(io.github.dan2097.jnainchi.InchiBond) InchiStereoParity(io.github.dan2097.jnainchi.InchiStereoParity) Point3d(javax.vecmath.Point3d) InchiAtom(io.github.dan2097.jnainchi.InchiAtom) IAtom(org.openscience.cdk.interfaces.IAtom) Order(org.openscience.cdk.interfaces.IBond.Order) CDKException(org.openscience.cdk.exception.CDKException) IDoubleBondStereochemistry(org.openscience.cdk.interfaces.IDoubleBondStereochemistry) Stereo(org.openscience.cdk.interfaces.ITetrahedralChirality.Stereo) InchiBondStereo(io.github.dan2097.jnainchi.InchiBondStereo) InchiStereo(io.github.dan2097.jnainchi.InchiStereo) Point2d(javax.vecmath.Point2d) InchiBondType(io.github.dan2097.jnainchi.InchiBondType) InchiBondStereo(io.github.dan2097.jnainchi.InchiBondStereo) ITetrahedralChirality(org.openscience.cdk.interfaces.ITetrahedralChirality) ExtendedTetrahedral(org.openscience.cdk.stereo.ExtendedTetrahedral) IStereoElement(org.openscience.cdk.interfaces.IStereoElement)

Example 2 with InchiBondStereo

use of io.github.dan2097.jnainchi.InchiBondStereo 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));
            }
        }
    }
}
Also used : IAtomContainer(org.openscience.cdk.interfaces.IAtomContainer) HashMap(java.util.HashMap) IBond(org.openscience.cdk.interfaces.IBond) InchiStereo(io.github.dan2097.jnainchi.InchiStereo) InchiBond(io.github.dan2097.jnainchi.InchiBond) InchiInput(io.github.dan2097.jnainchi.InchiInput) DoubleBondStereochemistry(org.openscience.cdk.stereo.DoubleBondStereochemistry) InchiAtom(io.github.dan2097.jnainchi.InchiAtom) IAtom(org.openscience.cdk.interfaces.IAtom) CDKException(org.openscience.cdk.exception.CDKException) IOException(java.io.IOException) InchiBondType(io.github.dan2097.jnainchi.InchiBondType) InchiBondStereo(io.github.dan2097.jnainchi.InchiBondStereo) ITetrahedralChirality(org.openscience.cdk.interfaces.ITetrahedralChirality) ExtendedTetrahedral(org.openscience.cdk.stereo.ExtendedTetrahedral) IStereoElement(org.openscience.cdk.interfaces.IStereoElement) ExtendedCisTrans(org.openscience.cdk.stereo.ExtendedCisTrans)

Aggregations

InchiAtom (io.github.dan2097.jnainchi.InchiAtom)2 InchiBond (io.github.dan2097.jnainchi.InchiBond)2 InchiBondStereo (io.github.dan2097.jnainchi.InchiBondStereo)2 InchiBondType (io.github.dan2097.jnainchi.InchiBondType)2 InchiStereo (io.github.dan2097.jnainchi.InchiStereo)2 HashMap (java.util.HashMap)2 CDKException (org.openscience.cdk.exception.CDKException)2 IAtom (org.openscience.cdk.interfaces.IAtom)2 IBond (org.openscience.cdk.interfaces.IBond)2 IStereoElement (org.openscience.cdk.interfaces.IStereoElement)2 ITetrahedralChirality (org.openscience.cdk.interfaces.ITetrahedralChirality)2 ExtendedTetrahedral (org.openscience.cdk.stereo.ExtendedTetrahedral)2 InchiInput (io.github.dan2097.jnainchi.InchiInput)1 InchiStereoParity (io.github.dan2097.jnainchi.InchiStereoParity)1 IOException (java.io.IOException)1 Point2d (javax.vecmath.Point2d)1 Point3d (javax.vecmath.Point3d)1 IAtomContainer (org.openscience.cdk.interfaces.IAtomContainer)1 Order (org.openscience.cdk.interfaces.IBond.Order)1 IDoubleBondStereochemistry (org.openscience.cdk.interfaces.IDoubleBondStereochemistry)1