Exemple #1
0
        /// <summary>
        /// Add double-bond stereo configuration to the Beam GraphBuilder.
        /// </summary>
        /// <param name="dbs">stereo element specifying double-bond configuration</param>
        /// <param name="gb">the current graph builder</param>
        /// <param name="indices">atom indices</param>
        private static void AddGeometricConfiguration(IDoubleBondStereochemistry dbs, SmiFlavors flavour, GraphBuilder gb, Dictionary <IAtom, int> indices)
        {
            var db = dbs.StereoBond;
            var bs = dbs.Bonds;

            // don't try to set a configuration on aromatic bonds
            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.UseAromaticSymbols) && db.IsAromatic)
            {
                return;
            }

            var u = indices[db.Begin];
            var v = indices[db.End];

            // is bs[0] always connected to db.Atom(0)?
            var x = indices[bs[0].GetOther(db.Begin)];
            var y = indices[bs[1].GetOther(db.End)];

            if (dbs.Stereo == DoubleBondConformation.Together)
            {
                gb.Geometric(u, v).Together(x, y);
            }
            else
            {
                gb.Geometric(u, v).Opposite(x, y);
            }
        }
Exemple #2
0
        /// <summary>
        /// Convert an CDK <see cref="IAtom"/> to a Beam Atom. The symbol and implicit
        /// hydrogen count are not optional. If the symbol is not supported by the
        /// SMILES notation (e.g. 'R1') the element will automatically default to
        /// Unknown ('*').
        /// </summary>
        /// <param name="a">cdk Atom instance</param>
        /// <returns>a Beam atom</returns>
        /// <exception cref="NullReferenceException">the atom had an undefined symbol or implicit hydrogen count</exception>
        static Beam.IAtom ToBeamAtom(IAtom a, SmiFlavors flavour)
        {
            var    aromatic = SmiFlavorTool.IsSet(flavour, SmiFlavors.UseAromaticSymbols) && a.IsAromatic;
            var    charge   = a.FormalCharge;
            string symbol   = CheckNotNull(a.Symbol, "An atom had an undefined symbol");

            var element = Beam.Element.OfSymbol(symbol);

            if (element == null)
            {
                element = Beam.Element.Unknown;
            }

            var ab = aromatic ? AtomBuilder.Aromatic(element) : AtomBuilder.Aliphatic(element);

            // CDK leaves nulls on pseudo atoms - we need to check this special case
            var hCount = a.ImplicitHydrogenCount;

            if (element == Beam.Element.Unknown)
            {
                ab.NumOfHydrogens(hCount ?? 0);
            }
            else
            {
                ab.NumOfHydrogens(CheckNotNull(hCount, "One or more atoms had an undefined number of implicit hydrogens"));
            }

            if (charge.HasValue)
            {
                ab.Charge(charge.Value);
            }

            // use the mass number to specify isotope?
            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.AtomicMass | SmiFlavors.AtomicMassStrict))
            {
                var massNumber = a.MassNumber;
                if (massNumber != null)
                {
                    ab.Isotope(massNumber.Value);
                }
            }

            var atomClass = a.GetProperty <int?>(CDKPropertyName.AtomAtomMapping);

            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.AtomAtomMap) && atomClass != null)
            {
                ab.AtomClass(atomClass.Value);
            }

            return(ab.Build());
        }
Exemple #3
0
        /// <summary>
        /// Given a molecule (possibly disconnected) compute the labels which
        /// would order the atoms by increasing canonical labelling. If the SMILES
        /// are isomeric (i.e. stereo and isotope specific) the InChI numbers are
        /// used. These numbers are loaded via reflection and the 'cdk-inchi' module
        /// should be present on the path.
        /// </summary>
        /// <param name="molecule">the molecule to</param>
        /// <returns>the permutation</returns>
        /// <seealso cref="Canon"/>
        private static int[] Labels(SmiFlavors flavour, IAtomContainer molecule)
        {
            // FIXME: use SmiOpt.InChiLabelling
            var labels = SmiFlavorTool.IsSet(flavour, SmiFlavors.Isomeric)
                ? InChINumbers(molecule)
                : Canon.Label(molecule, GraphUtil.ToAdjList(molecule), CreateComparator(molecule, flavour));
            var cpy = new int[labels.Length];

            for (int i = 0; i < labels.Length; i++)
            {
                cpy[i] = (int)labels[i] - 1;
            }
            return(cpy);
        }
