public static QueryAtomContainer CreateSymbolChargeIDQueryContainer(IAtomContainer container) { var queryContainer = new QueryAtomContainer(); for (int i = 0; i < container.Atoms.Count; i++) { queryContainer.Atoms.Add(new SymbolChargeIDQueryAtom(container.Atoms[i])); } foreach (var bond in container.Bonds) { int index1 = container.Atoms.IndexOf(bond.Begin); int index2 = container.Atoms.IndexOf(bond.End); if (bond.IsAromatic) { var qbond = new QueryBond(queryContainer.Atoms[index1], queryContainer.Atoms[index2], ExprType.IsAromatic); queryContainer.Bonds.Add(qbond); } else { QueryBond qbond = new QueryBond(queryContainer.Atoms[index1], queryContainer.Atoms[index2], ExprType.Order, bond.Order.Numeric()) { Order = bond.Order // backwards compatibility }; queryContainer.Bonds.Add(qbond); } } return(queryContainer); }
/// <summary> /// Create a query from a molecule and a provided set of expressions. The /// molecule is converted and any features specified in the <paramref name="opts"/> /// will be matched. /// </summary> /// <remarks> /// A good starting point is the following options: /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Isomorphisms.Matchers.QueryAtomContainer_Example.cs+Create1"]/*' /> /// Specifying <see cref="ExprType.Degree"/> (or <see cref="ExprType.TotalDegree"/> + /// <see cref="ExprType.ImplicitHCount"/>) means the molecule will not match as a /// substructure. /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Isomorphisms.Matchers.QueryAtomContainer_Example.cs+Create2"]/*' /> /// The <see cref="ExprType.RingBondCount"/> property is useful for locking in /// ring systems. Specifying the ring bond count on benzene means it will /// not match larger ring systems (e.g. naphthalenee) but can still be /// substituted. /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Isomorphisms.Matchers.QueryAtomContainer_Example.cs+Create3"]/*' /> /// Note that <see cref="ExprType.FormalCharge"/>, /// <see cref="ExprType.ImplicitHCount"/>, and <see cref="ExprType.Isotope"/> are ignored /// if <see langword="null"/>. Explicitly setting these to zero (only required for Isotope from /// SMILES) forces their inclusion. /// <include file='IncludeExamples.xml' path='Comments/Codes[@id="NCDK.Isomorphisms.Matchers.QueryAtomContainer_Example.cs+Create4"]/*' /> /// Please note not all <see cref="ExprType"/>s are currently supported, if you /// require a specific type that you think is useful please open an issue. /// </remarks> /// <param name="mol">the molecule</param> /// <param name="opts">set of the expr types to match</param> /// <returns>the query molecule</returns> public static QueryAtomContainer Create(IAtomContainer mol, params ExprType[] opts) { var optset = new HashSet <ExprType>(opts); var query = new QueryAtomContainer(); var mapping = new CDKObjectMap(); var stereos = new Dictionary <IChemObject, IStereoElement <IChemObject, IChemObject> >(); foreach (var se in mol.StereoElements) { stereos[se.Focus] = se; } var qstereo = new List <IStereoElement <IChemObject, IChemObject> >(); foreach (var atom in mol.Atoms) { var expr = new Expr(); // isotope first if (optset.Contains(ExprType.Isotope) && atom.MassNumber != null) { expr.And(new Expr(ExprType.Isotope, atom.MassNumber.Value)); } if (atom.AtomicNumber != 0) { if (atom.IsAromatic) { if (optset.Contains(ExprType.AromaticElement)) { expr.And(new Expr(ExprType.AromaticElement, atom.AtomicNumber)); } else { if (optset.Contains(ExprType.IsAromatic)) { if (optset.Contains(ExprType.Element)) { expr.And(new Expr(ExprType.AromaticElement, atom.AtomicNumber)); } else { expr.And(new Expr(ExprType.IsAromatic)); } } else if (optset.Contains(ExprType.Element)) { expr.And(new Expr(ExprType.Element, atom.AtomicNumber)); } } } else { if (optset.Contains(ExprType.AliphaticElement)) { expr.And(new Expr(ExprType.AliphaticElement, atom.AtomicNumber)); } else { if (optset.Contains(ExprType.IsAliphatic)) { if (optset.Contains(ExprType.Element)) { expr.And(new Expr(ExprType.AliphaticElement, atom.AtomicNumber)); } else { expr.And(new Expr(ExprType.IsAliphatic)); } } else if (optset.Contains(ExprType.Element)) { expr.And(new Expr(ExprType.Element, atom.AtomicNumber)); } } } } if (optset.Contains(ExprType.Degree)) { expr.And(new Expr(ExprType.Degree, atom.Bonds.Count)); } if (optset.Contains(ExprType.TotalDegree)) { expr.And(new Expr(ExprType.Degree, atom.Bonds.Count + atom.ImplicitHydrogenCount.Value)); } if (optset.Contains(ExprType.IsInRing) || optset.Contains(ExprType.IsInChain)) { expr.And(new Expr(atom.IsInRing ? ExprType.IsInRing : ExprType.IsInChain)); } if (optset.Contains(ExprType.ImplicitHCount)) { expr.And(new Expr(ExprType.ImplicitHCount)); } if (optset.Contains(ExprType.RingBondCount)) { int rbonds = 0; foreach (var bond in mol.GetConnectedBonds(atom)) { if (bond.IsInRing) { rbonds++; } } expr.And(new Expr(ExprType.RingBondCount, rbonds)); } if (optset.Contains(ExprType.FormalCharge) && atom.FormalCharge != null) { expr.And(new Expr(ExprType.FormalCharge, atom.FormalCharge.Value)); } if (stereos.TryGetValue(atom, out IStereoElement <IChemObject, IChemObject> se) && se.Class == StereoClass.Tetrahedral && optset.Contains(ExprType.Stereochemistry)) { expr.And(new Expr(ExprType.Stereochemistry, (int)se.Configure)); qstereo.Add(se); } var qatom = new QueryAtom(expr); // backward compatibility for naughty methods that are expecting // these to be set for a query! if (optset.Contains(ExprType.Element) || optset.Contains(ExprType.AromaticElement) || optset.Contains(ExprType.AliphaticElement)) { qatom.Symbol = atom.Symbol; } if (optset.Contains(ExprType.AromaticElement) || optset.Contains(ExprType.IsAromatic)) { qatom.IsAromatic = atom.IsAromatic; } mapping.Add(atom, qatom); query.Atoms.Add(qatom); } foreach (var bond in mol.Bonds) { var expr = new Expr(); if (bond.IsAromatic && (optset.Contains(ExprType.SingleOrAromatic) || optset.Contains(ExprType.DoubleOrAromatic) || optset.Contains(ExprType.IsAromatic))) { expr.And(new Expr(ExprType.IsAromatic)); } else if ((optset.Contains(ExprType.SingleOrAromatic) || optset.Contains(ExprType.DoubleOrAromatic) || optset.Contains(ExprType.AliphaticOrder)) && !bond.IsAromatic) { expr.And(new Expr(ExprType.AliphaticOrder, bond.Order.Numeric())); } else if (bond.IsAromatic && optset.Contains(ExprType.IsAliphatic)) { expr.And(new Expr(ExprType.IsAliphatic)); } else if (optset.Contains(ExprType.Order)) { expr.And(new Expr(ExprType.Order, bond.Order.Numeric())); } if (optset.Contains(ExprType.IsInRing) && bond.IsInRing) { expr.And(new Expr(ExprType.IsInRing)); } else if (optset.Contains(ExprType.IsInChain) && !bond.IsInRing) { expr.And(new Expr(ExprType.IsInChain)); } if (stereos.TryGetValue(bond, out IStereoElement <IChemObject, IChemObject> se) && optset.Contains(ExprType.Stereochemistry)) { expr.And(new Expr(ExprType.Stereochemistry, (int)se.Configure)); qstereo.Add(se); } var qbond = new QueryBond(mapping.Get(bond.Begin), mapping.Get(bond.End), expr); // backward compatibility for naughty methods that are expecting // these to be set for a query! if (optset.Contains(ExprType.AliphaticOrder) || optset.Contains(ExprType.Order)) { qbond.Order = bond.Order; } if (optset.Contains(ExprType.SingleOrAromatic) || optset.Contains(ExprType.DoubleOrAromatic) || optset.Contains(ExprType.IsAromatic)) { qbond.IsAromatic = bond.IsAromatic; } mapping.Add(bond, qbond); query.Bonds.Add(qbond); } foreach (var se in qstereo) { query.StereoElements.Add((IStereoElement <IChemObject, IChemObject>)se.Clone(mapping)); } return(query); }