/// <summary> /// Decide the bond to use for a ring bond. The bond symbol can be present on /// either or both bonded atoms. This method takes those bonds, chooses the /// correct one or reports an error if there is a conflict. /// </summary> /// <remarks> /// Equivalent SMILES: /// <list type="bullet"> /// <item>C=1CCCCC=1</item> /// <item>C=1CCCCC1 (preferred)</item> /// <item>C1CCCCC=1</item> /// </list> /// </remarks> /// <param name="a">a bond</param> /// <param name="b">other bond</param> /// <param name="pos">the position in the string of bond <paramref name="a"/></param> /// <param name="buffer"></param> /// <returns>the bond to use for this edge</returns> /// <exception cref="InvalidSmilesException">ring bonds did not match</exception> public Bond DecideBond(Bond a, Bond b, int pos, CharBuffer buffer) { if (a == b) { return(a); } else if (a == Bond.Implicit) { return(b); } else if (b == Bond.Implicit) { return(a); } if (strict || a.Inverse() != b) { throw new InvalidSmilesException($"Ring closure bonds did not match, '{a}'!='{b}':" + InvalidSmilesException.Display(buffer, pos - buffer.Position, lastBondPos - buffer.Position)); } warnings.Add("Ignored invalid Cis/Trans on ring closure, should flip:" + InvalidSmilesException.Display(buffer, pos - buffer.Position, lastBondPos - buffer.Position)); return(Bond.Implicit); }
/// <summary> /// Read a bracket atom from the buffer. A bracket atom optionally defines /// isotope, chirality, hydrogen count, formal charge and the atom class. /// <para> /// bracket_atom ::= '[' isotope? symbol chiral? hcount? charge? class? ']' /// </para> /// </summary> /// <param name="buffer">a character buffer</param> /// <returns>a bracket atom</returns> /// <exception cref="InvalidSmilesException">if the bracket atom did not match the grammar, invalid symbol, missing closing bracket or invalid chiral specification.</exception> public IAtom ReadBracketAtom(CharBuffer buffer) { int start = buffer.Position; bool arbitraryLabel = false; if (!buffer.HasRemaining()) { throw new InvalidSmilesException("Unclosed bracket atom, SMILES may be truncated", buffer); } var isotope = buffer.GetNumber(); var aromatic = buffer.NextChar >= 'a' && buffer.NextChar <= 'z'; var element = Element.Read(buffer); if (element == Element.Unknown) { hasAstrix = true; } if (strict && element == null) { throw new InvalidSmilesException("unrecognised element symbol, SMILES may be truncated: ", buffer); } if (element != null && aromatic) { g.AddFlags(Graph.HAS_AROM); } // element isn't aromatic as per the OpenSMILES specification if (strict && aromatic && !element.IsAromatic(Element.AromaticSpecification.OpenSmiles)) { throw new InvalidSmilesException("abnormal aromatic element", buffer); } if (element == null) { arbitraryLabel = true; } configuration = Configuration.Read(buffer); var hCount = ReadHydrogens(buffer); var charge = ReadCharge(buffer); var atomClass = ReadClass(buffer); if (!arbitraryLabel && !buffer.GetIf(']')) { if (strict) { throw InvalidSmilesException.InvalidBracketAtom(buffer); } else { arbitraryLabel = true; } } if (arbitraryLabel) { var end = buffer.Position; int depth = 1; while (buffer.HasRemaining()) { char c = buffer.Get(); if (c == '[') { depth++; } else if (c == ']') { depth--; if (depth == 0) { break; } } end++; } if (depth != 0) { throw new InvalidSmilesException("unparsable label in bracket atom", buffer, buffer.Position - 1); } var label = buffer.Substr(start, end); hasAstrix = true; return(new AtomImpl.BracketAtom(label)); } return(new AtomImpl.BracketAtom(isotope, element, hCount, charge, atomClass, aromatic)); }
/// <summary> /// Create the topologies (stereo configurations) for the chemical graph. The /// topologies define spacial arrangement around atoms. /// </summary> private void CreateTopologies(CharBuffer buffer) { // create topologies (stereo configurations) foreach (var e in configurations) { AddTopology(e.Key, Topology.ToExplicit(g, e.Key, e.Value)); } for (int v = BitArrays.NextSetBit(checkDirectionalBonds, 0); v >= 0; v = BitArrays.NextSetBit(checkDirectionalBonds, v + 1)) { int nUpV = 0; int nDownV = 0; int nUpW = 0; int nDownW = 0; int w = -1; { int d = g.Degree(v); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(v, j); Bond bond = e.GetBond(v); if (bond == Bond.Up) { nUpV++; } else if (bond == Bond.Down) { nDownV++; } else if (bond == Bond.Double) { w = e.Other(v); } } } if (w < 0) { continue; } BitArrays.EnsureCapacity(checkDirectionalBonds, w + 1); checkDirectionalBonds.Set(w, false); { int d = g.Degree(w); for (int j = 0; j < d; ++j) { Edge e = g.EdgeAt(w, j); Bond bond = e.GetBond(w); if (bond == Bond.Up) { nUpW++; } else if (bond == Bond.Down) { nDownW++; } } } if (nUpV + nDownV == 0 || nUpW + nDownW == 0) { continue; } if (nUpV > 1 || nDownV > 1) { int offset1 = -1, offset2 = -1; foreach (var e in g.GetEdges(v)) { if (e.Bond.IsDirectional) { if (offset1 < 0) { offset1 = bondStrPos[e]; } else { offset2 = bondStrPos[e]; } } } var errorPos = InvalidSmilesException.Display(buffer, offset1 - buffer.Length, offset2 - buffer.Length); if (strict) { throw new InvalidSmilesException($"Ignored invalid Cis/Trans specification: {errorPos}"); } else { warnings.Add($"Ignored invalid Cis/Trans specification: {errorPos}"); } } if (nUpW > 1 || nDownW > 1) { int offset1 = -1; int offset2 = -1; foreach (var e in g.GetEdges(w)) { if (e.Bond.IsDirectional) { if (offset1 < 0) { offset1 = bondStrPos[e]; } else { offset2 = bondStrPos[e]; } } } var errorPos = InvalidSmilesException.Display(buffer, offset1 - buffer.Length, offset2 - buffer.Length); if (strict) { throw new InvalidSmilesException($"Ignored invalid Cis/Trans specification: {errorPos}"); } else { warnings.Add($"Ignored invalid Cis/Trans specification: {errorPos}"); } } } }