Exemple #4
0
        /// <summary>
        /// Convert a CDK <see cref="IAtomContainer"/> to a Beam ChemicalGraph. The graph
        /// can when be written directly as to a SMILES or manipulated further (e.g
        /// canonical ordering/standard-form and other normalisations).
        /// </summary>
        /// <param name="ac">an atom container instance</param>
        /// <returns>the Beam ChemicalGraph for additional manipulation</returns>
        internal static Graph ToBeamGraph(IAtomContainer ac, SmiFlavors flavour)
        {
            int order = ac.Atoms.Count;

            var gb      = GraphBuilder.Create(order);
            var indices = new Dictionary <IAtom, int>(order);

            foreach (var a in ac.Atoms)
            {
                indices[a] = indices.Count;
                gb.Add(ToBeamAtom(a, flavour));
            }

            foreach (var b in ac.Bonds)
            {
                gb.Add(ToBeamEdge(b, flavour, indices));
            }

            // configure stereo-chemistry by encoding the stereo-elements
            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.Stereo))
            {
                foreach (var se in ac.StereoElements)
                {
                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoTetrahedral) &&
                        se is ITetrahedralChirality)
                    {
                        AddTetrahedralConfiguration((ITetrahedralChirality)se, gb, indices);
                    }
                    else if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoCisTrans) &&
                             se is IDoubleBondStereochemistry)
                    {
                        AddGeometricConfiguration((IDoubleBondStereochemistry)se, flavour, gb, indices);
                    }
                    else if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoExTetrahedral) &&
                             se is ExtendedTetrahedral)
                    {
                        AddExtendedTetrahedralConfiguration((ExtendedTetrahedral)se, gb, indices);
                    }
                    else if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoExCisTrans) &&
                             se is ExtendedCisTrans)
                    {
                        AddExtendedCisTransConfig((ExtendedCisTrans)se, gb, indices, ac);
                    }
                }
            }

            return(gb.Build());
        }
Exemple #5
0
        /// <summary>
        /// Convert a CDK <see cref="IBond"/> to the Beam edge label type.
        /// </summary>
        /// <param name="b">cdk bond</param>
        /// <returns>the edge label for the Beam edge</returns>
        /// <exception cref="NullReferenceException">the bond order was null and the bond was not-aromatic</exception>
        /// <exception cref="ArgumentException">the bond order could not be converted</exception>
        private static Bond ToBeamEdgeLabel(IBond b, SmiFlavors flavour)
        {
            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.UseAromaticSymbols) && b.IsAromatic)
            {
                if (!b.Begin.IsAromatic || !b.End.IsAromatic)
                {
                    throw new InvalidOperationException("Aromatic bond connects non-aromatic atomic atoms");
                }
                return(Bond.Aromatic);
            }

            if (b.Order.IsUnset())
            {
                throw new CDKException("A bond had undefined order, possible query bond?");
            }

            var order = b.Order;

            switch (order)
            {
            case BondOrder.Single:
                return(Bond.Single);

            case BondOrder.Double:
                return(Bond.Double);

            case BondOrder.Triple:
                return(Bond.Triple);

            case BondOrder.Quadruple:
                return(Bond.Quadruple);

            default:
                throw new CDKException("Unsupported bond order: " + order);
            }
        }
