Пример #1
0
        public static bool pairwiseangle(OBMol mol, ref double pangle)
        {
            VectorVecInt mapping = new VectorVecInt();

            if (findcarboxylates(mol, ref mapping))
            {
                double        sum    = 0;
                List <double> angles = new List <double>();
                if (mapping.Count > 1)
                {
                    OBAtom c1 = mol.GetAtom(mapping[0][1]);
                    OBAtom a1 = mol.GetAtom(mapping[0][3]);
                    OBAtom c2 = mol.GetAtom(mapping[1][1]);
                    OBAtom a2 = mol.GetAtom(mapping[1][3]);

                    pangle = pairwiseangle(c1, a1, c2, a2);
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                return(false);
            }
        }
Пример #2
0
        /// <summary>
        /// Calculate the angle between 2 carboxylates
        /// </summary>
        public static double CalAngle(OBMol mol)
        {
            var mapping = OBFunctions.findcarboxylates(mol);
            var carbA   = mol.GetAtom(mapping[0][1]); // carbon in carboxylate
            var aA      = mol.GetAtom(mapping[0][3]); // atom that the carbon connects to
            var carbB   = mol.GetAtom(mapping[1][1]);
            var aB      = mol.GetAtom(mapping[1][3]);
            var pAngle  = OBFunctions.pairwiseangle(carbA, aA, carbB, aB); // angle between carboxylates

            return(pAngle);
        }
        public static OBMol molecule_merge(OBMol mol1, OBAtom a, OBMol mol2, OBAtom b)
        {
            //takes two molecules and two atoms in each molecule and
            //assuming atoms are in the same place, delete one atom and copy bond to the other
            //a
            //foreach(OBBond bond in b.Bonds())
            //{

            //}

            int keepid     = Convert.ToInt32(a.GetIdx());
            int todeleteid = Convert.ToInt32(b.GetIdx());
            //var bonds = b.Bonds();
            List <OBBond> bondlist = new List <OBBond>();

            foreach (OBBond bon in b.Bonds())
            {
                bondlist.Add(bon);
            }
            OBBond bond      = bondlist[0];
            int    connectid = (int)bond.GetNbrAtomIdx(b);
            int    prevatms  = (int)mol1.NumAtoms(); //number of atoms before we combine things

            //OBMol mol3 = new OBMol ();

            mol1 = addmol(mol2, mol1);
            mol1.BeginModify();
            OBAtom keep      = mol1.GetAtom(keepid);
            OBAtom todelete  = mol1.GetAtom(todeleteid + prevatms);
            OBAtom toconnect = mol1.GetAtom(connectid + prevatms);
            OBBond newbond   = new OBBond();

            newbond.SetBegin(keep);
            newbond.SetEnd(toconnect);
            newbond.SetBO(1);
            mol1.AddBond(newbond);
            mol1.DeleteAtom(todelete);
            //OBAtom= atom1
            //var a = map2[0];
            //int c1 = (int)(map1[1]);
            //int c2 = (int)(map2[1] + prevatms);
            //int h1 = (int)(map1[0]);
            ///int h2 = (int)(map2[0] + prevatms);
            //OBAtom carbon1 = mol1.GetAtom(c1);
            //OBAtom carbon2 = mol1.GetAtom(c2);
            //OBAtom hydrogen1 = mol1.GetAtom(h1);
            ///OBAtom hydrogen2 = mol1.GetAtom(h2);
            //OBBuilder.Connect(mol1, c1, c2);//connect fragments
            //mol1.DeleteAtom(hydrogen1);
            //mol1.DeleteAtom(hydrogen2);
            mol1.EndModify();
            return(mol1);
        }
        public static OBMol addmol2(OBMol source, OBMol dest)
        {
            //combines two OBMols into one molecule
            // this is designed to do the same thing as the += operator in mol.cpp, in other words this implements functionality present in openbabel, but not in the C# api
            //modifies atom and bond indices in the process
            //dest.BeginModify();
            uint prevatms = dest.NumAtoms();
            uint nextatms = source.NumAtoms();
            uint acount   = prevatms;
            uint bcount   = dest.NumBonds();

            // First, handle atoms and bonds
            foreach (OBAtom atom in source.Atoms())
            {
                atom.SetId(acount); ////Need to remove ID which relates to source mol rather than this mol// But in the C++ it had a NoId thing I couldn't figure out
                dest.AddAtom(atom);
                acount++;
                //var foooo = dest.Atoms ();
            }
            //writeatominfotoscreen (dest);
            foreach (OBBond bond in source.Bonds())
            {
                bond.SetId(bcount);
                OBBond b = new OBBond();
                //b.SetBegin(dest.GetAtom((int)(bond.GetBeginAtomIdx() + prevatms)));
                //b.SetEnd(dest.GetAtom((int)(bond.GetEndAtomIdx() + prevatms)));
                b.Set(0, dest.GetAtom((int)(bond.GetBeginAtomIdx() + prevatms)),
                      dest.GetAtom((int)(bond.GetEndAtomIdx() + prevatms)), (int)bond.GetBO(), (int)bond.GetFlags());
                if (bond.HasData("trueBO"))
                {
                    b.CloneData(bond.GetData("trueBO")); //clone true bond order so we can eventually do MOF-UFF
                }
                dest.AddBond(b);
                //dest.AddBond( (int)bond.GetBO(), (int)bond.GetFlags());

                bcount++;
            }

            //don't really understand what they mean by residues
            //I think it's an amino acid or nucleotide so you can build up proteins and stuff?
            //don't think I'm going to use them either, but it might be useful


            //dest.EndModify(); this tends to mangle up all the atom ids, which is bad

            return(dest);
        }
Пример #5
0
        /// <summary>
        /// Whether the candidate has enough carboxylates and conforms to angle- and carboxylate-blocking constraints.
        /// </summary>
        public static bool CanCalculateReward(OBMol mol)
        {
            var mapping = OBFunctions.findcarboxylates(mol);

            if (mapping.Count < 2)
            {
                return(false);
            }

            var carbA  = mol.GetAtom(mapping[0][1]); // carbon in carboxylate
            var aA     = mol.GetAtom(mapping[0][3]); // atom that the carbon connects to
            var carbB  = mol.GetAtom(mapping[1][1]);
            var aB     = mol.GetAtom(mapping[1][3]);
            var pAngle = OBFunctions.pairwiseangle(carbA, aA, carbB, aB); // angle between carboxylates

            return(!OBFunctions.carboxylatesBlocked(mol, aA, carbA, aB, carbB) && pAngle >= AngleFloor);
        }
Пример #6
0
        public static bool averagepairwiseangle(OBMol mol, ref double avgangle)
        {
            VectorVecInt mapping = new VectorVecInt();

            if (findcarboxylates(mol, ref mapping))
            {
                double        sum    = 0;
                List <double> angles = new List <double>();
                if (mapping.Count > 1)
                {
                    OBAtom c1 = mol.GetAtom(mapping[0][1]);
                    OBAtom a1 = mol.GetAtom(mapping[0][3]);
                    OBAtom c2 = mol.GetAtom(mapping[1][1]);
                    OBAtom a2 = mol.GetAtom(mapping[1][3]);

                    double pangle = pairwiseangle(c1, a1, c2, a2);
                    angles.Add(pangle);
                    sum += pangle;
                    OBForceField MMFF94 = OBForceField.FindForceField("MMFF94");
                    MMFF94.Setup(mol);

                    for (int i = 0; i < 300; i++)
                    {
                        MMFF94.MolecularDynamicsTakeNSteps(1, 300, 1.2, 1);
                        MMFF94.GetCoordinates(mol);
                        pangle = pairwiseangle(c1, a1, c2,
                                               a2); //this function is sufficiently fast that it does not need to be optimized, molecular dynamics takes about 15 milliseconds per step, this take practically nothing
                        angles.Add(pangle);
                        sum += pangle;
                    }
                    avgangle = sum / angles.Count;
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            else
            {
                return(false);
            }
        }
 public static bool updatexyz(OBMol mol, double[,] xyz)
 {
     //OBMol newmol = new OBMol(mol);
     for (int i = 0; i < mol.NumAtoms(); i++)
     {
         OBAtom a = mol.GetAtom(i + 1);
         a.SetVector(xyz[i, 0], xyz[i, 1], xyz[i, 2]);
         //OBVector3 vec = OpenBabelFunctions.OBFunctions.dubs2obvec(xyz[i]);
         //a.SetVector(vec);
     }
     return(true);
 }
        public static OBMol merge_atoms_at_same_location(OBMol mol)
        {
            double radius = 0.1;
            //when we make a unit_cell by copy and pasting base cases we end up with atoms at the same location that need to be merged
            //assumes only two atoms overlap at a time
            Dictionary <int, int> tomerge = new Dictionary <int, int>();

            foreach (OBAtom a in mol.Atoms())
            {
                if (!tomerge.ContainsKey((int)a.GetIdx()))
                {
                    foreach (OBAtom b in mol.Atoms())
                    {
                        if (a.GetIdx() == b.GetIdx())
                        {
                            continue;
                        }
                        else
                        {
                            double len =
                                Math.Round(StarMath.norm2(obvec2dubs(OBVector3.Sub(a.GetVector(), b.GetVector()))),
                                           7); //gets length
                            if (len <= radius)
                            {
                                tomerge.Add((int)b.GetIdx(), (int)a.GetIdx());
                            }
                        }
                    }
                }
            }
            int atomsdeleted = 0;

            foreach (int key in tomerge.Keys)
            {
                mol = molecule_merge(mol, mol.GetAtom(key - atomsdeleted), mol.GetAtom(tomerge[key] - atomsdeleted));
                atomsdeleted++;
            }
            return(mol);
        }
        /// <summary>
        /// Calculates the approximate surface area contributions for each atom
        /// </summary>
        /// <param name="mol"></param>
        /// <param name="includeHydrogens">Include hydrogens in calculation</param>
        /// <returns></returns>
        public static double[] LabuteASAContributions(this OBMol mol, bool includeHydrogens = true)
        {
            var numAtoms = mol.NumAtoms();
            var Vi       = new double[numAtoms + 1].Zero().ToArray();
            var rads     = new double[numAtoms + 1].Zero().ToArray();

            rads[0] = AtomicConstants.GetRBO(1);
            double Ri, Rj, Bij, Dij;


            // OpenBabel atom numbering is 1-based
            for (var i = 1; i <= numAtoms; i++)
            {
                rads[i] = AtomicConstants.GetRBO(mol.GetAtom(i).GetAtomicNum());
            }

            foreach (var bond in mol.Bonds())
            {
                var beginAtom = bond.GetBeginAtomIdx();
                var endAtom   = bond.GetEndAtomIdx();
                Ri = rads[beginAtom];
                Rj = rads[endAtom];
                var bondOrder = bond.GetBondOrder();
                Bij = !bond.IsAromatic() ? Ri + Rj - BondConstants.BondScaleFactors[bondOrder] : Ri + Rj - BondConstants.BondScaleFactors[0];

                Dij            = Math.Min(Math.Max(Math.Abs(Ri - Rj), Bij), Ri + Rj);
                Vi[beginAtom] += Rj * Rj - Math.Pow((Ri - Dij), 2) / Dij;
                Vi[endAtom]   += Ri * Ri - Math.Pow((Rj - Dij), 2) / Dij;
            }

            if (includeHydrogens)
            {
                Rj = rads[0];
                for (var i = 1; i <= numAtoms; i++)
                {
                    Ri     = rads[i];
                    Bij    = Ri + Rj;
                    Dij    = Math.Min(Math.Max(Math.Abs(Ri - Rj), Bij), Ri + Rj);
                    Vi[i] += Rj * Rj - Math.Pow((Ri - Dij), 2) / Dij;
                    Vi[0] += Ri * Ri - Math.Pow((Rj - Dij), 2) / Dij;
                }
            }
            for (var i = 0; i <= numAtoms; i++)
            {
                Ri    = rads[i];
                Vi[i] = 4 * Math.PI * Math.Pow(Ri, 2) - Math.PI * Ri * Vi[i];
            }
            return(Vi);
        }
Пример #10
0
        public static designGraph updatepositions(designGraph graph, OBMol mol)
        {
            int count = graph.nodes.Count;

            for (int i = 0; i <= count - 1; i++)
            {
                OBAtom a = mol.GetAtom(i + 1);
                //a.GetVector
                node n = graph.nodes[i];
                n.X = a.GetX() / scale;
                n.Y = a.GetY() / scale;
                n.Z = a.GetZ() / scale;
            }
            return(graph);
        }
        /// <summary>
        /// Calculates the Hk deltas for the molecule
        /// </summary>
        /// <param name="mol"></param>
        /// <param name="skipHydrogens"></param>
        /// <returns></returns>
        public static double[] HkDeltas(this OBMol mol, bool skipHydrogens = true)
        {
            var numAtoms = mol.NumAtoms();
            var result   = new double[numAtoms];

            for (var i = 1; i <= numAtoms; i++)
            {
                var atom         = mol.GetAtom(i);
                var atomicNumber = atom.GetAtomicNum();
                if (atomicNumber > 1)
                {
                    result[i - 1] = atomicNumber <= 10 ? (double)atom.ValenceNumber() : (double)(atom.ValenceNumber() / (atomicNumber - atom.NOuterShellElectrons() - 1));
                }
                else if (!skipHydrogens)
                {
                    result[i - 1] = 0d;
                }
            }
            return(result);
        }
Пример #12
0
    public void parsePDB()
    {
        // for determining hydrogen bonds
        List <Vector3> hBondAcceptors = new List <Vector3> ();
        List <Vector3> hBondDonors    = new List <Vector3> ();


parseMol:                                                       // iteration start if multiple input files are used

        BezierSpline[] ribbonSplineList = new BezierSpline[30]; //for drawing protein lines (30 is max number of chains)


        int numAtoms = (int)mol.NumAtoms() + 1;


        atoms = new GameObject[numAtoms];


        int maxHelix = 500;

        radius = new float[maxHelix, numAtoms];

        int ribbonChainIndex = 0;


        OBElementTable table = new OBElementTable();

        int currentChain = -1;


        OBAtom[] alphaCarbons     = new OBAtom[numAtoms / 4];
        int      alphaCarbonCount = 0;

        List <Vector3> atomPositions = new List <Vector3> ();
        List <string>  atomElements  = new List <string> ();


        for (int i = 1; i < numAtoms; i++)
        {
            OBAtom    atom = mol.GetAtom(i);
            string    tag;
            OBResidue res      = atom.GetResidue();
            Vector3   position = new Vector3((float)atom.GetX(), (float)atom.GetY(), (float)atom.GetZ());
            string    elem     = table.GetSymbol((int)atom.GetAtomicNum()).ToUpper();


            //checks for pharmacophore labels
            if (res.GetName() == "ACC" || res.GetName() == "FOB" || res.GetName() == "POS" || res.GetName() == "NEG" || res.GetName() == "RNG" || res.GetName() == "DON")
            {
                if (atom.IsHydrogen())                //added hydrogens gets the same resName and has to be ignored
                {
                    continue;
                }
                renderPharmacophore = true;
            }
            else
            {
                renderPharmacophore = false;
            }

            // pharmacophores
            if (res.GetName() == "EXC")
            {
                if (atom.IsHydrogen())                //added hydrogens gets the same resName and has to be ignored
                {
                    continue;
                }
                renderEXC           = true;
                renderPharmacophore = true;
            }
            else
            {
                renderEXC = false;
            }

            //creates the atom object
            atoms[i] = createAtomType(elem, position);

            int n;
            if (int.TryParse(res.GetName(), out n))
            {
                if (CollectionOfLigands == null)
                {
                    CollectionOfLigands = new List <List <GameObject> >();
                    CollectionOfLigands.Add(new List <GameObject>());
                }
                while (n + 1 > CollectionOfLigands.Count)
                {
                    CollectionOfLigands.Add(new List <GameObject>());
                }
                CollectionOfLigands[n].Add(atoms[i]);
            }

            if (res.GetResidueProperty(9))             //water
            {
                tag = "water";
            }
            else if (res.GetAtomProperty(atom, 4) || (atom.IsHydrogen() && !res.GetResidueProperty(5))) //ligand
            {
                if (res.GetResidueProperty(5))                                                          //ion (should be 3 but a bug in openbabel labels ions as protein, thats why the check has to be done inside the ligand check)
                {
                    tag = "ion";
                }
                else
                {
                    TextMesh lText = atoms [i].transform.Find("Text").GetComponent <TextMesh> ();
                    lText.color = textColor;
                    if (renderPharmacophore)
                    {
                        tag            = "hetatms";              //make sure pharmacophores always show
                        lText.text     = res.GetName() + ":" + res.GetIdx();
                        lText.fontSize = labelFontSize;
                    }
                    else
                    {
                        tag = "hetatmbs";
                        if (ligandText)
                        {
                            lText.text     = elem + ":" + i.ToString();
                            lText.fontSize = labelFontSize;
                        }
                    }


                    if (sphere)
                    {
                        atoms [i].transform.localScale *= 4;
                    }

                    if (atom.IsHbondAcceptor())
                    {
                        foreach (Vector3 candidatePos in hBondDonors)
                        {
                            checkHBond(candidatePos, position);
                        }
                    }

                    if (atom.IsHbondDonor())
                    {
                        foreach (Vector3 candidatePos in hBondAcceptors)
                        {
                            checkHBond(candidatePos, position);
                        }
                    }
                }
            }
            else               //protein
            {
                tag = "balls";

                atomPositions.Add(position);

                atomElements.Add(elem);

                if (atom.IsHbondAcceptor())
                {
                    hBondAcceptors.Add(position);
                }
                else
                {
                    hBondDonors.Add(position);
                }

                if (res.GetAtomProperty(atom, 0))                //alpha carbon
                {
                    alphaCarbons[alphaCarbonCount] = atom;
                    if (alphaCarbonCount > 6)                   //check if the ribbon is alpha helix using torsion angles
                    {
                        double torsion     = mol.GetTorsion(atom, alphaCarbons[alphaCarbonCount - 1], alphaCarbons[alphaCarbonCount - 2], alphaCarbons[alphaCarbonCount - 3]);
                        double prevTorsion = mol.GetTorsion(alphaCarbons[alphaCarbonCount - 4], alphaCarbons[alphaCarbonCount - 5], alphaCarbons[alphaCarbonCount - 6], alphaCarbons[alphaCarbonCount - 7]);
                        double torsionSum  = torsion + prevTorsion;
                        if (torsionSum > 99 && torsionSum < 111)
                        {
                            for (int j = ribbonChainIndex - 7; j <= ribbonChainIndex; j++)
                            {
                                radius [currentChain, j] = 1.5f;                                        //alpha helix
                            }
                        }
                        else
                        {
                            radius [currentChain, ribbonChainIndex] = 0.5f;
                        }
                    }
                    alphaCarbonCount++;


                    if (proteinText)                       //only displays text on alpha carbons
                    {
                        TextMesh pText = atoms [i].transform.Find("Text").GetComponent <TextMesh> ();
                        pText.color    = textColor;
                        pText.text     = res.GetName() + " -" + res.GetNumString();
                        pText.fontSize = labelFontSize;
                    }

                    int tempChain = (int)res.GetChainNum();
                    if (tempChain != currentChain)
                    {
                        currentChain     = tempChain;
                        ribbonChainIndex = 0;
                    }


                    //add points for ribbon rendering
                    addBezierPoint(ribbonSplineList, currentChain, ribbonChainIndex, position);



                    ribbonChainIndex++;
                }
            }

            atoms[i].transform.tag = tag;
            centerPos += position;
            centerCount++;
        }

        //createSurface (atomPositions,atomElements);
        Debug.Log(hBondAcceptors.Count + " " + hBondDonors.Count);
        //evaluate bonds
        for (int i = 0; i < mol.NumBonds(); i++)
        {
            OBBond    bond   = mol.GetBond(i);
            OBAtom    atom   = mol.GetAtom((int)bond.GetBeginAtomIdx());
            OBResidue res    = atom.GetResidue();
            bool      ligand = res.GetAtomProperty(atom, 4) || (atom.IsHydrogen() && !res.GetResidueProperty(5));
            if (ligand && sphere)            //no ligand bonds if display mode is CPK sphere
            {
                continue;
            }
            try {
                connect(atoms[bond.GetBeginAtomIdx()], atoms[bond.GetEndAtomIdx()], bond);
            } catch (System.Exception e) {
                Debug.Log(e);
            }
        }

        //handle pharmacophore vectors
        if (mol.HasData("VECTOR"))
        {
            string[] vectors = mol.GetData("VECTOR").GetValue().Split(new string[] { "\n" }, StringSplitOptions.None);
            foreach (string vector in vectors)
            {
                string[] idx = vector.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
                try {
                    Vector3 pos = new Vector3(float.Parse(idx[1]), float.Parse(idx[2]), float.Parse(idx[3]));
                    int     id  = int.Parse(idx[0]);
                    drawVector(atoms[id], pos, mol.GetAtom(id).GetResidue().GetName());
                } catch (System.Exception e) {
                    Debug.Log(e);
                }
            }
        }


        //render protein ribbons
        drawRibbons(ribbonSplineList, "ribbons");          //must be before alpha due to indexing
        drawRibbons(ribbonSplineList, "alpha");

        // check for separate ligand file
        if (file.Contains("protein"))
        {
            file = file.Replace("protein", "ligand");
            string file1 = file.Replace("." + extension, ".sdf");          // .+extension incase the format would also appear in the file name
            string file2 = file.Replace("." + extension, ".mol2");
            if (File.Exists(file1))
            {
                readMol(file);
                goto parseMol;
            }
            else if (File.Exists(file2))
            {
                readMol(file);
                goto parseMol;
            }
        }

        center = createObject(new GameObject(), centerPos / centerCount);           //the center of the pdb
        show();
    }
Пример #13
0
        public static OBMol addmol(OBMol source, OBMol dest)
        {
            //combines two OBMols into one molecule
            // this is designed to do the same thing as the += operator in mol.cpp, in other words this implements functionality present in openbabel, but not in the C# api
            dest.BeginModify();
            uint prevatms = dest.NumAtoms();
            uint nextatms = source.NumAtoms();

            // First, handle atoms and bonds
            foreach (OBAtom atom in source.Atoms())
            {
                atom.SetId(0); ////Need to remove ID which relates to source mol rather than this mol// But in the C++ it had a NoId thing I couldn't figure out
                dest.AddAtom(atom);
                //var foooo = dest.Atoms ();
            }
            //writeatominfotoscreen (dest);
            foreach (OBBond bond in source.Bonds())
            {
                bond.SetId(0);

                dest.AddBond((int)(bond.GetBeginAtomIdx() + prevatms), (int)(bond.GetEndAtomIdx() + prevatms),
                             (int)bond.GetBO(), (int)bond.GetFlags());
            }

            //don't really understand what they mean by residues
            //I think it's an amino acid or nucleotide so you can build up proteins and stuff?
            //don't think I'm going to use them either, but it might be useful

            foreach (OBResidue residue in source.Residues())
            {
                OBResidue newres = new OBResidue();
                dest.AddResidue(newres);

                OBResidueAtomIter ai = new OBResidueAtomIter(residue);
                //dammit why didn't they implement a residue.atoms sort of thing? I don't want to play with enumerators
                ////#define FOR_ATOMS_OF_RESIDUE(a,r) for( OBResidueAtomIter a(r); a; ++a )
                while (ai.MoveNext())
                {
                    OBAtom resatom = new OBAtom();
                    resatom = ai.Current;
                    // This is the equivalent atom in our combined molecule
                    OBAtom atom = dest.GetAtom((int)(resatom.GetIdx() + prevatms));
                    // So we add this to the last-added residue
                    // (i.e., what we just copied)
                    //[dest.NumResidues () - 1]

                    var res = dest.Residues().GetEnumerator();
                    while (!res.MoveNext())
                    {
                    }                           //move to the last residue
                    res.Current.AddAtom(atom);

                    //var item = dest.Cast<RMSRequestProcessor.RMSMedia> ().ElementAt (1);
                }


                //for(OBAtom resatom in )
            }
            dest.EndModify();

            return(dest);
        }
Пример #14
0
        public MolStat GetMolStat()
        {
            /* 统计原子 */
            uint _numAtoms    = mol.NumAtoms();       //所有原子总数
            uint _numHvyAtoms = mol.NumHvyAtoms();    //所有除去氢原子的原子总数
            int  _totalCharge = mol.GetTotalCharge(); //总电荷数

            int n_C     = 0;                          // number of carbon atoms 碳原子总数
            int n_C1    = 0;                          // number of carbon atoms with sp1 hybridization sp1杂化的碳原子数
            int n_C2    = 0;                          // number of carbon atoms with sp1 hybridization sp2杂化的碳原子数
            int n_CHB1p = 0;                          // number of carbon atoms with at least 1 bond to a hetero atom 至少连接一个杂原子的碳原子数
            int n_CHB2p = 0;                          // number of carbon atoms with at least 2 bonds to a hetero atom 至少连接两个个杂原子的碳原子数
            int n_CHB3p = 0;                          // number of carbon atoms with at least 3 bonds to a hetero atom 至少连接三个杂原子的碳原子数
            int n_CHB4  = 0;                          // number of carbon atoms with 4 bonds to a hetero atom 连接了4个杂原子的碳原子数
            int n_O     = 0;                          // number of oxygen atoms 氧原子数
            int n_O2    = 0;                          // number of sp2-hybridized oxygen atoms sp2杂化的氧原子数
            int n_O3    = 0;                          // number of sp3-hybridized oxygen atoms sp3杂化的氧原子数
            int n_N     = 0;                          // number of nitrogen atoms 氮原子数
            int n_N1    = 0;                          // number of sp-hybridized nitrogen atoms sp杂化的氮原子数
            int n_N2    = 0;                          // number of sp2-hybridized nitrogen atoms sp2杂化的氮原子数
            int n_N3    = 0;                          // number of sp3-hybridized nitrogen atoms sp3杂化的氮原子数
            int n_S     = 0;                          // number of sulfur atoms 硫原子数
            int n_SeTe  = 0;                          // total number of selenium and tellurium atoms 硒和碲原子的数目
            int n_F     = 0;                          // number of fluorine atoms 氟原子的数目
            int n_Cl    = 0;                          // number of chlorine atoms 氯原子的数目
            int n_Br    = 0;                          // number of bromine atoms 溴原子的数目
            int n_I     = 0;                          // number of iodine atoms 碘原子的数目
            int n_P     = 0;                          // number of phosphorus atoms 磷原子的数目
            int n_B     = 0;                          // number of boron atoms 硼原子的数目
            int n_Met   = 0;                          // total number of metal atoms 金属原子的数目
            int n_X     = 0;                          // total number of "other" atoms (not listed above) and halogens 其他原子的数目,除了以上列出的之外。

            for (int i = 1; i <= _numAtoms; i++)
            {
                OBAtom _atom          = mol.GetAtom(i);
                uint   _AtomicNum     = _atom.GetAtomicNum();     //原子种类,周期表序号
                uint   _hyb           = _atom.GetHyb();           //原子的杂化形态,1 for sp1,  2 for sp2, 3 for sp3
                uint   _HeteroValence = _atom.GetHeteroValence(); //连接在这个原子上杂(非氢,碳)原子的数目
                //uint _HeteroValence = _atom.GetHeteroDegree();  //For openbabel 3.0

                if (_AtomicNum == 6) //碳原子
                {
                    n_C++;

                    switch (_hyb)
                    {
                    case 1:
                        n_C1++;
                        break;

                    case 2:
                        n_C2++;
                        break;

                    default:
                        break;
                    }

                    if (_HeteroValence >= 4)
                    {
                        n_CHB4++;
                    }

                    if (_HeteroValence >= 3)
                    {
                        n_CHB3p++;
                    }

                    if (_HeteroValence >= 2)
                    {
                        n_CHB2p++;
                    }

                    if (_HeteroValence >= 1)
                    {
                        n_CHB1p++;
                    }
                }
                else if (_AtomicNum == 8)
                {
                    n_O++;
                    switch (_hyb)
                    {
                    case 2:
                        n_O2++;
                        break;

                    case 3:
                        n_O3++;
                        break;

                    default:
                        break;
                    }
                }
                else if (_AtomicNum == 7)
                {
                    n_N++;
                    switch (_hyb)
                    {
                    case 1:
                        n_N1++;
                        break;

                    case 2:
                        n_N2++;
                        break;

                    case 3:
                        n_N3++;
                        break;

                    default:
                        break;
                    }
                }
                else if (_AtomicNum == 16)
                {
                    n_S++;
                }
                else if (_AtomicNum == 34 || _AtomicNum == 52)
                {
                    n_SeTe++;
                }
                else if (_AtomicNum == 9)
                {
                    n_F++;
                }
                else if (_AtomicNum == 17)
                {
                    n_Cl++;
                }
                else if (_AtomicNum == 35)
                {
                    n_Br++;
                }
                else if (_AtomicNum == 53)
                {
                    n_I++;
                }
                else if (_AtomicNum == 15)
                {
                    n_P++;
                }
                else if (_AtomicNum == 5)
                {
                    n_B++;
                }
                else if ((_AtomicNum >= 3 && _AtomicNum <= 4) || (_AtomicNum >= 11 && _AtomicNum <= 13) || (_AtomicNum >= 19 && _AtomicNum <= 32) || (_AtomicNum >= 37 && _AtomicNum <= 51) || (_AtomicNum >= 55 && _AtomicNum <= 84) || (_AtomicNum >= 87))
                {
                    n_Met++;
                }
            }

            n_X = (int)_numHvyAtoms - n_B - n_C - n_Met - n_N - n_O - n_P - n_S - n_SeTe;

            /* 统计化学键 */

            uint _numBonds = mol.NumBonds(); //所有化学键的总数

            int n_b1     = 0;                // number of single bonds 单键的个数
            int n_b1_NoH = 0;                // number of none C-H single bonds 不含氢的单键个数
            int n_b2     = 0;                // number of double bonds 双键的个数
            int n_b3     = 0;                // number of triple bonds 三键的个数
            int n_bar    = 0;                // number of aromatic bonds 芳香键的个数
            int n_C1O    = 0;                // number of C-O single bonds 碳-氧单键的个数
            int n_C2O    = 0;                // number of C=O double bonds 碳=氧双键的个数
            int n_CN     = 0;                // number of C/N bonds (any type) 任何类型的碳氮键的个数
            int n_XY     = 0;                // number of heteroatom/heteroatom bonds (any type) 任何类型的杂原子之间化学健的个数

            for (int i = 0; i < _numBonds; i++)
            {
                OBBond _bond = mol.GetBond(i);

                OBAtom _beginAtom = _bond.GetBeginAtom();
                OBAtom _endAtom   = _bond.GetEndAtom();

                uint _beginAtomAtomicNum = _beginAtom.GetAtomicNum();
                uint _endAtomAtomicNum   = _endAtom.GetAtomicNum();

                if (_bond.IsSingle())  //Replace IsSingle() method in Openbabel 3.0
                {
                    n_b1++;
                    if (_beginAtomAtomicNum != 1 && _endAtomAtomicNum != 1)
                    {
                        n_b1_NoH++;
                    }
                }
                else if (_bond.IsDouble())  // Replace IsDouble() method in Openbabel 3.0
                {
                    n_b2++;
                }
                else if (_bond.IsTriple())  // Replace IsTriple() method in Openbabel 3.0
                {
                    n_b3++;
                }
                else if (_bond.IsAromatic())
                {
                    n_bar++;
                }

                if ((_beginAtomAtomicNum == 6 && _endAtomAtomicNum == 8) || (_beginAtomAtomicNum == 8 && _endAtomAtomicNum == 6))
                {
                    uint _bondOrder = _bond.GetBondOrder();

                    if (_bondOrder == 1)
                    {
                        n_C1O++;
                    }
                    else if (_bondOrder == 2)
                    {
                        n_C2O++;
                    }
                }
                else if ((_beginAtomAtomicNum == 6 && _endAtomAtomicNum == 7) || (_beginAtomAtomicNum == 7 && _endAtomAtomicNum == 6))
                {
                    n_CN++;
                }
                else if (_beginAtomAtomicNum != 6 && _endAtomAtomicNum != 6)
                {
                    n_XY++;
                }
            }

            /* 统计环 */
            #region 统计环

            VectorpRing _SSSR = mol.GetSSSR();

            int n_r3   = 0; // number of 3-membered rings 3元环个数
            int n_r4   = 0; // number of 4-membered rings 4元环个数
            int n_r5   = 0; // number of 5-membered rings 5元环个数
            int n_r6   = 0; // number of 6-membered rings 6元环个数
            int n_r7   = 0; // number of 7-membered rings 7元环个数
            int n_r8   = 0; // number of 8-membered rings 8元环个数
            int n_r9   = 0; // number of 9-membered rings 9元环个数
            int n_r10  = 0; // number of 10-membered rings 10元环个数
            int n_r11  = 0; // number of 11-membered rings 11元环个数
            int n_r12  = 0; // number of 12-membered rings 12元环个数
            int n_r13p = 0; // number of 13-membered or larger rings 13元环或者更大的环的个数
            int n_rN   = 0; // number of rings containing nitrogen (any number) 含氮环的个数
            int n_rN1  = 0; // number of rings containing 1 nitrogen atom 含一个氮原子的环的个数
            int n_rN2  = 0; // number of rings containing 2 nitrogen atoms 含两个个氮原子的环的个数
            int n_rN3p = 0; // number of rings containing 3 or more nitrogen atoms 含三个或以上个氮原子的环的个数
            int n_rO   = 0; // number of rings containing oxygen (any number) 含氧环的个数
            int n_rO1  = 0; // number of rings containing 1 oxygen atom 含一个氧原子的环的个数
            int n_rO2p = 0; // number of rings containing 2 or more oxygen atoms 含三个或以上个氧原子的环的个数
            int n_rS   = 0; // number of rings containing sulfur (any number) 含硫环的个数
            int n_rX   = 0; // number of heterocycles (any type) 杂环的个数
            int n_rar  = 0; // number of aromatic rings (any type) 芳香环的个数

            if (_SSSR.Count != 0 || _SSSR != null)
            {
                foreach (OBRing _ring in _SSSR)
                {
                    uint _ringSize = _ring.Size();

                    if (_ringSize == 3)
                    {
                        n_r3++;
                    }
                    else if (_ringSize == 4)
                    {
                        n_r4++;
                    }
                    else if (_ringSize == 5)
                    {
                        n_r5++;
                    }
                    else if (_ringSize == 6)
                    {
                        n_r6++;
                    }
                    else if (_ringSize == 7)
                    {
                        n_r7++;
                    }
                    else if (_ringSize == 8)
                    {
                        n_r8++;
                    }
                    else if (_ringSize == 9)
                    {
                        n_r9++;
                    }
                    else if (_ringSize == 10)
                    {
                        n_r10++;
                    }
                    else if (_ringSize == 11)
                    {
                        n_r11++;
                    }
                    else if (_ringSize == 12)
                    {
                        n_r12++;
                    }
                    else if (_ringSize >= 13)
                    {
                        n_r13p++;
                    }

                    if (_ring.IsAromatic())
                    {
                        n_rar++;
                    }

                    VectorInt _pathes = _ring._path;

                    int _ct_N = 0;
                    int _ct_O = 0;
                    int _ct_S = 0;

                    bool _isRingX = false;
                    foreach (int _p in _pathes)
                    {
                        OBAtom _atom = mol.GetAtom(_p);

                        uint _AtomicNum = _atom.GetAtomicNum();

                        if (_AtomicNum == 7) //N原子
                        {
                            _ct_N++;
                        }
                        else if (_AtomicNum == 8) //O原子
                        {
                            _ct_O++;
                        }
                        else if (_AtomicNum == 16) //S原子
                        {
                            _ct_S++;
                        }

                        if (_AtomicNum != 6)
                        {
                            _isRingX = true;
                        }
                    }

                    if (_ct_N == 1)
                    {
                        n_rN1++;
                    }
                    else if (_ct_N == 2)
                    {
                        n_rN2++;
                    }
                    else if (_ct_N >= 3)
                    {
                        n_rN3p++;
                    }

                    n_rN = n_rN1 + n_rN2 + n_rN3p;

                    if (_ct_O == 1)
                    {
                        n_rO1++;
                    }
                    else if (_ct_O >= 2)
                    {
                        n_rO2p++;
                    }

                    n_rO = n_rO1 + n_rO2p;

                    if (_ct_S >= 1)
                    {
                        n_rS++;
                    }

                    if (_isRingX)
                    {
                        n_rX++;
                    }
                }
            }

            #endregion

            // 统计数字映射成MolStat对象
            MolStat stat = new MolStat
            {
                // General info
                AtomsCount      = (int)_numAtoms,
                HeavyAtomsCount = (int)_numHvyAtoms,
                TotalCharge     = _totalCharge,
                // Atoms info
                N_B     = n_B,
                N_Br    = n_Br,
                N_C     = n_C,
                N_C1    = n_C1,
                N_C2    = n_C2,
                N_CHB1p = n_CHB1p,
                N_CHB2p = n_CHB2p,
                N_CHB3p = n_CHB3p,
                N_CHB4  = n_CHB4,
                N_Cl    = n_Cl,
                N_F     = n_F,
                N_I     = n_I,
                N_Met   = n_Met,
                N_N     = n_N,
                N_N1    = n_N1,
                N_N2    = n_N2,
                N_N3    = n_N3,
                N_O     = n_O,
                N_O2    = n_O2,
                N_O3    = n_O3,
                N_P     = n_P,
                N_S     = n_S,
                N_SeTe  = n_SeTe,
                N_X     = n_X,
                // Bonds info
                BondsCount = (int)_numBonds,
                N_b1       = n_b1,
                N_b1_NoH   = n_b1_NoH,
                N_b2       = n_b2,
                N_b3       = n_b3,
                N_bar      = n_bar,
                N_C1O      = n_C1O,
                N_C2O      = n_C2O,
                N_CN       = n_CN,
                N_XY       = n_XY,
                // Rings info
                RingsCount = _SSSR.Count,
                N_r10      = n_r10,
                N_r11      = n_r11,
                N_r12      = n_r12,
                N_r13p     = n_r13p,
                N_r3       = n_r3,
                N_r4       = n_r4,
                N_r5       = n_r5,
                N_r6       = n_r6,
                N_r7       = n_r7,
                N_r8       = n_r8,
                N_r9       = n_r9,
                N_rar      = n_rar,
                N_rN       = n_rN,
                N_rN1      = n_rN1,
                N_rN2      = n_rN2,
                N_rN3p     = n_rN3p,
                N_rO       = n_rO,
                N_rO1      = n_rO1,
                N_rO2p     = n_rO2p,
                N_rS       = n_rS,
                N_rX       = n_rX
            };

            return(stat);
        }