/// <summary> /// Applies the MDL valence model to atoms using the explicit valence (bond /// order sum) and charge to determine the correct number of implicit /// hydrogens. The model is not applied if the explicit valence is less than /// 0 - this is the case when a query bond was read for an atom. /// </summary> /// <param name="atom">the atom to apply the model to</param> /// <param name="explicitValence">the explicit valence (bond order sum)</param> private static void ApplyMDLValenceModel(IAtom atom, int explicitValence, int unpaired) { if (atom.Valency != null) { if (atom.Valency >= explicitValence) { atom.ImplicitHydrogenCount = atom.Valency - (explicitValence - unpaired); } else { atom.ImplicitHydrogenCount = 0; } } else { int element = atom.AtomicNumber; int charge = atom.FormalCharge ?? 0; int implicitValence = MDLValence.ImplicitValence(element, charge, explicitValence); if (implicitValence < explicitValence) { atom.Valency = explicitValence; atom.ImplicitHydrogenCount = 0; } else { atom.Valency = implicitValence; atom.ImplicitHydrogenCount = implicitValence - explicitValence; } } }
public void Nitrogen_anion() { Assert.AreEqual(2, MDLValence.ImplicitValence(7, -1, 0)); Assert.AreEqual(2, MDLValence.ImplicitValence(7, -1, 1)); Assert.AreEqual(2, MDLValence.ImplicitValence(7, -1, 2)); Assert.AreEqual(3, MDLValence.ImplicitValence(7, -1, 3)); Assert.AreEqual(4, MDLValence.ImplicitValence(7, -1, 4)); Assert.AreEqual(5, MDLValence.ImplicitValence(7, -1, 5)); Assert.AreEqual(6, MDLValence.ImplicitValence(7, -1, 6)); }
public void Nitrogen_neutral() { Assert.AreEqual(3, MDLValence.ImplicitValence(7, 0, 0)); Assert.AreEqual(3, MDLValence.ImplicitValence(7, 0, 1)); Assert.AreEqual(3, MDLValence.ImplicitValence(7, 0, 2)); Assert.AreEqual(3, MDLValence.ImplicitValence(7, 0, 3)); Assert.AreEqual(5, MDLValence.ImplicitValence(7, 0, 4)); Assert.AreEqual(5, MDLValence.ImplicitValence(7, 0, 5)); Assert.AreEqual(6, MDLValence.ImplicitValence(7, 0, 6)); }
/// <summary> /// Write the atoms of a molecule. We pass in the order of atoms since for compatibility we /// have shifted all hydrogens to the back. /// </summary> /// <param name="mol">molecule</param> /// <param name="atoms">the atoms of a molecule in desired output order</param> /// <param name="idxs">index lookup</param> /// <param name="atomToStereo">tetrahedral stereo lookup</param> /// <exception cref="IOException">low-level IO error</exception> /// <exception cref="CDKException">inconsistent state etc</exception> private void WriteAtomBlock(IAtomContainer mol, IAtom[] atoms, Dictionary <IChemObject, int> idxs, Dictionary <IAtom, ITetrahedralChirality> atomToStereo) { if (mol.Atoms.Count == 0) { return; } var dim = GetNumberOfDimensions(mol); writer.Write("BEGIN ATOM\n"); int atomIdx = 0; foreach (var atom in atoms) { var elem = NullAsZero(atom.AtomicNumber); var chg = NullAsZero(atom.FormalCharge); var mass = NullAsZero(atom.MassNumber); var hcnt = NullAsZero(atom.ImplicitHydrogenCount); var elec = mol.GetConnectedSingleElectrons(atom).Count(); int rad = 0; switch (elec) { case 1: // 2 rad = MDLV2000Writer.SpinMultiplicity.Monovalent.Value; break; case 2: // 1 or 3? Information loss as to which rad = MDLV2000Writer.SpinMultiplicity.DivalentSinglet.Value; break; } int expVal = 0; foreach (var bond in mol.GetConnectedBonds(atom)) { if (bond.Order == BondOrder.Unset) { throw new CDKException($"Unsupported bond order: {bond.Order}"); } expVal += bond.Order.Numeric(); } string symbol = GetSymbol(atom, elem); int rnum = -1; if (symbol[0] == 'R') { var matcher = R_GRP_NUM.Match(symbol); if (matcher.Success) { symbol = "R#"; rnum = int.Parse(matcher.Groups[1].Value, NumberFormatInfo.InvariantInfo); } } writer.Write(++atomIdx) .Write(' ') .Write(symbol) .Write(' '); var p2d = atom.Point2D; var p3d = atom.Point3D; switch (dim) { case 0: writer.Write("0 0 0 "); break; case 2: if (p2d != null) { writer.Write(p2d.Value.X).WriteDirect(' ') .Write(p2d.Value.Y).WriteDirect(' ') .Write("0 "); } else { writer.Write("0 0 0 "); } break; case 3: if (p3d != null) { writer.Write(p3d.Value.X).WriteDirect(' ') .Write(p3d.Value.Y).WriteDirect(' ') .Write(p3d.Value.Z).WriteDirect(' '); } else { writer.Write("0 0 0 "); } break; } writer.Write(NullAsZero(atom.GetProperty <int>(CDKPropertyName.AtomAtomMapping))); if (chg != 0 && chg >= -15 && chg <= 15) { writer.Write(" CHG=").Write(chg); } if (mass != 0) { writer.Write(" MASS=").Write(mass); } if (rad > 0 && rad < 4) { writer.Write(" RAD=").Write(rad); } if (rnum >= 0) { writer.Write(" RGROUPS=(1 ").Write(rnum).Write(")"); } // determine if we need to write the valence if (MDLValence.ImplicitValence(elem, chg, expVal) - expVal != hcnt) { int val = expVal + hcnt; if (val <= 0 || val > 14) { val = -1; // -1 is 0 } writer.Write(" VAL=").Write(val); } if (atomToStereo.TryGetValue(atom, out ITetrahedralChirality stereo)) { switch (GetLocalParity(idxs, stereo)) { case TetrahedralStereo.Clockwise: writer.Write(" CFG=1"); break; case TetrahedralStereo.AntiClockwise: writer.Write(" CFG=2"); break; default: break; } } writer.Write('\n'); } writer.Write("END ATOM\n"); }