Exemple #6
0
        internal static string Generate(CxSmilesState state, SmiFlavors opts, int[] components, int[] ordering)
        {
            if (!SmiFlavorTool.IsSet(opts, SmiFlavors.CxSmilesWithCoords))
            {
                return("");
            }

            var invorder = Inverse(ordering);

            var sb = new StringBuilder();

            sb.Append(' ');
            sb.Append('|');

            //int invComp(int a, int b) => invorder[a].CompareTo(invorder[b]);
            int comp(int a, int b) => ordering[a].CompareTo(ordering[b]);

            // Fragment Grouping
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxFragmentGroup) &&
                state.fragGroups != null &&
                state.fragGroups.Any())
            {
                int maxCompId = 0;
                foreach (int compId_ in components)
                {
                    if (compId_ > maxCompId)
                    {
                        maxCompId = compId_;
                    }
                }

                // get the output component order
                var compMap = new int[maxCompId + 1];
                int compId  = 1;
                foreach (int idx in invorder)
                {
                    var component = components[idx];
                    if (compMap[component] == 0)
                    {
                        compMap[component] = compId++;
                    }
                }
                // index vs number, we need to output index
                for (int i = 0; i < compMap.Length; i++)
                {
                    compMap[i]--;
                }

                int compComp(int a, int b) => compMap[a].CompareTo(compMap[b]);

                var fragGroupCpy = new List <List <int> >(state.fragGroups);
                foreach (var idxs in fragGroupCpy)
                {
                    idxs.Sort(compComp);
                }
                fragGroupCpy.Sort((a, b) => CxSmilesGenerator.Compare(compComp, a, b));

                // C1=CC=CC=C1.C1=CC=CC=C1.[OH-].[Na+]>> |f:0.1,2.3,c:0,2,4,6,8,10|
                sb.Append('f');
                sb.Append(':');
                for (int i = 0; i < fragGroupCpy.Count; i++)
                {
                    if (i > 0)
                    {
                        sb.Append(',');
                    }
                    AppendIntegers(compMap, '.', sb, fragGroupCpy[i]);
                }
            }

            // Atom Labels
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxAtomLabel) &&
                state.atomLabels != null && state.atomLabels.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('$');
                int nonempty_cnt = 0;
                foreach (int idx in invorder)
                {
                    if (!state.atomLabels.TryGetValue(idx, out string label))
                    {
                        label = "";
                    }
                    else
                    {
                        nonempty_cnt++;
                    }
                    sb.Append(Encode_alias(label));
                    // don't need to write anymore more ';'
                    if (nonempty_cnt == state.atomLabels.Count)
                    {
                        break;
                    }
                    sb.Append(";");
                }
                sb.Append('$');
            }

            // Atom Values
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxAtomValue) &&
                state.atomValues != null && state.atomValues.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append("$_AV:");
                int nonempty_cnt = 0;
                foreach (int idx in invorder)
                {
                    var label = state.atomValues[idx];
                    if (string.IsNullOrEmpty(label))
                    {
                        label = "";
                    }
                    else
                    {
                        nonempty_cnt++;
                    }
                    sb.Append(Encode_alias(label));
                    // don't need to write anymore more ';'
                    if (nonempty_cnt == state.atomValues.Count)
                    {
                        break;
                    }
                    sb.Append(";");
                }
                sb.Append('$');
            }

            // 2D/3D Coordinates
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxCoordinates) &&
                state.atomCoords != null && state.atomCoords.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('(');
                for (int i = 0; i < ordering.Length; i++)
                {
                    var xyz = state.atomCoords[invorder[i]];
                    if (i != 0)
                    {
                        sb.Append(';');
                    }
                    if (xyz[0] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[0], 2));
                    }
                    sb.Append(',');
                    if (xyz[1] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[1], 2));
                    }
                    sb.Append(',');
                    if (xyz[2] != 0)
                    {
                        sb.Append(Strings.ToSimpleString(xyz[2], 2));
                    }
                }
                sb.Append(')');
            }

            // Multi-center/Positional variation bonds
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxMulticenter) &&
                state.positionVar != null &&
                state.positionVar.Any())
            {
                if (sb.Length > 2)
                {
                    sb.Append(',');
                }
                sb.Append('m');
                sb.Append(':');

                var multicenters = new List <KeyValuePair <int, IList <int> > >(state.positionVar);

                // consistent output order
                multicenters.Sort((a, b) => comp(a.Key, b.Key));

                for (int i = 0; i < multicenters.Count; i++)
                {
                    if (i != 0)
                    {
                        sb.Append(',');
                    }
                    var e = multicenters[i];
                    sb.Append(ordering[e.Key]);
                    sb.Append(':');
                    var vals = new List <int>(e.Value);
                    vals.Sort(comp);
                    AppendIntegers(ordering, '.', sb, vals);
                }
            }

            // *CCO* |$_AP1;;;;_AP2$,Sg:n:1,2,3::ht|
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxPolymer) &&
                state.sgroups != null && state.sgroups.Any())
            {
                var sgroups = new List <PolymerSgroup>(state.sgroups);

                foreach (PolymerSgroup psgroup in sgroups)
                {
                    psgroup.atomset.Sort(comp);
                }

                sgroups.Sort((a, b) =>
                {
                    int cmp = 0;
                    cmp     = string.CompareOrdinal(a.type, b.type);
                    if (cmp != 0)
                    {
                        return(cmp);
                    }
                    cmp = CxSmilesGenerator.Compare(comp, a.atomset, b.atomset);
                    return(cmp);
                });

                for (int i = 0; i < sgroups.Count; i++)
                {
                    if (sb.Length > 2)
                    {
                        sb.Append(',');
                    }
                    sb.Append("Sg:");
                    var sgroup = sgroups[i];
                    sb.Append(sgroup.type);
                    sb.Append(':');
                    AppendIntegers(ordering, ',', sb, sgroup.atomset);
                    sb.Append(':');
                    if (sgroup.subscript != null)
                    {
                        sb.Append(sgroup.subscript);
                    }
                    sb.Append(':');
                    if (sgroup.supscript != null)
                    {
                        sb.Append(sgroup.supscript.ToLowerInvariant());
                    }
                }
            }

            // [C]1[CH][CH]CCC1 |^1:1,2,^3:0|
            if (SmiFlavorTool.IsSet(opts, SmiFlavors.CxRadical) &&
                state.atomRads != null &&
                state.atomRads.Any())
            {
                var radinv = new SortedDictionary <CxSmilesState.Radical, List <int> >();
                foreach (var e in state.atomRads)
                {
                    if (!radinv.TryGetValue(e.Value, out List <int> idxs))
                    {
                        radinv[e.Value] = idxs = new List <int>();
                    }
                    idxs.Add(e.Key);
                }
                foreach (var e in radinv)
                {
                    if (sb.Length > 2)
                    {
                        sb.Append(',');
                    }
                    sb.Append('^');
                    sb.Append((int)e.Key + 1);
                    sb.Append(':');
                    e.Value.Sort(comp);
                    AppendIntegers(ordering, ',', sb, e.Value);
                }
            }

            sb.Append('|');
            if (sb.Length <= 3)
            {
                return("");
            }
            else
            {
                return(sb.ToString());
            }
        }
