From b9819e9932f61e27e358972aca8935559845ade9 Mon Sep 17 00:00:00 2001 From: An Nguyen Date: Mon, 29 Jul 2024 10:33:06 +0700 Subject: [PATCH 1/2] Update valenceCount from ketcher v2 --- .../ketcherails/chem/struct_valence.js | 439 +++++++----------- 1 file changed, 175 insertions(+), 264 deletions(-) diff --git a/app/assets/javascripts/ketcherails/chem/struct_valence.js b/app/assets/javascripts/ketcherails/chem/struct_valence.js index 9805e91..0696e3d 100644 --- a/app/assets/javascripts/ketcherails/chem/struct_valence.js +++ b/app/assets/javascripts/ketcherails/chem/struct_valence.js @@ -13,8 +13,7 @@ if (!window.chem || !chem.Struct) throw new Error("Include MolData.js first"); -chem.Struct.prototype.calcConn = function (aid) -{ +chem.Struct.prototype.calcConn = function (aid) { var conn = 0; var atom = this.atoms.get(aid); var hasAromatic = false; @@ -47,8 +46,11 @@ chem.Struct.prototype.calcConn = function (aid) return conn; }; -chem.Struct.Atom.prototype.calcValence = function (conn) -{ +chem.Struct.Atom.isHeteroAtom = function (label) { + return label !== 'C' && label !== 'H'; +} + +chem.Struct.Atom.prototype.calcValence = function (connectionCount) { var atom = this; var charge = atom.charge; var label = atom.label; @@ -56,319 +58,239 @@ chem.Struct.Atom.prototype.calcValence = function (conn) this.implicitH = 0; return true; } - var elem = chem.Element.getElementByLabel(label); - if (elem == null) { + var element = chem.Element.getElementByLabel(label); + + if (element == null) { this.implicitH = 0; return true; } - var groupno = chem.Element.elements.get(elem).group; - var rad = chem.Struct.radicalElectrons(atom.radical); + var groupno = chem.Element.elements.get(element).group; + var radicalCount = chem.Struct.radicalElectrons(atom.radical); - var valence = conn; - var hyd = 0; + var valence = connectionCount; + var hydrogenCount = 0; var absCharge = Math.abs(charge); - if (groupno == 1) - { + if (groupno == 1) { if (label == 'H' || label == 'Li' || label == 'Na' || label == 'K' || - label == 'Rb' || label == 'Cs' || label == 'Fr') - { + label == 'Rb' || label == 'Cs' || label == 'Fr') { valence = 1; - hyd = 1 - rad - conn - absCharge; + hydrogenCount = 1 - radicalCount - connectionCount - absCharge; } - } - else if (groupno == 3) - { - if (label == 'B' || label == 'Al' || label == 'Ga' || label == 'In') - { - if (charge == -1) - { + } else if (groupno === 2) { + if ( + connectionCount + radicalCount + absCharge === 2 || + connectionCount + radicalCount + absCharge === 0 + ) { + valence = 2; + } else hydrogenCount = -1; + } else if (groupno === 3) { + if (label === 'B' || label === 'Al' || label === 'Ga' || label === 'In') { + if (charge === -1) { valence = 4; - hyd = 4 - rad - conn; - } - else - { + hydrogenCount = 4 - radicalCount - connectionCount; + } else { valence = 3; - hyd = 3 - rad - conn - absCharge; + hydrogenCount = 3 - radicalCount - connectionCount - absCharge; } - } - else if (label == 'Tl') - { - if (charge == -1) - { - if (rad + conn <= 2) - { + } else if (label === 'Tl') { + if (charge === -1) { + if (radicalCount + connectionCount <= 2) { valence = 2; - hyd = 2 - rad - conn; - } - else - { + hydrogenCount = 2 - radicalCount - connectionCount; + } else { valence = 4; - hyd = 4 - rad - conn; + hydrogenCount = 4 - radicalCount - connectionCount; } - } - else if (charge == -2) - { - if (rad + conn <= 3) - { + } else if (charge === -2) { + if (radicalCount + connectionCount <= 3) { valence = 3; - hyd = 3 - rad - conn; - } - else - { + hydrogenCount = 3 - radicalCount - connectionCount; + } else { valence = 5; - hyd = 5 - rad - conn; - } - } - else - { - if (rad + conn + absCharge <= 1) - { - valence = 1; - hyd = 1 - rad - conn - absCharge; - } - else - { - valence = 3; - hyd = 3 - rad - conn - absCharge; + hydrogenCount = 5 - radicalCount - connectionCount; } + } else if (radicalCount + connectionCount + absCharge <= 1) { + valence = 1; + hydrogenCount = 1 - radicalCount - connectionCount - absCharge; + } else { + valence = 3; + hydrogenCount = 3 - radicalCount - connectionCount - absCharge; } } - } - else if (groupno == 4) - { - if (label == 'C' || label == 'Si' || label == 'Ge') - { + } else if (groupno === 4) { + if (label === 'C' || label === 'Si' || label === 'Ge') { valence = 4; - hyd = 4 - rad - conn - absCharge; - } - else if (label == 'Sn' || label == 'Pb') - { - if (conn + rad + absCharge <= 2) - { + hydrogenCount = 4 - radicalCount - connectionCount - absCharge; + } else if (label === 'Sn' || label === 'Pb') { + if (connectionCount + radicalCount + absCharge <= 2) { valence = 2; - hyd = 2 - rad - conn - absCharge; - } - else - { + hydrogenCount = 2 - radicalCount - connectionCount - absCharge; + } else { valence = 4; - hyd = 4 - rad - conn - absCharge; + hydrogenCount = 4 - radicalCount - connectionCount - absCharge; } } - } - else if (groupno == 5) - { - if (label == 'N' || label == 'P') - { - if (charge == 1) - { + } else if (groupno === 5) { + if (label === 'N' || label === 'P') { + if (charge === 1) { valence = 4; - hyd = 4 - rad - conn; - } - else if (charge == 2) - { + hydrogenCount = 4 - radicalCount - connectionCount; + } else if (charge === 2) { valence = 3; - hyd = 3 - rad - conn; - } - else - { - if (label == 'N' || rad + conn + absCharge <= 3) - { - valence = 3; - hyd = 3 - rad - conn - absCharge; - } - else // ELEM_P && rad + conn + absCharge > 3 - { - valence = 5; - hyd = 5 - rad - conn - absCharge; - } + hydrogenCount = 3 - radicalCount - connectionCount; + } else if ( + label === 'N' || + radicalCount + connectionCount + absCharge <= 3 + ) { + valence = 3; + hydrogenCount = 3 - radicalCount - connectionCount - absCharge; + } else { + // ELEM_P && rad + conn + absCharge > 3 + valence = 5; + hydrogenCount = 5 - radicalCount - connectionCount - absCharge; } - } - else if (label == 'Bi' || label == 'Sb' || label == 'As') - { - if (charge == 1) - { - if (rad + conn <= 2 && label != 'As') - { + } else if (label === 'Bi' || label === 'Sb' || label === 'As') { + if (charge === 1) { + if (radicalCount + connectionCount <= 2 && label !== 'As') { valence = 2; - hyd = 2 - rad - conn; - } - else - { + hydrogenCount = 2 - radicalCount - connectionCount; + } else { valence = 4; - hyd = 4 - rad - conn; + hydrogenCount = 4 - radicalCount - connectionCount; } - } - else if (charge == 2) - { + } else if (charge === 2) { valence = 3; - hyd = 3 - rad - conn; - } - else - { - if (rad + conn <= 3) - { - valence = 3; - hyd = 3 - rad - conn - absCharge; - } - else - { - valence = 5; - hyd = 5 - rad - conn - absCharge; - } + hydrogenCount = 3 - radicalCount - connectionCount; + } else if (radicalCount + connectionCount <= 3) { + valence = 3; + hydrogenCount = 3 - radicalCount - connectionCount - absCharge; + } else { + valence = 5; + hydrogenCount = 5 - radicalCount - connectionCount - absCharge; } } - } - else if (groupno == 6) - { - if (label == 'O') - { - if (charge >= 1) - { + } else if (groupno === 6) { + if (label === 'O') { + if (charge >= 1) { valence = 3; - hyd = 3 - rad - conn; - } - else - { + hydrogenCount = 3 - radicalCount - connectionCount; + } else { valence = 2; - hyd = 2 - rad - conn - absCharge; + hydrogenCount = 2 - radicalCount - connectionCount - absCharge; } - } - else if (label == 'S' || label == 'Se' || label == 'Po') - { - if (charge == 1) - { - if (conn <= 3) - { + } else if (label === 'S' || label === 'Se' || label === 'Po') { + if (charge === 1) { + if (connectionCount <= 3) { valence = 3; - hyd = 3 - rad - conn; - } - else - { + hydrogenCount = 3 - radicalCount - connectionCount; + } else { valence = 5; - hyd = 5 - rad - conn; + hydrogenCount = 5 - radicalCount - connectionCount; } - } - else - { - if (conn + rad + absCharge <= 2) - { - valence = 2; - hyd = 2 - rad - conn - absCharge; - } - else if (conn + rad + absCharge <= 4) + } else if (connectionCount + radicalCount + absCharge <= 2) { + valence = 2; + hydrogenCount = 2 - radicalCount - connectionCount - absCharge; + } else if (connectionCount + radicalCount + absCharge <= 4) { // See examples in PubChem // [S] : CID 16684216 // [Se]: CID 5242252 // [Po]: no example, just following ISIS/Draw logic here - { - valence = 4; - hyd = 4 - rad - conn - absCharge; - } - else + valence = 4; + hydrogenCount = 4 - radicalCount - connectionCount - absCharge; + } else { // See examples in PubChem // [S] : CID 46937044 // [Se]: CID 59786 // [Po]: no example, just following ISIS/Draw logic here - { - valence = 6; - hyd = 6 - rad - conn - absCharge; - } + valence = 6; + hydrogenCount = 6 - radicalCount - connectionCount - absCharge; } - } - else if (label == 'Te') - { - if (charge == -1) - { - if (conn <= 2) - { + } else if (label === 'Te') { + if (charge === -1) { + if (connectionCount <= 2) { valence = 2; - hyd = 2 - rad - conn - absCharge; + hydrogenCount = 2 - radicalCount - connectionCount - absCharge; } - } - else if (charge == 0 || charge == 2) - { - if (conn <= 2) - { + } else if (charge === 0 || charge === 2) { + if (connectionCount <= 2) { valence = 2; - hyd = 2 - rad - conn - absCharge; - } - else if (conn <= 4) - { + hydrogenCount = 2 - radicalCount - connectionCount - absCharge; + } else if (connectionCount <= 4) { valence = 4; - hyd = 4 - rad - conn - absCharge; - } - else if (charge == 0 && conn <= 6) - { + hydrogenCount = 4 - radicalCount - connectionCount - absCharge; + } else if (charge === 0 && connectionCount <= 6) { valence = 6; - hyd = 6 - rad - conn - absCharge; + hydrogenCount = 6 - radicalCount - connectionCount - absCharge; + } else { + hydrogenCount = -1; } - else - hyd = -1; } } - } - else if (groupno == 7) - { - if (label == 'F') - { + } else if (groupno === 7) { + if (label === 'F') { valence = 1; - hyd = 1 - rad - conn - absCharge; - } - else if (label == 'Cl' || label == 'Br' || - label == 'I' || label == 'At') - { - if (charge == 1) - { - if (conn <= 2) - { + hydrogenCount = 1 - radicalCount - connectionCount - absCharge; + } else if ( + label === 'Cl' || + label === 'Br' || + label === 'I' || + label === 'At' + ) { + if (charge === 1) { + if (connectionCount <= 2) { valence = 2; - hyd = 2 - rad - conn; + hydrogenCount = 2 - radicalCount - connectionCount; + } else if ( + connectionCount === 3 || + connectionCount === 5 || + connectionCount >= 7 + ) { + hydrogenCount = -1; } - else if (conn == 3 || conn == 5 || conn >= 7) - hyd = -1; - } - else if (charge == 0) - { - if (conn <= 1) - { + } else if (charge === 0) { + if (connectionCount <= 1) { valence = 1; - hyd = 1 - rad - conn; - } - // While the halogens can have valence 3, they can not have - // hydrogens in that case. - else if (conn == 2 || conn == 4 || conn == 6) - { - if (rad == 1) - { - valence = conn; - hyd = 0; + hydrogenCount = 1 - radicalCount - connectionCount; + // While the halogens can have valence 3, they can not have + // hydrogens in that case. + } else if ( + connectionCount === 2 || + connectionCount === 4 || + connectionCount === 6 + ) { + if (radicalCount === 1) { + valence = connectionCount; + hydrogenCount = 0; + } else { + hydrogenCount = -1; // will throw an error in the end } - else - hyd = -1; // will throw an error in the end + } else if (connectionCount > 7) { + hydrogenCount = -1; // will throw an error in the end } - else if (conn > 7) - hyd = -1; // will throw an error in the end } } + } else if (groupno === 8) { + if (connectionCount + radicalCount + absCharge === 0) valence = 1; + else hydrogenCount = -1; + } + if (chem.Struct.Atom.isHeteroAtom(label) && this.implicitHCount !== null) { + hydrogenCount = this.implicitHCount; } - this.valence = valence; - this.implicitH = hyd; - if (this.implicitH < 0) - { - this.valence = conn; + this.implicitH = hydrogenCount; + if (this.implicitH < 0) { + this.valence = connectionCount; this.implicitH = 0; this.badConn = true; return false; } return true; -}; +} -chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) -{ +chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) { var atom = this; var charge = atom.charge; var label = atom.label; @@ -383,50 +305,40 @@ chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) var groupno = chem.Element.elements.get(elem).group; var rad = chem.Struct.radicalElectrons(atom.radical); - if (groupno == 3) - { - if (label == 'B' || label == 'Al' || label == 'Ga' || label == 'In') - { + if (groupno == 3) { + if (label == 'B' || label == 'Al' || label == 'Ga' || label == 'In') { if (charge == -1) if (rad + conn <= 4) return rad + conn; } } - else if (groupno == 5) - { - if (label == 'N' || label == 'P') - { + else if (groupno == 5) { + if (label == 'N' || label == 'P') { if (charge == 1) return rad + conn; if (charge == 2) return rad + conn; } - else if (label == 'Sb' || label == 'Bi' || label == 'As') - { + else if (label == 'Sb' || label == 'Bi' || label == 'As') { if (charge == 1) return rad + conn; else if (charge == 2) return rad + conn; } } - else if (groupno == 6) - { - if (label == 'O') - { + else if (groupno == 6) { + if (label == 'O') { if (charge >= 1) return rad + conn; } - else if (label == 'S' || label == 'Se' || label == 'Po') - { + else if (label == 'S' || label == 'Se' || label == 'Po') { if (charge == 1) return rad + conn; } } - else if (groupno == 7) - { + else if (groupno == 7) { if (label == 'Cl' || label == 'Br' || - label == 'I' || label == 'At') - { + label == 'I' || label == 'At') { if (charge == 1) return rad + conn; } @@ -435,8 +347,7 @@ chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) return rad + conn + Math.abs(charge); }; -chem.Struct.prototype.calcImplicitHydrogen = function (aid) -{ +chem.Struct.prototype.calcImplicitHydrogen = function (aid) { var conn = this.calcConn(aid); var atom = this.atoms.get(aid); atom.badConn = false; From cdb6c0b1ac6fd5e5aa1aa8d88b0353e205e11af1 Mon Sep 17 00:00:00 2001 From: An Nguyen Date: Fri, 2 Aug 2024 09:08:47 +0700 Subject: [PATCH 2/2] Update calcImplicitHydrogen --- .../ketcherails/chem/struct_valence.js | 149 +++++++++--------- 1 file changed, 77 insertions(+), 72 deletions(-) diff --git a/app/assets/javascripts/ketcherails/chem/struct_valence.js b/app/assets/javascripts/ketcherails/chem/struct_valence.js index 0696e3d..2fa64c0 100644 --- a/app/assets/javascripts/ketcherails/chem/struct_valence.js +++ b/app/assets/javascripts/ketcherails/chem/struct_valence.js @@ -13,13 +13,12 @@ if (!window.chem || !chem.Struct) throw new Error("Include MolData.js first"); -chem.Struct.prototype.calcConn = function (aid) { - var conn = 0; - var atom = this.atoms.get(aid); - var hasAromatic = false; - for (var i = 0; i < atom.neighbors.length; ++i) { - var hb = this.halfBonds.get(atom.neighbors[i]); - var bond = this.bonds.get(hb.bid); +chem.Struct.prototype.calcConn = function (atom) { + let conn = 0; + for (let i = 0; i < atom.neighbors.length; ++i) { + const hb = this.halfBonds.get(atom.neighbors[i]); + const bond = this.bonds.get(hb.bid); + switch (bond.type) { case chem.Struct.BOND.TYPE.SINGLE: conn += 1; @@ -31,19 +30,13 @@ chem.Struct.prototype.calcConn = function (aid) { conn += 3; break; case chem.Struct.BOND.TYPE.AROMATIC: - conn += 1; - hasAromatic = true; - break; - case chem.Struct.BOND.TYPE.COORDINATION: - // conn += 0; - break; + if (atom.neighbors.length === 1) return [-1, true]; + return [atom.neighbors.length, true]; default: - return -1; + return [-1, false]; } } - if (hasAromatic) - conn += 1; - return conn; + return [conn, false]; }; chem.Struct.Atom.isHeteroAtom = function (label) { @@ -291,56 +284,41 @@ chem.Struct.Atom.prototype.calcValence = function (connectionCount) { } chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) { - var atom = this; - var charge = atom.charge; - var label = atom.label; - var elem = chem.Element.getElementByLabel(label); - if (elem == null) - throw new Error("Element " + label + " unknown"); - if (elem < 0) { // query atom, skip + const charge = this.charge || 0; + const label = this.label; + const element = chem.Element.getElementByLabel(this.label); + if (!element) { + // query atom, skip this.implicitH = 0; - return null; + return 0; } - var groupno = chem.Element.elements.get(elem).group; - var rad = chem.Struct.radicalElectrons(atom.radical); + const groupno = chem.Element.elements.get(elem).group; + const rad = chem.Struct.radicalElectrons(this.radical); - if (groupno == 3) { - if (label == 'B' || label == 'Al' || label == 'Ga' || label == 'In') { - if (charge == -1) - if (rad + conn <= 4) - return rad + conn; - } - } - else if (groupno == 5) { - if (label == 'N' || label == 'P') { - if (charge == 1) - return rad + conn; - if (charge == 2) - return rad + conn; - } - else if (label == 'Sb' || label == 'Bi' || label == 'As') { - if (charge == 1) - return rad + conn; - else if (charge == 2) - return rad + conn; + if (groupno === 3) { + if (label === 'B' || label === 'Al' || label === 'Ga' || label === 'In') { + if (charge === -1) { + if (rad + conn <= 4) return rad + conn; + } } - } - else if (groupno == 6) { - if (label == 'O') { - if (charge >= 1) - return rad + conn; + } else if (groupno === 5) { + if (label === 'N' || label === 'P') { + if (charge === 1) return rad + conn; + if (charge === 2) return rad + conn; + } else if (label === 'Sb' || label === 'Bi' || label === 'As') { + if (charge === 1) return rad + conn; + else if (charge === 2) return rad + conn; } - else if (label == 'S' || label == 'Se' || label == 'Po') { - if (charge == 1) - return rad + conn; + } else if (groupno === 6) { + if (label === 'O') { + if (charge >= 1) return rad + conn; + } else if (label === 'S' || label === 'Se' || label === 'Po') { + if (charge === 1) return rad + conn; } - } - else if (groupno == 7) { - if (label == 'Cl' || label == 'Br' || - label == 'I' || label == 'At') { - if (charge == 1) - return rad + conn; + } else if (groupno === 7) { + if (label === 'Cl' || label === 'Br' || label === 'I' || label === 'At') { + if (charge === 1) return rad + conn; } } @@ -348,24 +326,51 @@ chem.Struct.Atom.prototype.calcValenceMinusHyd = function (conn) { }; chem.Struct.prototype.calcImplicitHydrogen = function (aid) { - var conn = this.calcConn(aid); - var atom = this.atoms.get(aid); + const atom = this.atoms.get(aid); + const charge = atom.charge || 0; + const [conn, isAromatic] = this.calcConn(atom); + let correctConn = conn; atom.badConn = false; - if (conn < 0 || atom.isQuery()) { + + if (isAromatic) { + if (atom.label === 'C' && charge === 0) { + if (conn === 3) { + atom.implicitH = - chem.Struct.radicalElectrons(atom.radical); + return; + } + if (conn === 2) { + atom.implicitH = 1 - chem.Struct.radicalElectrons(atom.radical); + return; + } + } else if ( + (atom.label === 'O' && charge === 0) || + (atom.label === 'N' && charge === 0 && conn === 3) || + (atom.label === 'N' && charge === 1 && conn === 3) || + (atom.label === 'S' && charge === 0 && conn === 3) || + !atom.implicitH + ) { + atom.implicitH = 0; + return; + } else if (!atom.hasImplicitH) { + correctConn++; + } + } + + if (correctConn < 0 || atom.isQuery() || atom.attachmentPoints) { atom.implicitH = 0; return; } + if (atom.explicitValence >= 0) { - var elem = chem.Element.getElementByLabel(atom.label); - atom.implicitH = 0; - if (elem != null) { - atom.implicitH = atom.explicitValence - atom.calcValenceMinusHyd(conn); - if (atom.implicitH < 0) { - atom.implicitH = 0; - atom.badConn = true; - } + const elem = chem.Element.getElementByLabel(atom.label); + atom.implicitH = elem + ? atom.explicitValence - atom.calcValenceMinusHyd(correctConn) + : 0; + if (atom.implicitH < 0) { + atom.implicitH = 0; + atom.badConn = true; } } else { - atom.calcValence(conn); + atom.calcValence(correctConn); } };