/// <summary>
        /// Adjust the configuration of the <paramref name="dbs"/> element (if required).
        /// </summary>
        /// <param name="dbs">double-bond stereochemistry element</param>
        private void Adjust(IDoubleBondStereochemistry dbs)
        {
            var db    = dbs.StereoBond;
            var bonds = dbs.Bonds;

            var left  = db.Begin;
            var right = db.End;

            var p = Parity(dbs.Configure);
            var q = Parity(GetAtoms(left, bonds[0].GetOther(left), right))
                    * Parity(GetAtoms(right, bonds[1].GetOther(right), left));

            // configuration is unspecified? then we add an unspecified bond.
            // note: IDoubleBondStereochemistry doesn't indicate this yet
            if (p == 0)
            {
                foreach (var bond in container.GetConnectedBonds(left))
                {
                    bond.Stereo = BondStereo.None;
                }
                foreach (var bond in container.GetConnectedBonds(right))
                {
                    bond.Stereo = BondStereo.None;
                }
                bonds[0].Stereo = BondStereo.UpOrDown;
                return;
            }

            // configuration is already correct
            if (p == q)
            {
                return;
            }

            Arrays.Fill(visited, false);
            visited[atomToIndex[left]] = true;

            if (ringSearch.Cyclic(atomToIndex[left], atomToIndex[right]))
            {
                db.Stereo = BondStereo.EOrZ;
                return;
            }

            foreach (var w in graph[atomToIndex[right]])
            {
                if (!visited[w])
                {
                    Reflect(w, db);
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Find and mark all cyclic atoms and bonds in the provided molecule. This optimised version
        /// allows the caller to optionally provided indexed fast access structure which would otherwise
        /// be created.
        /// </summary>
        /// <param name="mol">molecule</param>
        /// <param name="adjList"></param>
        /// <param name="bondMap"></param>
        /// <returns>Number of rings found (circuit rank)</returns>
        /// <seealso cref="IMolecularEntity.IsInRing"/>
        /// <seealso href="https://en.wikipedia.org/wiki/Circuit_rank">Circuit Rank</seealso>
        public static int MarkRingAtomsAndBonds(IAtomContainer mol, int[][] adjList, EdgeToBondMap bondMap)
        {
            var ringSearch = new RingSearch(mol, adjList);

            for (int v = 0; v < mol.Atoms.Count; v++)
            {
                mol.Atoms[v].IsInRing = false;
                foreach (var w in adjList[v])
                {
                    // note we only mark the bond on second visit (first v < w) and
                    // clear flag on first visit (or if non-cyclic)
                    if (v > w && ringSearch.Cyclic(v, w))
                    {
                        bondMap[v, w].IsInRing = true;
                        mol.Atoms[v].IsInRing  = true;
                        mol.Atoms[w].IsInRing  = true;
                    }
                    else
                    {
                        bondMap[v, w].IsInRing = false;
                    }
                }
            }
            return(ringSearch.NumRings);
        }
Esempio n. 3
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            int n         = container.Atoms.Count;
            var electrons = new int[n];
            var piBonds   = new int[n];

            // count number of cyclic pi bonds
            foreach (var bond in container.Bonds)
            {
                int u = container.Atoms.IndexOf(bond.Begin);
                int v = container.Atoms.IndexOf(bond.End);

                if (bond.Order == BondOrder.Double && ringSearch.Cyclic(u, v))
                {
                    piBonds[u]++;
                    piBonds[v]++;
                }
            }

            // any atom which is adjacent to one (and only one) cyclic
            // pi bond contributes 1 electron
            for (int i = 0; i < n; i++)
            {
                electrons[i] = piBonds[i] == 1 ? 1 : -1;
            }

            return(electrons);
        }
Esempio n. 4
0
        public static bool IsArene(this IAtomContainer mol)
        {
            if (mol == null)
            {
                return(false);
            }

            var ringSearch = new RingSearch(mol);

            //this is null for isolated and fused rings
            var cyclic = ringSearch.Cyclic();

            if (cyclic == null)
            {
                return(false);
            }

            for (var i = 1; i < mol.Bonds.Count; i++)
            {
                var p0 = mol.Bonds[i - 1].Order == BondOrder.Single ^ mol.Bonds[i].Order == BondOrder.Single;
                var p1 = mol.Bonds[i - 1].Order == BondOrder.Double ^ mol.Bonds[i].Order == BondOrder.Double;
                if (p0 && p1)
                {
                    continue;
                }

                return(false);
            }

            return(true);
        }
Esempio n. 5
0
        public override ISet <int> Find(long[] invariants, IAtomContainer container, int[][] graph)
        {
            int n = invariants.Length;

            // find cyclic vertices using DFS
            RingSearch ringSearch = new RingSearch(container, graph);

            // ordered map of the set of vertices for each value
            var equivalent = new SortedDictionary <long, ISet <int> >();

            // divide the invariants into equivalent indexed and ordered sets
            for (int i = 0; i < invariants.Length; i++)
            {
                long invariant = invariants[i];
                if (!equivalent.TryGetValue(invariant, out ISet <int> set))
                {
                    if (ringSearch.Cyclic(i))
                    {
                        set = new HashSet <int>
                        {
                            i
                        };
                        equivalent[invariant] = set;
                    }
                }
                else
                {
                    set.Add(i);
                }
            }

            // find the smallest set of equivalent cyclic vertices
            int        minSize = int.MaxValue;
            ISet <int> min     = new SortedSet <int>();

            foreach (var e in equivalent)
            {
                ISet <int> vertices = e.Value;
                if (vertices.Count < minSize && vertices.Count > 1)
                {
                    min     = vertices;
                    minSize = vertices.Count;
                }
                else if (vertices.Count == minSize)
                {
                    foreach (var vertice in vertices)
                    {
                        min.Add(vertice);
                    }
                }
            }

            return(min);
        }
Esempio n. 6
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            var nAtoms    = container.Atoms.Count;
            var electrons = new int[nAtoms];

            Arrays.Fill(electrons, -1);

            var indexMap = new Dictionary <IAtom, int>();

            for (int i = 0; i < nAtoms; i++)
            {
                var atom = container.Atoms[i];
                indexMap.Add(atom, i);

                // acyclic atom skipped
                if (!ringSearch.Cyclic(i))
                {
                    continue;
                }

                var hyb = atom.Hybridization;

                CheckNotNull(atom.AtomTypeName, "atom has unset atom type");

                // atom has been assigned an atom type but we don't know the hybrid state,
                // typically for atom type 'X' (unknown)
                switch (hyb)
                {
                case Hybridization.SP2:
                case Hybridization.Planar3:
                    electrons[i] = ElectronsForAtomType(atom);
                    break;

                case Hybridization.SP3:
                    electrons[i] = LonePairCount(atom) > 0 ? 2 : -1;
                    break;
                }
            }

            // exocyclic double bonds are allowed no further processing
            if (exocyclic)
            {
                return(electrons);
            }

            // check for exocyclic double/triple bonds and disallow their contribution
            foreach (var bond in container.Bonds)
            {
                if (bond.Order == BondOrder.Double || bond.Order == BondOrder.Triple)
                {
                    var a1 = bond.Begin;
                    var a2 = bond.End;

                    var a1Type = a1.AtomTypeName;
                    var a2Type = a2.AtomTypeName;

                    var u = indexMap[a1];
                    var v = indexMap[a2];

                    if (!ringSearch.Cyclic(u, v))
                    {
                        // XXX: single exception - we could make this more general but
                        // for now this mirrors the existing behavior
                        switch (a1Type)
                        {
                        case "N.sp2.3":
                            switch (a2Type)
                            {
                            case "O.sp2":
                                continue;
                            }
                            break;

                        case "O.sp2":
                            switch (a2Type)
                            {
                            case "N.sp2.3":
                                continue;
                            }
                            break;
                        }

                        electrons[u] = electrons[v] = -1;
                    }
                }
            }

            return(electrons);
        }
Esempio n. 7
0
        /// <summary>
        /// Recognise the tetrahedral stereochemistry in the provided structure.
        /// </summary>
        /// <param name="projections">allowed projection types</param>
        /// <returns>zero of more stereo elements</returns>
        public IEnumerable <IStereoElement <IChemObject, IChemObject> > Recognise(ICollection <Projection> projections)
        {
            if (!projections.Contains(Projection.Fischer))
            {
                return(Array.Empty <IStereoElement <IChemObject, IChemObject> >());
            }

            // build atom index and only recognize 2D depictions
            var atomToIndex = new Dictionary <IAtom, int>();

            foreach (var atom in container.Atoms)
            {
                if (atom.Point2D == null)
                {
                    return(Array.Empty <IStereoElement <IChemObject, IChemObject> >());
                }
                atomToIndex.Add(atom, atomToIndex.Count);
            }

            var ringSearch = new RingSearch(container, graph);

            var elements = new List <IStereoElement <IChemObject, IChemObject> >(5);

            for (int v = 0; v < container.Atoms.Count; v++)
            {
                var focus = container.Atoms[v];
                if (!focus.AtomicNumber.Equals(AtomicNumbers.Carbon))
                {
                    continue;
                }
                if (ringSearch.Cyclic(v))
                {
                    continue;
                }
                if (stereocenters.ElementType(v) != CoordinateType.Tetracoordinate)
                {
                    continue;
                }
                if (!stereocenters.IsStereocenter(v))
                {
                    continue;
                }

                var element = NewTetrahedralCenter(focus, Neighbors(v, graph, bonds));

                if (element == null)
                {
                    continue;
                }

                // east/west bonds must be to terminal atoms
                var east = element.Ligands[EAST];
                var west = element.Ligands[WEST];

                if (east != focus && !IsTerminal(east, atomToIndex))
                {
                    continue;
                }
                if (west != focus && !IsTerminal(west, atomToIndex))
                {
                    continue;
                }

                elements.Add(element);
            }

            return(elements);
        }
Esempio n. 8
0
        /// <inheritdoc/>
        public override IReadOnlyList <int> Contribution(IAtomContainer container, RingSearch ringSearch)
        {
            int n = container.Atoms.Count;

            // we compute values we need for all atoms and then make the decisions
            // - this avoids costly operations such as looking up connected
            // bonds on each atom at the cost of memory
            var degree          = new int[n];
            var bondOrderSum    = new int[n];
            var nCyclicPiBonds  = new int[n];
            var exocyclicPiBond = new int[n];
            var electrons       = new int[n];

            Arrays.Fill(exocyclicPiBond, -1);

            // index atoms and set the degree to the number of implicit hydrogens
            var atomIndex = new Dictionary <IAtom, int>(n);

            for (int i = 0; i < n; i++)
            {
                var a = container.Atoms[i];
                atomIndex.Add(a, i);
                degree[i] = CheckNotNull(a.ImplicitHydrogenCount,
                                         "Aromaticity model requires implicit hydrogen count is set.");
            }

            // for each bond we increase the degree count and check for cyclic and
            // exocyclic pi bonds. if there is a cyclic pi bond the atom is marked.
            // if there is an exocyclic pi bond we store the adjacent atom for
            // lookup later.
            foreach (var bond in container.Bonds)
            {
                var u = atomIndex[bond.Begin];
                var v = atomIndex[bond.End];
                degree[u]++;
                degree[v]++;

                var order = CheckNotNull(bond.Order, "Aromaticity model requires that bond orders must be set");

                if (order == BondOrder.Unset)
                {
                    throw new ArgumentException("Aromaticity model requires that bond orders must be set");
                }
                else if (order == BondOrder.Double)
                {
                    if (ringSearch.Cyclic(u, v))
                    {
                        nCyclicPiBonds[u]++;
                        nCyclicPiBonds[v]++;
                    }
                    else
                    {
                        exocyclicPiBond[u] = v;
                        exocyclicPiBond[v] = u;
                    }
                    // note - fall through
                }

                if (order == BondOrder.Single ||
                    order == BondOrder.Double ||
                    order == BondOrder.Triple ||
                    order == BondOrder.Quadruple)
                {
                    bondOrderSum[u] += order.Numeric();
                    bondOrderSum[v] += order.Numeric();
                }
            }

            // now make a decision on how many electrons each atom contributes
            for (int i = 0; i < n; i++)
            {
                var element = Element(container.Atoms[i]);
                var charge  = Charge(container.Atoms[i]);

                // abnormal valence, usually indicated a radical. these cause problems
                // with kekulisations
                var bondedValence = bondOrderSum[i] + container.Atoms[i].ImplicitHydrogenCount.Value;
                if (!Normal(element, charge, bondedValence))
                {
                    electrons[i] = -1;
                }

                // non-aromatic element, acyclic atoms, atoms with more than three
                // neighbors and atoms with more than 1 cyclic pi bond are not
                // considered
                else if (!AromaticElement(element) || !ringSearch.Cyclic(i) || degree[i] > 3 || nCyclicPiBonds[i] > 1)
                {
                    electrons[i] = -1;
                }

                // exocyclic bond contributes 0 or 1 electrons depending on
                // preset electronegativity - check the exocyclicContribution method
                else if (exocyclicPiBond[i] >= 0)
                {
                    electrons[i] = ExocyclicContribution(element, Element(container.Atoms[exocyclicPiBond[i]]), charge,
                                                         nCyclicPiBonds[i]);
                }

                // any atom (except arsenic) with one cyclic pi bond contributes a
                // single electron
                else if (nCyclicPiBonds[i] == 1)
                {
                    electrons[i] = element == ARSENIC ? -1 : 1;
                }

                // a anion with a lone pair contributes 2 electrons - simplification
                // here is we count the number free valence electrons but also
                // check if the bonded valence is okay (i.e. not a radical)
                else if (charge <= 0 && charge > -3)
                {
                    if (Valence(element, charge) - bondOrderSum[i] >= 2)
                    {
                        electrons[i] = 2;
                    }
                    else
                    {
                        electrons[i] = -1;
                    }
                }

                else
                {
                    // cation with no double bonds - single exception?
                    if (element == Carbon && charge > 0)
                    {
                        electrons[i] = 0;
                    }
                    else
                    {
                        electrons[i] = -1;
                    }
                }
            }

            return(electrons);
        }