Exemple #7
0
            public int Compare(IAtom a, IAtom b)
            {
                var aBonds = mol.GetConnectedBonds(a).ToReadOnlyList();
                var bBonds = mol.GetConnectedBonds(b).ToReadOnlyList();
                var aH     = Unbox(a.ImplicitHydrogenCount);
                var bH     = Unbox(b.ImplicitHydrogenCount);
                int cmp;

                // 1) Connectivity, X=D+h
                if ((cmp = (aBonds.Count + aH).CompareTo(bBonds.Count + bH)) != 0)
                {
                    return(cmp);
                }
                // 2) Degree, D
                if ((cmp = aBonds.Count.CompareTo(bBonds.Count)) != 0)
                {
                    return(cmp);
                }
                // 3) Element, #<N>
                if ((cmp = Unbox(a.AtomicNumber).CompareTo(Unbox(b.AtomicNumber))) != 0)
                {
                    return(cmp);
                }
                // 4a) charge sign
                int aQ = Unbox(a.FormalCharge);
                int bQ = Unbox(b.FormalCharge);

                if ((cmp = ((aQ >> 31) & 0x1).CompareTo((bQ >> 31) & 0x1)) != 0)
                {
                    return(cmp);
                }
                // 4b) charge magnitude
                if ((cmp = Math.Abs(aQ).CompareTo(Math.Abs(bQ))) != 0)
                {
                    return(cmp);
                }
                int aTotalH = aH;
                int bTotalH = bH;

                foreach (var bond in aBonds)
                {
                    aTotalH += bond.GetOther(a).AtomicNumber == 1 ? 1 : 0;
                }
                foreach (var bond in bBonds)
                {
                    bTotalH += bond.GetOther(b).AtomicNumber == 1 ? 1 : 0;
                }
                // 5) total H count
                if ((cmp = aTotalH.CompareTo(bTotalH)) != 0)
                {
                    return(cmp);
                }
                // XXX: valence and ring membership should also be used to split
                //      ties, but will change the current canonical labelling!
                // extra 1) atomic mass
                if (SmiFlavorTool.IsSet(flavor, SmiFlavors.Isomeric) &&
                    (cmp = Unbox(a.MassNumber).CompareTo(b.MassNumber)) != 0)
                {
                    return(cmp);
                }
                // extra 2) atom map
                if (SmiFlavorTool.IsSet(flavor, SmiFlavors.AtomAtomMap) &&
                    (flavor & SmiFlavors.AtomAtomMapRenumber) != SmiFlavors.AtomAtomMapRenumber)
                {
                    var aMapIdx = a.GetProperty <int>(CDKPropertyName.AtomAtomMapping);
                    var bMapIdx = b.GetProperty <int>(CDKPropertyName.AtomAtomMapping);
                    if ((cmp = Unbox(aMapIdx).CompareTo(Unbox(bMapIdx))) != 0)
                    {
                        return(cmp);
                    }
                }
                return(0);
            }
