public void AtomLabelsEmpty() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$$|", state)); Assert.AreEqual(0, state.atomLabels.Count); }
public void AtomLabelsTruncated1() { CxSmilesState state = new CxSmilesState(); Assert.AreEqual(-1, CxSmilesParser.ProcessCx("|$;;;Het", state)); Assert.AreEqual(-1, CxSmilesParser.ProcessCx("|$;;;Het;", state)); }
public void EscapedAtomLabels() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$R';;;;;;$|", state)); Assert.IsTrue(state.atomLabels[0] == "R'"); }
/// <summary> /// Coordinates are written between parenthesis. The z-coord may be omitted '(0,1,),(2,3,)'. /// </summary> /// <param name="iter">input characters, iterator is progressed by this method</param> /// <param name="state">output CXSMILES state</param> /// <returns>parse was a success (or not)</returns> private static bool ProcessCoords(CharIter iter, CxSmilesState state) { if (state.atomCoords == null) { state.atomCoords = new List <double[]>(); } while (iter.HasNext()) { // end of coordinate list if (iter.Curr() == ')') { iter.Next(); iter.NextIf(','); // optional return(true); } double x = ReadDouble(iter); if (!iter.NextIf(',')) { return(false); } double y = ReadDouble(iter); if (!iter.NextIf(',')) { return(false); } double z = ReadDouble(iter); iter.NextIf(';'); state.coordFlag = state.coordFlag || z != 0; state.atomCoords.Add(new double[] { x, y, z }); } return(false); }
public void HydrogenBondingTruncated() { CxSmilesState state = new CxSmilesState(); Assert.AreEqual(-1, CxSmilesParser.ProcessCx("|H:0.1,2.3", state)); Assert.AreEqual(-1, CxSmilesParser.ProcessCx("|H:0.1,2.", state)); }
public void RemoveUnderscore() { CxSmilesState state = new CxSmilesState(); CxSmilesParser.ProcessCx("|$;;;_R1;$|", state); Assert.AreEqual("R1", state.atomLabels[3]); }
/// <summary> /// Positional variation/multi centre bonding. Describe as a begin atom and one or more end points. /// </summary> /// <param name="iter">input characters, iterator is progressed by this method</param> /// <param name="state">output CXSMILES state</param> /// <returns>parse was a success (or not)</returns> private static bool ProcessPositionalVariation(CharIter iter, CxSmilesState state) { if (state.positionVar == null) { state.positionVar = new SortedDictionary <int, IList <int> >(); } while (iter.HasNext()) { if (IsDigit(iter.Curr())) { int beg = ProcessUnsignedInt(iter); if (!iter.NextIf(':')) { return(false); } var endpoints = new List <int>(6); if (!ProcessIntList(iter, DotSeparatorChar, endpoints)) { return(false); } iter.NextIf(','); state.positionVar.Add(beg, endpoints); } else { return(true); } } return(false); }
public void EscapedAtomLabels2() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$;;;(C(R41)(R41))n;;R41;R41;R41;;_AP1;R41;R41;;_AP1$|", state)); Assert.IsTrue(state.atomLabels[3] == "(C(R41)(R41))n"); }
public void AtomLabels() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$;;;Het;;;;;A$|", state)); Assert.IsTrue(state.atomLabels.Contains(new KeyValuePair <int, string>(3, "Het"))); Assert.IsTrue(state.atomLabels.Contains(new KeyValuePair <int, string>(8, "A"))); }
public void RelativeStereoMolecule() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|r|", state)); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|r,$_R1$|", state)); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$_R1$,r|", state)); }
public void AtomValues() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|$_AV:;;;5;;;;;8$|", state)); Assert.IsTrue(state.atomValues.Contains(new KeyValuePair <int, string>(3, "5"))); Assert.IsTrue(state.atomValues.Contains(new KeyValuePair <int, string>(8, "8"))); }
public void DataSgroups() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|SgD::cdk:ReactionConditions:Heat Hv|", state)); Assert.IsTrue(Compares.DeepContains( state.dataSgroups, new CxSmilesState.DataSgroup(new List <int>(), "cdk:ReactionConditions", "Heat\nHv", "", "", ""))); }
public void MultiAtomSRU() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|Sg:n:1,2,3:m:ht|", state)); Assert.IsTrue(Compares.DeepContains( state.sgroups, new CxSmilesState.PolymerSgroup("n", new[] { 1, 2, 3 }, "m", "ht"))); }
public void FragmentGrouping() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|f:0.1.2.3,4.5.6|", state)); Assert.IsTrue(Compares.AreDeepEqual( new[] { new[] { 0, 1, 2, 3 }, new[] { 4, 5, 6 } }, state.fragGroups)); }
/// <summary> /// CXSMILES radicals. /// </summary> /// <param name="iter">input characters, iterator is progressed by this method</param> /// <param name="state">output CXSMILES state</param> /// <returns>parse was a success (or not)</returns> private static bool ProcessRadicals(CharIter iter, CxSmilesState state) { if (state.atomRads == null) { state.atomRads = new SortedDictionary <int, Radical>(); } CxSmilesState.Radical rad; switch (iter.Next()) { case '1': rad = Monovalent; break; case '2': rad = Divalent; break; case '3': rad = DivalentSinglet; break; case '4': rad = DivalentTriplet; break; case '5': rad = Trivalent; break; case '6': rad = TrivalentDoublet; break; case '7': rad = TrivalentQuartet; break; default: return(false); } if (!iter.NextIf(':')) { return(false); } var dest = new List <int>(4); if (!ProcessIntList(iter, CommaSeparatorChar, dest)) { return(false); } foreach (var atomidx in dest) { state.atomRads.Add(atomidx, rad); } return(true); }
public void FragmentGroupingFollowedByAtomLabels() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|f:0.1.2.3,4.5.6,$;;;R$|", state)); Assert.IsTrue(Compares.AreDeepEqual( new[] { new[] { 0, 1, 2, 3 }, new[] { 4, 5, 6 } }, state.fragGroups)); Assert.IsTrue(state.atomLabels.Contains(new KeyValuePair <int, string>(3, "R"))); }
public void Multicenter() { var state = new CxSmilesState { positionVar = new SortedDictionary <int, IList <int> >() }; state.positionVar[0] = new[] { 4, 5, 6, 7 }; state.positionVar[2] = new[] { 4, 6, 5, 7 }; Assert.AreEqual(" |m:5:0.1.2.3,7:0.1.2.3|", CxSmilesGenerator.Generate(state, SmiFlavors.CxMulticenter, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 })); }
public void Coords() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|(.0,-1.5,;-1.3,-.75,;-2.6,-1.5,;-3.9,-.75,;-3.9,.75,)|", state)); Assert.IsTrue(new AprxDoubleArray(0, -1.5, 0).Matches(state.atomCoords[0])); Assert.IsTrue(new AprxDoubleArray(-1.3, -.75, 0).Matches(state.atomCoords[1])); Assert.IsTrue(new AprxDoubleArray(-2.6, -1.5, 0).Matches(state.atomCoords[2])); Assert.IsTrue(new AprxDoubleArray(-3.9, -.75, 0).Matches(state.atomCoords[3])); Assert.IsTrue(new AprxDoubleArray(-3.9, .75, 0).Matches(state.atomCoords[4])); }
public void PositionalVariationImpliedLayer() { CxSmilesState state = new CxSmilesState(); Assert.AreNotEqual(-1, CxSmilesParser.ProcessCx("|m:2:5.6.7.8.9.10,4:5.6.7.8.9|", state)); Assert.IsTrue(Compares.DeepContains( state.positionVar, new KeyValuePair <int, IList <int> >(2, new[] { 5, 6, 7, 8, 9, 10 }))); Assert.IsTrue(Compares.DeepContains( state.positionVar, new KeyValuePair <int, IList <int> >(4, new[] { 5, 6, 7, 8, 9 }))); }
public void Sgroups() { CxSmilesState state = new CxSmilesState { sgroups = new List <CxSmilesState.PolymerSgroup>(1) { new CxSmilesState.PolymerSgroup("n", new[] { 2, 3 }, "n", "ht"), new CxSmilesState.PolymerSgroup("n", new[] { 5 }, "m", "ht") } }; Assert.AreEqual(" |Sg:n:2:m:ht,Sg:n:4,5:n:ht|", CxSmilesGenerator.Generate(state, SmiFlavors.CxPolymer, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 })); }
public void Radicals() { CxSmilesState state = new CxSmilesState { atomRads = new SortedDictionary <int, CxSmilesState.Radical> { [2] = CxSmilesState.Radical.Monovalent, [6] = CxSmilesState.Radical.Monovalent, [4] = CxSmilesState.Radical.Divalent } }; Assert.AreEqual(" |^1:1,5,^2:3|", CxSmilesGenerator.Generate(state, SmiFlavors.CxSmiles, Array.Empty <int>(), new int[] { 7, 6, 5, 4, 3, 2, 1, 0 })); }
public void Coords2d() { CxSmilesState state = new CxSmilesState { atomCoords = new List <double[]> { new double[] { 0, 1.5, 0 }, new double[] { 0, 3, 0 }, new double[] { 1.5, 1.5, 0 }, } }; Assert.AreEqual(" |(1.5,1.5,;,1.5,;,3,)|", CxSmilesGenerator.Generate(state, SmiFlavors.CxCoordinates, Array.Empty <int>(), new int[] { 1, 2, 0 })); }
/// <summary> /// Parses CXSMILES layer and set attributes for atoms and bonds on the provided reaction. /// </summary> /// <param name="title">SMILES title field</param> /// <param name="rxn">parsed reaction</param> private void ParseRxnCXSMILES(string title, IReaction rxn) { if (title != null && title.StartsWithChar('|')) { int pos; CxSmilesState cxstate; if ((pos = CxSmilesParser.ProcessCx(title, cxstate = new CxSmilesState())) >= 0) { // set the correct title rxn.SetProperty(CDKPropertyName.Title, title.Substring(pos)); var atomToMol = new Dictionary <IAtom, IAtomContainer>(100); var atoms = new List <IAtom>(); HandleFragmentGrouping(rxn, cxstate); // merge all together foreach (var mol in rxn.Reactants) { foreach (var atom in mol.Atoms) { atoms.Add(atom); atomToMol[atom] = mol; } } foreach (var mol in rxn.Agents) { foreach (var atom in mol.Atoms) { atoms.Add(atom); atomToMol[atom] = mol; } } foreach (var mol in rxn.Products) { foreach (var atom in mol.Atoms) { atoms.Add(atom); atomToMol[atom] = mol; } } AssignCxSmilesInfo(rxn.Builder, rxn, atoms, atomToMol, cxstate); } } }
/// <summary> /// Fragment grouping defines disconnected components that should be considered part of a single molecule (i.e. /// Salts). Examples include NaH, AlCl3, Cs2CO3, HATU, etc. /// </summary> /// <param name="iter">input characters, iterator is progressed by this method</param> /// <param name="state">output CXSMILES state</param> /// <returns>parse was a success (or not)</returns> private static bool ProcessFragmentGrouping(CharIter iter, CxSmilesState state) { if (state.fragGroups == null) { state.fragGroups = new List <List <int> >(); } var dest = new List <int>(); while (iter.HasNext()) { dest.Clear(); if (!ProcessIntList(iter, DotSeparatorChar, dest)) { return(false); } iter.NextIf(CommaSeparatorChar); if (dest.Count == 0) { return(true); } state.fragGroups.Add(new List <int>(dest)); } return(false); }
/// <summary> /// Parses CXSMILES layer and set attributes for atoms and bonds on the provided molecule. /// </summary> /// <param name="title">SMILES title field</param> /// <param name="mol">molecule</param> private void ParseMolCXSMILES(string title, IAtomContainer mol) { if (title != null && title.StartsWithChar('|')) { int pos; CxSmilesState cxstate; if ((pos = CxSmilesParser.ProcessCx(title, cxstate = new CxSmilesState())) >= 0) { // set the correct title mol.Title = title.Substring(pos); var atomToMol = new Dictionary <IAtom, IAtomContainer>(mol.Atoms.Count); var atoms = new List <IAtom>(mol.Atoms.Count); foreach (var atom in mol.Atoms) { atoms.Add(atom); atomToMol.Add(atom, mol); } AssignCxSmilesInfo(mol.Builder, mol, atoms, atomToMol, cxstate); } } }
/// <summary> /// Parse an string possibly containing CXSMILES into an intermediate state /// (<see cref="CxSmilesState"/>) representation. /// </summary> /// <param name="str">input character string (SMILES title field)</param> /// <param name="state">output CXSMILES state</param> /// <returns>position where CXSMILES ends (below 0 means no CXSMILES)</returns> public static int ProcessCx(string str, CxSmilesState state) { CharIter iter = new CharIter(str); if (!iter.NextIf('|')) { return(-1); } while (iter.HasNext()) { switch (iter.Next()) { case '$': // atom labels and values // dest is atom labels by default SortedDictionary <int, string> dest; // check for atom values if (iter.NextIf("_AV:")) { dest = state.atomValues = new SortedDictionary <int, string>(); } else { dest = state.atomLabels = new SortedDictionary <int, string>(); } if (!ProcessAtomLabels(iter, dest)) { return(-1); } break; case '(': // coordinates if (!ProcessCoords(iter, state)) { return(-1); } break; case 'c': // cis/trans/unspec ignored case 't': // c/t: if (iter.NextIf(':')) { if (!SkipIntList(iter, CommaSeparatorChar)) { return(-1); } } // ctu: else if (iter.NextIf("tu:")) { if (!SkipIntList(iter, CommaSeparatorChar)) { return(-1); } } break; case 'r': // relative stereochemistry ignored if (iter.NextIf(':')) { if (!SkipIntList(iter, CommaSeparatorChar)) { return(-1); } } else { if (!iter.NextIf(',') && iter.Curr() != '|') { return(-1); } } break; case 'l': // lone pairs ignored if (!iter.NextIf("p:")) { return(-1); } if (!SkipIntMap(iter)) { return(-1); } break; case 'f': // fragment grouping if (!iter.NextIf(':')) { return(-1); } if (!ProcessFragmentGrouping(iter, state)) { return(-1); } break; case 'S': // Sgroup polymers if (iter.NextIf("g:")) { if (!ProcessPolymerSgroups(iter, state)) { return(-1); } } else if (iter.NextIf("gD:")) { if (!ProcessDataSgroups(iter, state)) { return(-1); } } else { return(-1); } break; case 'm': // positional variation if (!iter.NextIf(':')) { return(-1); } if (!ProcessPositionalVariation(iter, state)) { return(-1); } break; case '^': // Radicals if (!ProcessRadicals(iter, state)) { return(-1); } break; case 'C': case 'H': // coordination and hydrogen bonding ignored if (!iter.NextIf(':')) { return(-1); } while (iter.HasNext() && IsDigit(iter.Curr())) { if (!SkipIntList(iter, DotSeparatorChar)) { return(-1); } iter.NextIf(','); } break; case '|': // end of CX // consume optional separators if (!iter.NextIf(' ')) { iter.NextIf('\t'); } return(iter.pos); default: return(-1); } } return(-1); }
/// <summary> /// Polymer Sgroups describe variations of repeating units. Only the atoms and not crossing bonds are written. /// </summary> /// <param name="iter">input characters, iterator is progressed by this method</param> /// <param name="state">output CXSMILES state</param> /// <returns>parse was a success (or not)</returns> private static bool ProcessPolymerSgroups(CharIter iter, CxSmilesState state) { if (state.sgroups == null) { state.sgroups = new List <PolymerSgroup>(); } var beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } var keyword = iter.Substr(beg, iter.pos); if (!iter.NextIf(':')) { return(false); } var atomset = new List <int>(); if (!ProcessIntList(iter, CommaSeparatorChar, atomset)) { return(false); } string subscript; string supscript; if (!iter.NextIf(':')) { return(false); } // "If the subscript equals the keyword of the Sgroup this field can be empty", ergo // if omitted it equals the keyword beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } subscript = Unescape(iter.Substr(beg, iter.pos)); if (string.IsNullOrEmpty(subscript)) { subscript = keyword; } // "In the superscript only connectivity and flip information is allowed.", default // appears to be "eu" either/unspecified if (!iter.NextIf(':')) { return(false); } beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } supscript = Unescape(iter.Substr(beg, iter.pos)); if (string.IsNullOrEmpty(supscript)) { supscript = "eu"; } if (iter.NextIf(',') || iter.Curr() == '|') { state.sgroups.Add(new CxSmilesState.PolymerSgroup(keyword, atomset, subscript, supscript)); return(true); } // not supported: crossing bond info (difficult to work out from doc) and bracket orientation return(false); }
private static bool ProcessDataSgroups(CharIter iter, CxSmilesState state) { if (state.dataSgroups == null) { state.dataSgroups = new List <DataSgroup>(4); } var atomset = new List <int>(); if (!ProcessIntList(iter, CommaSeparatorChar, atomset)) { return(false); } if (!iter.NextIf(':')) { return(false); } var beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } string field = Unescape(iter.Substr(beg, iter.pos)); if (!iter.NextIf(':')) { return(false); } beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } string value = Unescape(iter.Substr(beg, iter.pos)); if (!iter.NextIf(':')) { state.dataSgroups.Add(new CxSmilesState.DataSgroup(atomset, field, value, "", "", "")); return(true); } beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } var operator_ = Unescape(iter.Substr(beg, iter.pos)); if (!iter.NextIf(':')) { state.dataSgroups.Add(new CxSmilesState.DataSgroup(atomset, field, value, operator_, "", "")); return(true); } beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } var unit = Unescape(iter.Substr(beg, iter.pos)); if (!iter.NextIf(':')) { state.dataSgroups.Add(new CxSmilesState.DataSgroup(atomset, field, value, operator_, unit, "")); return(true); } beg = iter.pos; while (iter.HasNext() && !IsSgroupDelim(iter.Curr())) { iter.Next(); } string tag = Unescape(iter.Substr(beg, iter.pos)); state.dataSgroups.Add(new CxSmilesState.DataSgroup(atomset, field, value, operator_, unit, tag)); return(true); }
/// <summary> /// Transfers the CXSMILES state onto the CDK atom/molecule data-structures. /// </summary> /// <param name="bldr">chem-object builder</param> /// <param name="atoms">atoms parsed from the molecule or reaction. Reaction molecules are list left to right.</param> /// <param name="atomToMol">look-up of atoms to molecules when connectivity/sgroups need modification</param> /// <param name="cxstate">the CXSMILES state to read from</param> private void AssignCxSmilesInfo(IChemObjectBuilder bldr, IChemObject chemObj, List <IAtom> atoms, Dictionary <IAtom, IAtomContainer> atomToMol, CxSmilesState cxstate) { // atom-labels - must be done first as we replace atoms if (cxstate.atomLabels != null) { foreach (var e in cxstate.atomLabels) { // bounds check if (e.Key >= atoms.Count) { continue; } var old = atoms[e.Key]; var pseudo = bldr.NewPseudoAtom(); var val = e.Value; // specialised label handling if (val.EndsWith("_p", StringComparison.Ordinal)) // pseudo label { val = val.Substring(0, val.Length - 2); } else if (val.StartsWith("_AP", StringComparison.Ordinal)) // attachment point { pseudo.AttachPointNum = ParseIntSafe(val.Substring(3)); } pseudo.Label = val; pseudo.AtomicNumber = 0; pseudo.ImplicitHydrogenCount = 0; var mol = atomToMol[old]; AtomContainerManipulator.ReplaceAtomByAtom(mol, old, pseudo); atomToMol.Add(pseudo, mol); atoms[e.Key] = pseudo; } } // atom-values - set as comment, mirrors Molfile reading behavior if (cxstate.atomValues != null) { foreach (var e in cxstate.atomValues) { atoms[e.Key].SetProperty(CDKPropertyName.Comment, e.Value); } } // atom-coordinates if (cxstate.atomCoords != null) { var numAtoms = atoms.Count; var numCoords = cxstate.atomCoords.Count; var lim = Math.Min(numAtoms, numCoords); if (cxstate.coordFlag) { for (int i = 0; i < lim; i++) { atoms[i].Point3D = new Vector3( cxstate.atomCoords[i][0], cxstate.atomCoords[i][1], cxstate.atomCoords[i][2]); } } else { for (int i = 0; i < lim; i++) { atoms[i].Point2D = new Vector2( cxstate.atomCoords[i][0], cxstate.atomCoords[i][1]); } } } // atom radicals if (cxstate.atomRads != null) { foreach (var e in cxstate.atomRads) { // bounds check if (e.Key >= atoms.Count) { continue; } int count = 0; var aa = e.Value; switch (e.Value) { case CxSmilesState.Radical.Monovalent: count = 1; break; // no distinction in CDK between singled/triplet case CxSmilesState.Radical.Divalent: case CxSmilesState.Radical.DivalentSinglet: case CxSmilesState.Radical.DivalentTriplet: count = 2; break; // no distinction in CDK between doublet/quartet case CxSmilesState.Radical.Trivalent: case CxSmilesState.Radical.TrivalentDoublet: case CxSmilesState.Radical.TrivalentQuartet: count = 3; break; } var atom = atoms[e.Key]; var mol = atomToMol[atom]; while (count-- > 0) { mol.SingleElectrons.Add(bldr.NewSingleElectron(atom)); } } } var sgroupMap = new MultiDictionary <IAtomContainer, Sgroup>(); // positional-variation if (cxstate.positionVar != null) { foreach (var e in cxstate.positionVar) { var sgroup = new Sgroup { Type = SgroupType.ExtMulticenter }; var beg = atoms[e.Key]; var mol = atomToMol[beg]; var bonds = mol.GetConnectedBonds(beg); if (bonds.Count() == 0) { continue; // bad } sgroup.Add(beg); sgroup.Add(bonds.First()); foreach (var endpt in e.Value) { sgroup.Add(atoms[endpt]); } sgroupMap.Add(mol, sgroup); } } // data sgroups if (cxstate.dataSgroups != null) { foreach (var dsgroup in cxstate.dataSgroups) { if (dsgroup.Field != null && dsgroup.Field.StartsWith("cdk:", StringComparison.Ordinal)) { chemObj.SetProperty(dsgroup.Field, dsgroup.Value); } } } // polymer Sgroups if (cxstate.sgroups != null) { foreach (var psgroup in cxstate.sgroups) { var sgroup = new Sgroup(); var atomset = new HashSet <IAtom>(); IAtomContainer mol = null; foreach (var idx in psgroup.AtomSet) { if (idx >= atoms.Count) { continue; } var atom = atoms[idx]; var amol = atomToMol[atom]; if (mol == null) { mol = amol; } else if (amol != mol) { goto C_PolySgroup; } atomset.Add(atom); } if (mol == null) { continue; } foreach (var atom in atomset) { foreach (var bond in mol.GetConnectedBonds(atom)) { if (!atomset.Contains(bond.GetOther(atom))) { sgroup.Add(bond); } } sgroup.Add(atom); } sgroup.Subscript = psgroup.Subscript; sgroup.PutValue(SgroupKey.CtabConnectivity, psgroup.Supscript); switch (psgroup.Type) { case "n": sgroup.Type = SgroupType.CtabStructureRepeatUnit; break; case "mon": sgroup.Type = SgroupType.CtabMonomer; break; case "mer": sgroup.Type = SgroupType.CtabMer; break; case "co": sgroup.Type = SgroupType.CtabCopolymer; break; case "xl": sgroup.Type = SgroupType.CtabCrossLink; break; case "mod": sgroup.Type = SgroupType.CtabModified; break; case "mix": sgroup.Type = SgroupType.CtabMixture; break; case "f": sgroup.Type = SgroupType.CtabFormulation; break; case "any": sgroup.Type = SgroupType.CtabAnyPolymer; break; case "gen": sgroup.Type = SgroupType.CtabGeneric; break; case "c": sgroup.Type = SgroupType.CtabComponent; break; case "grf": sgroup.Type = SgroupType.CtabGraft; break; case "alt": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "ALT"); break; case "ran": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "RAN"); break; case "blk": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "BLO"); break; } sgroupMap.Add(mol, sgroup); C_PolySgroup: ; } } // assign Sgroups foreach (var e in sgroupMap) { e.Key.SetCtabSgroups(new List <Sgroup>(e.Value)); } }
/// <summary> /// Handle fragment grouping of a reaction that specifies certain disconnected components /// are actually considered a single molecule. Normally used for salts, [Na+].[OH-]. /// </summary> /// <param name="rxn">reaction</param> /// <param name="cxstate">state</param> private static void HandleFragmentGrouping(IReaction rxn, CxSmilesState cxstate) { // repartition/merge fragments if (cxstate.fragGroups != null) { const int reactant = 1; const int agent = 2; const int product = 3; // note we don't use a list for fragmap as the indexes need to stay consistent var fragMap = new SortedDictionary <int, IAtomContainer>(); var roleMap = new Dictionary <IAtomContainer, int>(); foreach (var mol in rxn.Reactants) { fragMap.Add(fragMap.Count, mol); roleMap.Add(mol, reactant); } foreach (var mol in rxn.Agents) { fragMap.Add(fragMap.Count, mol); roleMap.Add(mol, agent); } foreach (var mol in rxn.Products) { fragMap.Add(fragMap.Count, mol); roleMap.Add(mol, product); } // check validity of group bool invalid = false; var visit = new HashSet <int>(); foreach (var grouping in cxstate.fragGroups) { var dest = fragMap[grouping[0]]; if (dest == null) { continue; } if (visit.Contains(grouping[0])) { invalid = true; } else { visit.Add(grouping[0]); } for (int i = 1; i < grouping.Count; i++) { if (visit.Contains(grouping[i])) { invalid = true; } else { visit.Add(grouping[i]); } var src = fragMap[grouping[i]]; if (src != null) { dest.Add(src); roleMap[src] = 0; // no-role } } } if (!invalid) { rxn.Reactants.Clear(); rxn.Agents.Clear(); rxn.Products.Clear(); foreach (var mol in fragMap.Values) { var aa = roleMap[mol]; if (aa == reactant) { rxn.Reactants.Add(mol); } else if (aa == product) { rxn.Products.Add(mol); } else if (aa == agent) { rxn.Agents.Add(mol); } } } } }