/// <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); }
private void SetDirection(Edge e, int u, Bond b) { if (e.Either() == u) { e.SetBond(b); } else { e.SetBond(b.Inverse()); } }
public Bond GetBond(int x) { if (x == u) { return(b); } else if (x == v) { return(b.Inverse()); } throw new ArgumentException("invalid endpoint"); }
private void AssignDirectionalLabels() { if (!builders.Any()) { return; } // handle extended geometric configurations first var buildersToRemove = new List <GeometricBuilder>(); foreach (var builder in builders) { if (!builder.Extended) { continue; } buildersToRemove.Add(builder); Edge e = FindDoubleBond(g, builder.u); Edge f = FindDoubleBond(g, builder.v); if (e == null || f == null) { continue; } Edge eRef = g.CreateEdge(builder.u, builder.X); Edge fRef = g.CreateEdge(builder.v, builder.Y); Edge eLab = FindBondToLabel(g, builder.u); Edge fLab = FindBondToLabel(g, builder.v); // adjust for reference Configuration.ConfigurationDoubleBond config = builder.c; if ((eLab == eRef) != (fRef == fLab)) { if (config == Configuration.ConfigurationDoubleBond.Together) { config = Configuration.ConfigurationDoubleBond.Opposite; } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { config = Configuration.ConfigurationDoubleBond.Together; } } if (eLab.Bond.IsDirectional) { if (fLab.Bond.IsDirectional) { // can't do anything, may be incorrect } else { if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(fLab, builder.v, eLab.GetBond(builder.u)); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(fLab, builder.v, eLab.GetBond(builder.u)); } } } else { if (fLab.Bond.IsDirectional) { if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(eLab, builder.v, fLab.GetBond(builder.u)); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(eLab, builder.v, fLab.GetBond(builder.u)); } } else { SetDirection(eLab, builder.u, Bond.Down); if (config == Configuration.ConfigurationDoubleBond.Together) { SetDirection(fLab, builder.v, Bond.Down); } else if (config == Configuration.ConfigurationDoubleBond.Opposite) { SetDirection(fLab, builder.v, Bond.Up); } } } } foreach (var builder in buildersToRemove) { builders.Remove(builder); } if (!builders.Any()) { return; } // store the vertices which are adjacent to pi bonds with a config BitArray pibonded = new BitArray(g.Order); BitArray unspecified = new BitArray(g.Order); var unspecEdges = new HashSet <Edge>(); // clear existing directional labels, if build is called multiple times // this can cause problems if (g.GetFlags(Graph.HAS_BND_STRO) != 0) { foreach (Edge edge in g.Edges) { if (edge.Bond.IsDirectional) { edge.SetBond(Bond.Implicit); } } } foreach (Edge e in g.Edges) { int u = e.Either(); int v = e.Other(u); if (e.Bond.Order == 2 && g.Degree(u) >= 2 && g.Degree(v) >= 2) { unspecified.Set(u, true); unspecified.Set(v, true); pibonded.Set(u, true); pibonded.Set(v, true); unspecEdges.Add(e); } } foreach (var builder in builders) { g.AddFlags(Graph.HAS_BND_STRO); // unspecified only used for getting not setting configuration if (builder.c == Configuration.ConfigurationDoubleBond.Unspecified) { continue; } CheckGeometricBuilder(builder); // check required vertices are adjacent int u = builder.u, v = builder.v, x = builder.X, y = builder.Y; if (x == y) { continue; } unspecEdges.Remove(g.CreateEdge(u, v)); unspecified.Set(u, false); unspecified.Set(v, false); Fix(g, u, v, pibonded); Fix(g, v, u, pibonded); Bond first = FirstDirectionalLabel(u, x, pibonded); Bond second = builder.c == Configuration.ConfigurationDoubleBond.Together ? first : first.Inverse(); // check if the second label would cause a conflict if (CheckDirectionalAssignment(second, v, y, pibonded)) { // okay to assign the labels as they are g.Replace(g.CreateEdge(u, x), new Edge(u, x, first)); g.Replace(g.CreateEdge(v, y), new Edge(v, y, second)); } // there will be a conflict - check if we invert the first one... else if (CheckDirectionalAssignment(first.Inverse(), u, x, pibonded)) { g.Replace(g.CreateEdge(u, x), new Edge(u, x, (first = first.Inverse()))); g.Replace(g.CreateEdge(v, y), new Edge(v, y, (second = second.Inverse()))); } else { BitArray visited = new BitArray(g.Order); visited.Set(v, true); InvertExistingDirectionalLabels(pibonded, visited, v, u); if (!CheckDirectionalAssignment(first, u, x, pibonded) || !CheckDirectionalAssignment(second, v, y, pibonded)) { throw new ArgumentException("cannot assign geometric configuration"); } g.Replace(g.CreateEdge(u, x), new Edge(u, x, first)); g.Replace(g.CreateEdge(v, y), new Edge(v, y, second)); } // propagate bond directions to other adjacent bonds foreach (var e in g.GetEdges(u)) { if (e.Bond != Bond.Double && !e.Bond.IsDirectional) { e.SetBond(e.Either() == u ? first.Inverse() : first); } } foreach (var e in g.GetEdges(v)) { if (e.Bond != Bond.Double && !e.Bond.IsDirectional) { e.SetBond(e.Either() == v ? second.Inverse() : second); } } } // unspecified pibonds should "not" have a configuration, if they // do we try to eliminate it foreach (Edge unspecEdge in unspecEdges) { int u = unspecEdge.Either(); int v = unspecEdge.Other(u); // no problem if one side isn't defined if (!HasDirectional(g, u) || !HasDirectional(g, v)) { continue; } foreach (Edge e in g.GetEdges(u)) { if (IsRedundantDirectionalEdge(g, e, unspecified)) { e.SetBond(Bond.Implicit); } } if (!HasDirectional(g, u)) { continue; } foreach (Edge e in g.GetEdges(v)) { if (IsRedundantDirectionalEdge(g, e, unspecified)) { e.SetBond(Bond.Implicit); } } // if (hasDirectional(g, v)) // could generate warning! } }