Exemple #8
0
        // Creates a CxSmilesState from a molecule with atom labels, repeat units, multi-center bonds etc
        private static CxSmilesState GetCxSmilesState(SmiFlavors flavour, IAtomContainer mol)
        {
            CxSmilesState state = new CxSmilesState
            {
                atomCoords = new List <double[]>(),
                coordFlag  = false
            };

            // set the atom labels, values, and coordinates,
            // and build the atom->idx map required by other parts
            var atomidx = new Dictionary <IAtom, int>();

            for (int idx = 0; idx < mol.Atoms.Count; idx++)
            {
                IAtom atom = mol.Atoms[idx];
                if (atom is IPseudoAtom)
                {
                    if (state.atomLabels == null)
                    {
                        state.atomLabels = new SortedDictionary <int, string>();
                    }

                    IPseudoAtom pseudo = (IPseudoAtom)atom;
                    if (pseudo.AttachPointNum > 0)
                    {
                        state.atomLabels[idx] = "_AP" + pseudo.AttachPointNum;
                    }
                    else
                    {
                        if (!"*".Equals(pseudo.Label, StringComparison.Ordinal))
                        {
                            state.atomLabels[idx] = pseudo.Label;
                        }
                    }
                }
                object comment = atom.GetProperty <object>(CDKPropertyName.Comment);
                if (comment != null)
                {
                    if (state.atomValues == null)
                    {
                        state.atomValues = new SortedDictionary <int, string>();
                    }
                    state.atomValues[idx] = comment.ToString();
                }
                atomidx[atom] = idx;

                var p2 = atom.Point2D;
                var p3 = atom.Point3D;

                if (SmiFlavorTool.IsSet(flavour, SmiFlavors.Cx2dCoordinates) && p2 != null)
                {
                    state.atomCoords.Add(new double[] { p2.Value.X, p2.Value.Y, 0 });
                    state.coordFlag = true;
                }
                else if (SmiFlavorTool.IsSet(flavour, SmiFlavors.Cx3dCoordinates) && p3 != null)
                {
                    state.atomCoords.Add(new double[] { p3.Value.X, p3.Value.Y, p3.Value.Z });
                    state.coordFlag = true;
                }
                else if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxCoordinates))
                {
                    state.atomCoords.Add(new double[3]);
                }
            }

            if (!state.coordFlag)
            {
                state.atomCoords = null;
            }

            // radicals
            if (mol.SingleElectrons.Count > 0)
            {
                state.atomRads = new SortedDictionary <int, CxSmilesState.Radical>();
                foreach (ISingleElectron radical in mol.SingleElectrons)
                {
                    // 0->1, 1->2, 2->3
                    if (!state.atomRads.TryGetValue(EnsureNotNull(atomidx[radical.Atom]), out CxSmilesState.Radical val))
                    {
                        val = CxSmilesState.Radical.Monovalent;
                    }
                    else if (val == CxSmilesState.Radical.Monovalent)
                    {
                        val = CxSmilesState.Radical.Divalent;
                    }
                    else if (val == CxSmilesState.Radical.Divalent)
                    {
                        val = CxSmilesState.Radical.Trivalent;
                    }
                    else if (val == CxSmilesState.Radical.Trivalent)
                    {
                        throw new ArgumentException("Invalid radical state, can not be more than trivalent");
                    }

                    state.atomRads[atomidx[radical.Atom]] = val;
                }
            }

            var sgroups = mol.GetCtabSgroups();

            if (sgroups != null)
            {
                state.sgroups     = new List <CxSmilesState.PolymerSgroup>();
                state.positionVar = new SortedDictionary <int, IList <int> >();
                foreach (Sgroup sgroup in sgroups)
                {
                    switch (sgroup.Type)
                    {
                    // polymer SRU
                    case SgroupType.CtabStructureRepeatUnit:
                    case SgroupType.CtabMonomer:
                    case SgroupType.CtabMer:
                    case SgroupType.CtabCopolymer:
                    case SgroupType.CtabCrossLink:
                    case SgroupType.CtabModified:
                    case SgroupType.CtabMixture:
                    case SgroupType.CtabFormulation:
                    case SgroupType.CtabAnyPolymer:
                    case SgroupType.CtabGeneric:
                    case SgroupType.CtabComponent:
                    case SgroupType.CtabGraft:
                        string supscript = (string)sgroup.GetValue(SgroupKey.CtabConnectivity);
                        state.sgroups.Add(new CxSmilesState.PolymerSgroup(GetSgroupPolymerKey(sgroup),
                                                                          ToAtomIdxs(sgroup.Atoms, atomidx),
                                                                          sgroup.Subscript,
                                                                          supscript));
                        break;

                    case SgroupType.ExtMulticenter:
                        IAtom        beg   = null;
                        List <IAtom> ends  = new List <IAtom>();
                        ISet <IBond> bonds = sgroup.Bonds;
                        if (bonds.Count != 1)
                        {
                            throw new ArgumentException("Multicenter Sgroup in inconsistent state!");
                        }
                        IBond bond = bonds.First();
                        foreach (IAtom atom in sgroup.Atoms)
                        {
                            if (bond.Contains(atom))
                            {
                                if (beg != null)
                                {
                                    throw new ArgumentException("Multicenter Sgroup in inconsistent state!");
                                }
                                beg = atom;
                            }
                            else
                            {
                                ends.Add(atom);
                            }
                        }
                        state.positionVar[EnsureNotNull(atomidx[beg])] =
                            ToAtomIdxs(ends, atomidx);
                        break;

                    case SgroupType.CtabAbbreviation:
                    case SgroupType.CtabMultipleGroup:
                        // display shortcuts are not output
                        break;

                    case SgroupType.CtabData:
                        // can be generated but currently ignored
                        break;

                    default:
                        throw new NotSupportedException("Unsupported Sgroup Polymer");
                    }
                }
            }

            return(state);
        }
