Files
rdkit/Code/GraphMol/Bond.cpp
Greg Landrum e625c58bab Add cis/trans tags to double bonds (#1316)
* add cis and trans to bond stereo

* compiles, does not work

* tests all pass

* Whitespace cleanup to recent changes.

* C++ test case for Bond::setStereo using Bond::STEREOCIS and Bond::STEREOTRANS

* Adding a PRECONDITION to Bond::setStereo to make sure the stereo atoms
are already specified if CIS or TRANS is being specified.

E/Z is technically defined by the topology of the molecule so the
stereo atoms are redundant (easier to understand and use!), but
ultimately redundant with the graph. However, CIS and TRANS is _only_
defined in this usage as the orientation of the atoms in the
getStereoAtoms vector.

* Exposing Bond::setStereo to Python and adding test cases to make sure
it can be used to set CIS/TRANS stereochemistry.

* verify substructure matching works

* Adding Bond::setStereoAtoms to C++ Bond class.

This allows setting the atoms to be considered for CIS or TRANS
directly without a much more costly determination of ranking that E/Z
requires.

* Wrap Bond::SetStereoAtoms into python with a new type of test case.

* docs
2017-02-26 08:15:44 -05:00

330 lines
7.8 KiB
C++

//
// Copyright (C) 2001-2017 Greg Landrum and Rational Discovery LLC
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
#include "Bond.h"
#include "Atom.h"
#include "ROMol.h"
#include <RDGeneral/Invariant.h>
namespace RDKit {
Bond::Bond() : RDProps() { initBond(); };
Bond::Bond(BondType bT) : RDProps() {
initBond();
d_bondType = bT;
};
Bond::Bond(const Bond &other) : RDProps(other) {
// NOTE: we do *not* copy ownership!
dp_mol = 0;
d_bondType = other.d_bondType;
d_beginAtomIdx = other.d_beginAtomIdx;
d_endAtomIdx = other.d_endAtomIdx;
d_dirTag = other.d_dirTag;
d_stereo = other.d_stereo;
if (other.dp_stereoAtoms) {
dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms);
} else {
dp_stereoAtoms = NULL;
}
df_isAromatic = other.df_isAromatic;
df_isConjugated = other.df_isConjugated;
d_index = other.d_index;
}
Bond::~Bond() { delete dp_stereoAtoms; }
Bond &Bond::operator=(const Bond &other) {
dp_mol = other.dp_mol;
d_bondType = other.d_bondType;
d_beginAtomIdx = other.d_beginAtomIdx;
d_endAtomIdx = other.d_endAtomIdx;
d_dirTag = other.d_dirTag;
if (other.dp_stereoAtoms) {
dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms);
} else {
dp_stereoAtoms = NULL;
}
df_isAromatic = other.df_isAromatic;
df_isConjugated = other.df_isConjugated;
d_index = other.d_index;
dp_props = other.dp_props;
return *this;
}
Bond *Bond::copy() const {
Bond *res = new Bond(*this);
return res;
}
void Bond::setOwningMol(ROMol *other) {
// FIX: doesn't update topology
dp_mol = other;
}
unsigned int Bond::getOtherAtomIdx(const unsigned int thisIdx) const {
PRECONDITION(d_beginAtomIdx == thisIdx || d_endAtomIdx == thisIdx,
"bad index");
if (d_beginAtomIdx == thisIdx)
return d_endAtomIdx;
else if (d_endAtomIdx == thisIdx)
return d_beginAtomIdx;
// we cannot actually get down here
return 0;
}
void Bond::setBeginAtomIdx(unsigned int what) {
if (dp_mol) URANGE_CHECK(what, getOwningMol().getNumAtoms() - 1);
d_beginAtomIdx = what;
};
void Bond::setEndAtomIdx(unsigned int what) {
if (dp_mol) URANGE_CHECK(what, getOwningMol().getNumAtoms() - 1);
d_endAtomIdx = what;
};
void Bond::setBeginAtom(Atom *at) {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
setBeginAtomIdx(at->getIdx());
}
void Bond::setBeginAtom(Atom::ATOM_SPTR at) {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
setBeginAtomIdx(at->getIdx());
}
void Bond::setEndAtom(Atom *at) {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
setEndAtomIdx(at->getIdx());
}
void Bond::setEndAtom(Atom::ATOM_SPTR at) {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
setEndAtomIdx(at->getIdx());
}
Atom *Bond::getBeginAtom() const {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
return dp_mol->getAtomWithIdx(d_beginAtomIdx);
};
Atom *Bond::getEndAtom() const {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
return dp_mol->getAtomWithIdx(d_endAtomIdx);
};
Atom *Bond::getOtherAtom(Atom const *what) const {
PRECONDITION(dp_mol != 0, "no owning molecule for bond");
return dp_mol->getAtomWithIdx(getOtherAtomIdx(what->getIdx()));
};
double Bond::getBondTypeAsDouble() const {
switch (getBondType()) {
case UNSPECIFIED:
return 0;
break;
case IONIC:
return 0;
break;
case SINGLE:
return 1;
break;
case DOUBLE:
return 2;
break;
case TRIPLE:
return 3;
break;
case QUADRUPLE:
return 4;
break;
case QUINTUPLE:
return 5;
break;
case HEXTUPLE:
return 6;
break;
case ONEANDAHALF:
return 1.5;
break;
case TWOANDAHALF:
return 2.5;
break;
case THREEANDAHALF:
return 3.5;
break;
case FOURANDAHALF:
return 4.5;
break;
case FIVEANDAHALF:
return 5.5;
break;
case AROMATIC:
return 1.5;
break;
case DATIVEONE:
return 1.0;
break; // FIX: this should probably be different
case DATIVE:
return 1.0;
break; // FIX: again probably wrong
case ZERO:
return 0;
break;
default:
UNDER_CONSTRUCTION("Bad bond type");
}
}
double Bond::getValenceContrib(Atom::ATOM_SPTR at) const {
return getValenceContrib(at.get());
}
double Bond::getValenceContrib(const Atom *atom) const {
switch (getBondType()) {
case UNSPECIFIED:
return 0;
break;
case IONIC:
return 0;
break;
case SINGLE:
return 1;
break;
case DOUBLE:
return 2;
break;
case TRIPLE:
return 3;
break;
case QUADRUPLE:
return 4;
break;
case QUINTUPLE:
return 5;
break;
case HEXTUPLE:
return 6;
break;
case ONEANDAHALF:
return 1.5;
break;
case TWOANDAHALF:
return 2.5;
break;
case THREEANDAHALF:
return 3.5;
break;
case FOURANDAHALF:
return 4.5;
break;
case FIVEANDAHALF:
return 5.5;
break;
case AROMATIC:
return 1.5;
break;
case DATIVEONE:
if (atom->getIdx() == getEndAtomIdx())
return 1.0;
else
return 0.0;
break;
case DATIVE:
if (atom->getIdx() == getEndAtomIdx())
return 1.0;
else
return 0.0;
break;
case ZERO:
return 0;
break;
default:
UNDER_CONSTRUCTION("Bad bond type");
}
}
void Bond::setQuery(QUERYBOND_QUERY *what) {
// Bonds don't have queries at the moment because I have not
// yet figured out what a good base query should be.
// It would be nice to be able to do substructure searches
// using molecules alone, so it'd be nice if we got this
// issue resolved ASAP.
RDUNUSED_PARAM(what);
PRECONDITION(0, "plain bonds have no Query");
}
Bond::QUERYBOND_QUERY *Bond::getQuery() const {
PRECONDITION(0, "plain bonds have no Query");
return NULL;
};
bool Bond::Match(Bond const *what) const {
bool res;
if (getBondType() == Bond::UNSPECIFIED ||
what->getBondType() == Bond::UNSPECIFIED) {
res = true;
} else {
res = getBondType() == what->getBondType();
}
return res;
};
bool Bond::Match(const Bond::BOND_SPTR what) const {
return Match(what.get());
};
void Bond::expandQuery(Bond::QUERYBOND_QUERY *what,
Queries::CompositeQueryType how, bool maintainOrder) {
RDUNUSED_PARAM(what);
RDUNUSED_PARAM(how);
RDUNUSED_PARAM(maintainOrder);
PRECONDITION(0, "plain bonds have no query");
};
void Bond::initBond() {
d_bondType = UNSPECIFIED;
d_dirTag = NONE;
d_stereo = STEREONONE;
dp_mol = 0;
d_beginAtomIdx = 0;
d_endAtomIdx = 0;
df_isAromatic = 0;
d_index = 0;
df_isConjugated = 0;
dp_stereoAtoms = NULL;
};
void Bond::setStereoAtoms(unsigned int bgnIdx, unsigned int endIdx) {
PRECONDITION(
getOwningMol().getBondBetweenAtoms(getBeginAtomIdx(), bgnIdx) != NULL,
"bgnIdx not connected to begin atom of bond");
PRECONDITION(
getOwningMol().getBondBetweenAtoms(getEndAtomIdx(), endIdx) != NULL,
"endIdx not connected to end atom of bond");
INT_VECT &atoms = getStereoAtoms();
atoms.clear();
atoms.push_back(bgnIdx);
atoms.push_back(endIdx);
};
}; // end o' namespace
std::ostream &operator<<(std::ostream &target, const RDKit::Bond &bond) {
target << bond.getIdx() << " ";
target << bond.getBeginAtomIdx() << "->" << bond.getEndAtomIdx();
target << " order: " << bond.getBondType();
if (bond.getBondDir()) target << " dir: " << bond.getBondDir();
if (bond.getStereo()) target << " stereo: " << bond.getStereo();
target << " conj?: " << bond.getIsConjugated();
target << " aromatic?: " << bond.getIsAromatic();
return target;
}