public string Export(Chem4Word.Model.Model model) { String segment = ""; int atomCount = 0; if (model.Molecules.Select(m => m.Bonds.Count).Max() > 999) { throw new ArgumentOutOfRangeException("Model's bond count exceeds CTAB format limits"); } if (model.Molecules.Select(m => m.Atoms.Count).Max() > 999) { throw new ArgumentOutOfRangeException("Model's atom count exceeds CTAB format limits"); } int startAtom = 0, endAtom = 0; System.Text.StringBuilder SDFileContents = new StringBuilder(); //However many molecues there are, shove them all in the output //Counts line //Atom count padded with spaces foreach (Molecule molecule in model.Molecules) { SDFileContents.Append(ExportMolecule(molecule, atomCount)); } return(SDFileContents.ToString()); }
public Chem4Word.Model.Model Import(object data) { var jsonModel = JsonConvert.DeserializeObject <ModelJSON>(data as string); var newModel = new Chem4Word.Model.Model(); if (jsonModel.m != null) { foreach (var molJson in jsonModel.m) { AddMolecule(molJson, newModel); } } else { var jsonMol = JsonConvert.DeserializeObject <MolJSON>(data as string); AddMolecule(jsonMol, newModel); } return(newModel); }
public string Export(Chem4Word.Model.Model model) { XDocument xd = new XDocument(); XElement root = new XElement(CML.cml + "cml", new XAttribute(XNamespace.Xmlns + "conventions", CML.conventions), new XAttribute(XNamespace.Xmlns + "cml", CML.cml), new XAttribute(XNamespace.Xmlns + "cmlDict", CML.cmlDict), new XAttribute(XNamespace.Xmlns + "nameDict", CML.nameDict), new XAttribute(XNamespace.Xmlns + "c4w", CML.c4w), new XAttribute("conventions", "convention:molecular") ); // Only export if set if (!string.IsNullOrEmpty(model.CustomXmlPartGuid)) { XElement customXmlPartGuid = new XElement(CML.c4w + "customXmlPartGuid", model.CustomXmlPartGuid); root.Add(customXmlPartGuid); } bool relabelRequired = false; // Handle case where id's are null foreach (Molecule molecule in model.Molecules) { if (molecule.Id == null) { relabelRequired = true; break; } foreach (Atom atom in molecule.Atoms) { if (atom.Id == null) { relabelRequired = true; break; } } foreach (Bond bond in molecule.Bonds) { if (bond.Id == null) { relabelRequired = true; break; } } } if (relabelRequired) { model.Relabel(false); } foreach (Molecule molecule in model.Molecules) { root.Add(GetMoleculeElement(molecule)); } xd.Add(root); return(xd.ToString()); }
public Chem4Word.Model.Model Import(object data) { string line; int atomsUnread = 0, bondsUnread = 0; bool countsRead = false, endRead = false; double minY = 0, maxY = 0; bool firstY = true; Chem4Word.Model.Model newModel = new Chem4Word.Model.Model(); Chem4Word.Model.Molecule newMolecule = new Chem4Word.Model.Molecule(); Chem4Word.Model.PeriodicTable pt = new Chem4Word.Model.PeriodicTable(); //Element ce = new Element(); Dictionary <string, Chem4Word.Model.Atom> atomsDict = new Dictionary <string, Chem4Word.Model.Atom>(); Dictionary <string, Chem4Word.Model.Bond> bondsDict = new Dictionary <string, Chem4Word.Model.Bond>(); int atomNumber = 0, bondNumber = 0; int lineNumber = 0; using (StringReader reader = new StringReader(data.ToString())) { while ((line = reader.ReadLine()) != null) { if (CountsLine.IsMatch(line)) { if (lineNumber == 3) { Match m = CountsLine.Match(line); var atomCount = m.Groups["atoms"].Captures[0]; var bondCount = m.Groups["bonds"].Captures[0]; int.TryParse(atomCount.ToString(), out atomsUnread); int.TryParse(bondCount.ToString(), out bondsUnread); countsRead = true; } else { throw new FileFormatException("Counts line misplaced"); } } else { if (countsRead) { if (AtomLine.IsMatch(line)) { Match m = AtomLine.Match(line); Capture xCoord, yCoord, zCoord, element, isotope = null, charge = null, stereo, hydrogens; //read the atom details xCoord = m.Groups["x"].Captures[0]; yCoord = m.Groups["y"].Captures[0]; zCoord = m.Groups["z"].Captures[0]; //not used element = m.Groups["element"].Captures[0]; if (m.Groups["isotope"].Success) { isotope = m.Groups["isotope"].Captures[0]; } if (m.Groups["charge"].Success) { charge = m.Groups["charge"].Captures[0]; } if (m.Groups["stereo"].Success) { stereo = m.Groups["stereo"].Captures[0]; } if (m.Groups["hydrogens"].Success) { hydrogens = m.Groups["hydrogens"].Captures[0]; } //Create a new atom instance, set its properties and add it to our collection Atom atom = new Atom(); atom.Position = Point.Parse($"{xCoord.Value},{yCoord.Value}"); //Keep track of the maximum and minimum Y coordinate values so we can invert them later if (firstY) { minY = atom.Position.Y; maxY = atom.Position.Y; firstY = false; } else { if (minY > atom.Position.Y) { minY = atom.Position.Y; } if (maxY < atom.Position.Y) { maxY = atom.Position.Y; } } atom.Element = Globals.PeriodicTable.Elements[element.ToString().Trim()]; //need to make sure this is the element name //Isotope offset. Do we need to add the most common or default isotope mass to get the right thing here? //CD- leave the isotopes for now; int tempIso = 0; int.TryParse(isotope.ToString(), out tempIso); if (tempIso > 0) { atom.IsotopeNumber = tempIso; } //it is awkward that we cannot pass the property into TryParse.... //we could write a function but then we would need a different one for every type. //Charge int tempCharge = 0; int.TryParse(charge.ToString(), out tempCharge); //Translate the Molfile charge/radical enumeration to atom formal charge and doublet radical properties atom.FormalCharge = FormalChargeFromMolfile(tempCharge); atom.DoubletRadical = DoubletRadicalFromMolfile(tempCharge); //Stereo - ignore for now?? //Hydrogens - ignore for now - saturation property mops this up in the model?? //Number the atom by incrementing the previous atom's number, add the atom to the dictionary //keyed using the number converted to a string (why?) atomNumber++; atom.Id = atomNumber.ToString(); atomsDict.Add(atom.Id, atom); //decrement the count of atom rows still to be read atomsUnread--; } else { if (atomsUnread == 0) { if (BondLine.IsMatch(line)) { Match m = BondLine.Match(line); //read the bond details var atom1 = m.Groups["atom1"].Captures[0]; var atom2 = m.Groups["atom2"].Captures[0]; var order = m.Groups["order"].Captures[0]; var stereo = m.Groups["stereo"].Captures[0]; //var topochain = m.Groups["topochain"].Captures[0]; //var toporing = m.Groups["toporing"].Captures[0]; //parse into object values and add //Create a new bond instance, set its properties and add it to our collection Bond bond = new Bond(); //Get start and end atoms, and bond order int atom1id = 0, atom2id = 0, orderId = 0; int.TryParse(atom1.ToString(), out atom1id); int.TryParse(atom2.ToString(), out atom2id); int.TryParse(order.ToString(), out orderId); bond.StartAtom = atomsDict[atom1id.ToString()]; bond.EndAtom = atomsDict[atom2id.ToString()]; bond.Order = Bond.OrderValueToOrder(orderId); //bond stereo int bondStereo = 0; int.TryParse(stereo.ToString(), out bondStereo); if (bond.Order == Bond.OrderSingle) { bond.Stereo = BondStereoFromMolfile(bondStereo); } /* we don't care about this we always use the coordinates I think?? * else * { * if (bond.Order == Enums.BondOrder.Double) * { * //0 is use x- y- z- coordinates from ataom block to determine Z or E * //3 means either Z or E * } * } */ //So... what about allenes?? //CD - um...err.... //topochain - ignore for now because the ring detection sorts it out?? //CD- Yup //toporing - ignore for now because the ring detection sorts it out?? //CD- Likewise //Number the bond by incrementing the previous bond's number, add the bond to the dictionary //keyed using the number converted to a string (why?) //CD - you could always try Dictionary<int, Chem4Word.Model.Atom> bondsDict and see what happens bondNumber++; bond.Id = bondNumber.ToString(); bondsDict.Add(bond.Id, bond); //decrement the count of bond rows still to be read bondsUnread--; } //if bonds line } //if all atoms read } //if atoms line } //if counts read line } //if counts line endRead = EndLine.IsMatch(line); if (endRead) { lineNumber = 0; break; } lineNumber++; } //while //Values of > 0 for the integer variable atomsUnread or bondsUnread would indicate not enough rows in the molfile. //Values of false for readCounts or endRead would also be bad now we have run out of string } //using //Check that everything read consistently if (!countsRead) { //What kind of exception do we want to raise, or error to log throw new System.FormatException("Counts line missing."); } if (!endRead) { //What kind of exception do we want to raise, or error to log throw new System.FormatException("End of Molfile line not found."); } if (atomsUnread != 0) { //What kind of exception do we want to raise, or error to log throw new System.FormatException("Number of atom lines inconsistent with counts line."); } if (bondsUnread != 0) { //What kind of exception do we want to raise, or error to log throw new System.FormatException("Number of bond lines inconsistent with counts line."); } if (atomsDict.Count == 0) { //Something is wrong if there are no atoms. Having no bonds is conceivably intentional. throw new System.FormatException("No atoms were read from the Molfile during import."); } //Sum the minumum and maximum Y coordinates and later subtract each atom's Y coordinate from this value double yReversal = minY + maxY; //Add all the atoms and bonds to a new molecule, doing this outside the loop that reads them from the file Molecule newMol = new Chem4Word.Model.Molecule(); //Can we really just chuck them in like this or will it fail validation checks?? foreach (Atom atom in atomsDict.Values) { //Set the atom's coordinates to the same existing x value, but y subtracted from the sum of the maximum and minumum y values atom.Position = Point.Parse($"{atom.Position.X},{yReversal - atom.Position.Y}"); //Add the atom to the model newMol.Atoms.Add(atom); } foreach (Bond bond in bondsDict.Values) { newMol.Bonds.Add(bond); } //Add the molecule to the model newModel.Molecules.Add(newMol); //Make sure it is valid newModel.RebuildMolecules(); return(newModel); }