Exemple #9
0
        /// <summary>
        /// Create a SMILES for a reaction of the flavour specified in the constructor and
        /// write the output order to the provided array.
        /// </summary>
        /// <param name="reaction">CDK reaction instance</param>
        /// <param name="ordering">order of output</param>
        /// <returns>reaction SMILES</returns>
        public string Create(IReaction reaction, int[] ordering)
        {
            var reactants = reaction.Reactants;
            var agents    = reaction.Agents;
            var products  = reaction.Products;

            var reactantPart = reaction.Builder.NewAtomContainer();
            var agentPart    = reaction.Builder.NewAtomContainer();
            var productPart  = reaction.Builder.NewAtomContainer();

            var sgroups = new List <Sgroup>();

            foreach (var reactant in reactants)
            {
                reactantPart.Add(reactant);
                SafeAddSgroups(sgroups, reactant);
            }
            foreach (var agent in agents)
            {
                agentPart.Add(agent);
                SafeAddSgroups(sgroups, agent);
            }
            foreach (var product in products)
            {
                productPart.Add(product);
                SafeAddSgroups(sgroups, product);
            }

            var reactantOrder = new int[reactantPart.Atoms.Count];
            var agentOrder    = new int[agentPart.Atoms.Count];
            var productOrder  = new int[productPart.Atoms.Count];

            var expectedSize = reactantOrder.Length + agentOrder.Length + productOrder.Length;

            if (expectedSize != ordering.Length)
            {
                throw new CDKException($"Output ordering array does not have correct amount of space: {ordering.Length} expected: {expectedSize}");
            }

            // we need to make sure we generate without the CXSMILES layers
            string smi = Create(reactantPart, flavour & ~SmiFlavors.CxSmilesWithCoords, reactantOrder) + ">" +
                         Create(agentPart, flavour & ~SmiFlavors.CxSmilesWithCoords, agentOrder) + ">" +
                         Create(productPart, flavour & ~SmiFlavors.CxSmilesWithCoords, productOrder);

            // copy ordering back to unified array and adjust values
            var agentBeg = reactantOrder.Length;
            var agentEnd = reactantOrder.Length + agentOrder.Length;
            var prodEnd  = reactantOrder.Length + agentOrder.Length + productOrder.Length;

            System.Array.Copy(reactantOrder, 0, ordering, 0, agentBeg);
            System.Array.Copy(agentOrder, 0, ordering, agentBeg, agentEnd - agentBeg);
            System.Array.Copy(productOrder, 0, ordering, agentEnd, prodEnd - agentEnd);
            for (int i = agentBeg; i < agentEnd; i++)
            {
                ordering[i] += agentBeg;
            }
            for (int i = agentEnd; i < prodEnd; i++)
            {
                ordering[i] += agentEnd;
            }

            if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
            {
                var unified = reaction.Builder.NewAtomContainer();
                unified.Add(reactantPart);
                unified.Add(agentPart);
                unified.Add(productPart);
                unified.SetCtabSgroups(sgroups);

                // base CXSMILES state information
                var cxstate = GetCxSmilesState(flavour, unified);

                int[] components = null;

                // extra state info on fragment grouping, specific to reactions
                if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxFragmentGroup))
                {
                    cxstate.fragGroups = new List <List <int> >();

                    // calculate the connected components
                    components = new ConnectedComponents(GraphUtil.ToAdjList(unified)).GetComponents();

                    // AtomContainerSet is ordered so this is safe, it was actually a set we
                    // would need some extra data structures
                    var tmp = new HashSet <int>();
                    int beg = 0, end = 0;
                    foreach (var mol in reactants)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                    foreach (var mol in agents)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                    foreach (var mol in products)
                    {
                        end = end + mol.Atoms.Count;
                        tmp.Clear();
                        for (int i = beg; i < end; i++)
                        {
                            tmp.Add(components[i]);
                        }
                        if (tmp.Count > 1)
                        {
                            cxstate.fragGroups.Add(new List <int>(tmp));
                        }
                        beg = end;
                    }
                }

                smi += CxSmilesGenerator.Generate(cxstate, flavour, components, ordering);
            }

            return(smi);
        }
