Files
pymol-open-source/layer2/Mol2Typing.cpp
2021-08-20 11:24:10 -04:00

189 lines
4.5 KiB
C++

/*
* Mol2 atom typing
*
* (c) Schrodinger, Inc.
*/
#include "Mol2Typing.h"
#include "AtomInfo.h"
#include "ObjectMolecule.h"
/**
* atm: Atom index of a carbon atom with geom=3
*
* Return: True if atm has 3 neighbors which are all nitrogens with geom=3
*/
static bool isGuanidiniumCarbon(ObjectMolecule * obj, int atm) {
int neighbor_count = 0;
int charge = 0;
for (auto const& item : AtomNeighbors(obj, atm)) {
AtomInfoType const* neighbor = obj->AtomInfo.data() + item.atm;
if (neighbor->protons != cAN_N || neighbor->geom != 3)
return false;
++neighbor_count;
charge += neighbor->formalCharge;
}
return neighbor_count == 3 && charge > 0;
}
/**
* TODO: bond order 4 seems to be no guarantee for a ring, which is
* required for aromaticity
*
* Return: always false, since PyMOL doesn't know enough about aromaticity
*/
static bool isAromaticAtom(ObjectMolecule * obj, int atm) {
#if 0
for (auto const& neighbor : AtomNeighbors(obj, atm)) {
if (obj->Bond[neighbor.bond].order == 4)
return true;
}
#endif
return false;
}
/**
* atm: Atom index of an oxygen atom
*
* Return: True if atom is part of a carboxylate or phosphate group
*/
static bool isCarboxylateOrPhosphateOxygen(ObjectMolecule * obj, int atm) {
int o_count = 0, other_count = 0;
auto const neighbors = AtomNeighbors(obj, atm);
// must have only one neighbor
if (neighbors.size() != 1)
return false;
// get that one neighbor as center of the acidic group
atm = neighbors[0].atm;
// check center atom
AtomInfoType * ai = obj->AtomInfo + atm;
if (!(ai->protons == cAN_C && ai->geom == 3) &&
!(ai->protons == cAN_P && ai->geom == 4))
return false;
// iterate over neighbors of center atom
for (auto const& item : AtomNeighbors(obj, atm)) {
AtomInfoType const* neighbor = obj->AtomInfo.data() + item.atm;
if (neighbor->protons == cAN_O)
++o_count;
else
++other_count;
}
// carboxylate
if (ai->protons == cAN_C)
return (o_count == 2 && other_count == 1);
// phosphate
return (o_count == 4 && other_count == 0);
}
/**
* atm: Atom index of a sulfur atom
*
* Return: Number of bound Oxygens if bound to two non-Oxygen atoms. Otherwise 0.
*/
static int sulfurCountOxygenNeighbors(ObjectMolecule * obj, int atm) {
int o_count = 0, other_count = 0;
for (auto const& item : AtomNeighbors(obj, atm)) {
AtomInfoType const* neighbor = obj->AtomInfo.data() + item.atm;
if (neighbor->protons == cAN_O)
++o_count;
else
++other_count;
}
return (other_count == 2) ? o_count : 0;
}
/**
* Get the Tripos Mol2 atom type
*
* Pre-condition: ObjectMoleculeVerifyChemistry
*/
const char * getMOL2Type(ObjectMolecule * obj, int atm) {
auto G = obj->G;
auto ai = obj->AtomInfo + atm;
switch (ai->protons) {
case cAN_C:
switch (ai->geom) {
case cAtomInfoLinear:
return "C.1";
case cAtomInfoPlanar:
if (isAromaticAtom(obj, atm))
return "C.ar";
if (isGuanidiniumCarbon(obj, atm))
return "C.cat";
return "C.2";
case cAtomInfoTetrahedral:
return "C.3";
}
break;
case cAN_N:
switch (ai->geom) {
case cAtomInfoLinear:
return "N.1";
case cAtomInfoPlanar:
if ((ai->flags & cAtomFlag_polymer)
&& ai->name == G->lex_const.N)
return "N.am";
if (isAromaticAtom(obj, atm))
return "N.ar";
if (ai->valence == 2 && ai->formalCharge == 0)
return "N.2";
return "N.pl3";
case cAtomInfoTetrahedral:
return (ai->formalCharge == 1) ? "N.4" : "N.3";
}
break;
case cAN_O:
if (isCarboxylateOrPhosphateOxygen(obj, atm))
return "O.co2";
switch (ai->geom) {
case cAtomInfoPlanar: return "O.2";
case cAtomInfoTetrahedral: return "O.3";
}
break;
case cAN_S:
switch (sulfurCountOxygenNeighbors(obj, atm)) {
case 1: return "S.o";
case 2: return "S.o2";
}
switch (ai->geom) {
case cAtomInfoPlanar: return "S.2";
case cAtomInfoTetrahedral: return "S.3";
}
break;
case cAN_P:
if (ai->geom == 4)
return "P.3";
break;
case cAN_Cr:
if (ai->geom == 4)
return "Cr.th";
return "Cr.oh";
case cAN_Co:
return "Co.oh";
}
if (ai->protons >= 0 && ai->protons < ElementTableSize)
return ElementTable[ai->protons].symbol;
return "Du";
}