Exemple #10
0
        /// <summary>
        /// Creates a SMILES string of the flavour specified as a parameter
        /// and write the output order to the provided array.
        /// </summary>
        /// <remarks>
        /// The output order allows one to arrange auxiliary atom data in the
        /// order that a SMILES string will be read. A simple example is seen below
        /// where 2D coordinates are stored with a SMILES string. This method
        /// forms the basis of CXSMILES.
        /// </remarks>
        /// <example>
        /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Smiles.SmilesGenerator_Example.cs+Create_IAtomContainer_int_int"]/*' />
        /// </example>
        /// <param name="molecule">the molecule to write</param>
        /// <param name="order">array to store the output order of atoms</param>
        /// <returns>the SMILES string</returns>
        /// <exception cref="CDKException">a valid SMILES could not be created</exception>
        public static string Create(IAtomContainer molecule, SmiFlavors flavour, int[] order)
        {
            try
            {
                if (order.Length != molecule.Atoms.Count)
                {
                    throw new ArgumentException("the array for storing output order should be the same length as the number of atoms");
                }

                var g = CDKToBeam.ToBeamGraph(molecule, flavour);

                // apply the canonical labelling
                if (SmiFlavorTool.IsSet(flavour, SmiFlavors.Canonical))
                {
                    // determine the output order
                    var labels = Labels(flavour, molecule);

                    g = g.Permute(labels);

                    if ((flavour & SmiFlavors.AtomAtomMapRenumber) == SmiFlavors.AtomAtomMapRenumber)
                    {
                        g = Functions.RenumberAtomMaps(g);
                    }

                    if (!SmiFlavorTool.IsSet(flavour, SmiFlavors.UseAromaticSymbols))
                    {
                        g = g.Resonate();
                    }

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.StereoCisTrans))
                    {
                        // FIXME: required to ensure canonical double bond labelling
                        g.Sort(new Graph.VisitHighOrderFirst());

                        // canonical double-bond stereo, output be C/C=C/C or C\C=C\C
                        // and we need to normalise to one
                        g = Functions.NormaliseDirectionalLabels(g);

                        // visit double bonds first, prefer C1=CC=C1 to C=1C=CC1
                        // visit hydrogens first
                        g.Sort(new Graph.VisitHighOrderFirst()).Sort(new Graph.VisitHydrogenFirst());
                    }

                    var smiles = g.ToSmiles(order);

                    // the SMILES has been generated on a reordered molecule, transform
                    // the ordering
                    var canorder = new int[order.Length];
                    for (int i = 0; i < labels.Length; i++)
                    {
                        canorder[i] = order[labels[i]];
                    }
                    System.Array.Copy(canorder, 0, order, 0, order.Length);

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
                    {
                        smiles += CxSmilesGenerator.Generate(GetCxSmilesState(flavour, molecule), flavour, null, order);
                    }

                    return(smiles);
                }
                else
                {
                    string smiles = g.ToSmiles(order);

                    if (SmiFlavorTool.IsSet(flavour, SmiFlavors.CxSmilesWithCoords))
                    {
                        smiles += CxSmilesGenerator.Generate(GetCxSmilesState(flavour, molecule), flavour, null, order);
                    }

                    return(smiles);
                }
            }
            catch (IOException e)
            {
                throw new CDKException(e.Message);
            }
        }