/**********************************************************************
Molecule - Molecule class derived from the base Primitive class
Copyright (C) 2007 Donald Ephraim Curtis
Copyright (C) 2008 Marcus D. Hanwell
This file is part of the Avogadro molecular editor project.
For more information, see
Avogadro is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Avogadro is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
**********************************************************************/
#include "molecule.h"
#include "atom.h"
#include "bond.h"
#include "cube.h"
#include "mesh.h"
#include "fragment.h"
#include "residue.h"
#include "zmatrix.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using std::vector;
using Eigen::Vector3d;
namespace Avogadro{
class MoleculePrivate {
public:
MoleculePrivate() : farthestAtom(0), invalidGeomInfo(true),
invalidRings(true), obmol(0), obunitcell(0), obvibdata(0) {}
// These are logically cached variables and thus are marked as mutable.
// Const objects should be logically constant (and not mutable)
// http://www.highprogrammer.com/alan/rants/mutable.html
mutable Eigen::Vector3d center;
mutable Eigen::Vector3d normalVector;
mutable double radius;
mutable Atom * farthestAtom;
mutable bool invalidGeomInfo;
mutable bool invalidRings;
mutable std::vector energies;
// std::vector used over QVector due to index issues, QVector uses ints
std::vector cubes;
std::vector meshes;
std::vector residues;
std::vector rings;
std::vector zMatrix;
// Used to store the index based list (not unique ids)
QList cubeList;
QList meshList;
QList residueList;
QList ringList;
QList zMatrixList;
// Our OpenBabel OBMol object
OpenBabel::OBMol * obmol;
// Our OpenBabel OBUnitCell object (if any)
OpenBabel::OBUnitCell * obunitcell;
// Our OpenBabel OBVibrationData object (if any)
// TODO: Cache an OBMol, in which case the vib. data (and others)
// won't be necessary
OpenBabel::OBVibrationData * obvibdata;
};
Molecule::Molecule(QObject *parent) : Primitive(MoleculeType, parent),
d_ptr(new MoleculePrivate),
m_fileName(""),
m_atomPos(0), m_dipoleMoment(0),
m_invalidPartialCharges(true), m_invalidAromaticity(true)
{
connect(this, SIGNAL(updated()), this, SLOT(updatePrimitive()));
}
Molecule::Molecule(const Molecule &other) :
Primitive(MoleculeType, other.parent()), d_ptr(new MoleculePrivate),
m_atomPos(0), m_dipoleMoment(0), m_invalidPartialCharges(true),
m_invalidAromaticity(true)
{
*this = other;
connect(this, SIGNAL(updated()), this, SLOT(updatePrimitive()));
}
Molecule::~Molecule()
{
// Need to iterate through all atoms/bonds and destroy them
//Q_D(Molecule);
disconnect(this, 0, 0, 0);
clear();
delete d_ptr;
}
void Molecule::setFileName(const QString& name)
{
QWriteLocker lock(m_lock);
m_fileName = name;
}
QString Molecule::fileName() const
{
QReadLocker lock(m_lock);
return m_fileName;
}
Atom *Molecule::addAtom()
{
// Add an atom with the next unique id
return addAtom(m_atoms.size());
}
// do some fancy footwork when we add an atom previously created
Atom *Molecule::addAtom(unsigned long id)
{
Atom *atom = new Atom(this);
m_lock->lockForWrite();
if (!m_atomPos) {
m_atomConformers.resize(1);
m_atomConformers[0] = new vector;
m_atomPos = m_atomConformers[0];
m_atomPos->reserve(100);
}
if(id >= m_atoms.size()) {
m_atoms.resize(id+1,0);
m_atomPos->resize(id+1, Vector3d::Zero());
}
m_atoms[id] = atom;
// Does this still want to have the same index as before somehow?
m_atomList.push_back(atom);
m_lock->unlock();
atom->setId(id);
atom->setIndex(m_atomList.size()-1);
// now that the id is correct, emit the signal
connect(atom, SIGNAL(updated()), this, SLOT(updateAtom()));
emit atomAdded(atom);
return atom;
}
void Molecule::setAtomPos(unsigned long id, const Eigen::Vector3d& vec)
{
if (id < m_atomPos->size()) {
m_lock->lockForWrite();
(*m_atomPos)[id] = vec;
m_lock->unlock();
}
}
void Molecule::setAtomPos(unsigned long id, const Eigen::Vector3d *vec)
{
if (vec) setAtomPos(id, *vec);
}
void Molecule::removeAtom(Atom *atom)
{
if(atom) {
// When deleting an atom this also implicitly deletes any bonds to the atom
foreach (unsigned long bond, atom->bonds()) {
removeBond(bond);
}
m_lock->lockForWrite();
m_atoms[atom->id()] = 0;
// 1 based arrays stored/shown to user
int index = atom->index();
m_atomList.removeAt(index);
for (int i = index; i < m_atomList.size(); ++i)
m_atomList[i]->setIndex(i);
atom->deleteLater();
m_lock->unlock();
disconnect(atom, SIGNAL(updated()), this, SLOT(updateAtom()));
emit atomRemoved(atom);
}
}
void Molecule::removeAtom(unsigned long id)
{
removeAtom(atomById(id));
}
Bond *Molecule::addBond()
{
return addBond(m_bonds.size());
}
Bond *Molecule::addBond(unsigned long id)
{
Q_D(Molecule);
Bond *bond = new Bond(this);
m_lock->lockForWrite();
d->invalidRings = true;
m_invalidPartialCharges = true;
m_invalidAromaticity = true;
if(id >= m_bonds.size())
m_bonds.resize(id+1,0);
m_bonds[id] = bond;
m_bondList.push_back(bond);
m_lock->unlock();
bond->setId(id);
bond->setIndex(m_bondList.size()-1);
// now that the id is correct, emit the signal
connect(bond, SIGNAL(updated()), this, SLOT(updateBond()));
emit bondAdded(bond);
return(bond);
}
void Molecule::removeBond(Bond *bond)
{
if(bond) {
removeBond(bond->id());
}
}
void Molecule::removeBond(unsigned long id)
{
if (id < m_bonds.size()) {
Q_D(Molecule);
if (m_bonds[id] == 0) {
return;
}
m_lock->lockForWrite();
d->invalidRings = true;
m_invalidPartialCharges = true;
m_invalidAromaticity = true;
Bond *bond = m_bonds[id];
m_bonds[id] = 0;
// Delete the bond from the list and reorder the remaining bonds
int index = bond->index();
m_bondList.removeAt(index);
for (int i = index; i < m_bondList.size(); ++i) {
m_bondList[i]->setIndex(i);
}
m_lock->unlock();
// Also delete the bond from the attached atoms
if (m_atoms.size() > bond->beginAtomId()) {
if (m_atoms[bond->beginAtomId()])
m_atoms[bond->beginAtomId()]->removeBond(id);
}
if (m_atoms.size() > bond->endAtomId()) {
if (m_atoms[bond->endAtomId()])
m_atoms[bond->endAtomId()]->removeBond(id);
}
disconnect(bond, SIGNAL(updated()), this, SLOT(updateBond()));
emit bondRemoved(bond);
bond->deleteLater();
}
}
Residue *Molecule::residue(int index)
{
Q_D(Molecule);
QReadLocker lock(m_lock);
if (index >= 0 && index < d->residueList.size()) {
return d->residueList[index];
}
else {
return 0;
}
}
const Residue *Molecule::residue(int index) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if (index >= 0 && index < d->residueList.size()) {
return d->residueList[index];
}
else {
return 0;
}
}
Residue *Molecule::residueById(unsigned long id) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if(id < d->residues.size()) {
return d->residues[id];
}
else {
return 0;
}
}
Cube *Molecule::cube(int index) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if (index >= 0 && index < d->cubeList.size()) {
return d->cubeList[index];
}
else {
return 0;
}
}
Cube *Molecule::cubeById(unsigned long id) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if(id < d->cubes.size()) {
return d->cubes[id];
}
else {
return 0;
}
}
Cube *Molecule::addCube()
{
Q_D(Molecule);
Cube *cube = new Cube(this);
m_lock->lockForWrite();
d->cubes.push_back(cube);
d->cubeList.push_back(cube);
m_lock->unlock();
cube->setId(d->cubes.size()-1);
cube->setIndex(d->cubeList.size()-1);
// now that the id is correct, emit the signal
connect(cube, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveAdded(cube);
return(cube);
}
void Molecule::removeCube(Cube *cube)
{
Q_D(Molecule);
if(cube) {
m_lock->lockForWrite();
d->cubes[cube->id()] = 0;
// 0 based arrays stored/shown to user
int index = cube->index();
d->cubeList.removeAt(index);
for (int i = index; i < d->cubeList.size(); ++i) {
d->cubeList[i]->setIndex(i);
}
m_lock->unlock();
cube->deleteLater();
disconnect(cube, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveRemoved(cube);
}
}
void Molecule::removeCube(unsigned long id)
{
Q_D(Molecule);
if (id < d->cubes.size())
removeCube(d->cubes[id]);
}
Mesh * Molecule::addMesh()
{
Q_D(Molecule);
Mesh *mesh = new Mesh(this);
m_lock->lockForWrite();
d->meshes.push_back(mesh);
d->meshList.push_back(mesh);
m_lock->unlock();
mesh->setId(d->meshes.size()-1);
mesh->setIndex(d->meshList.size()-1);
// now that the id is correct, emit the signal
connect(mesh, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveAdded(mesh);
return(mesh);
}
void Molecule::removeMesh(Mesh *mesh)
{
Q_D(Molecule);
if(mesh) {
m_lock->lockForWrite();
d->meshes[mesh->id()] = 0;
// 0 based arrays stored/shown to user
int index = mesh->index();
d->meshList.removeAt(index);
for (int i = index; i < d->meshList.size(); ++i) {
d->meshList[i]->setIndex(i);
}
m_lock->unlock();
mesh->deleteLater();
disconnect(mesh, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveRemoved(mesh);
}
}
void Molecule::removeMesh(unsigned long id)
{
Q_D(Molecule);
if (id < d->meshes.size())
removeMesh(d->meshes[id]);
}
Mesh * Molecule::mesh(int index) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if (index >= 0 && index < d->meshList.size()) {
return d->meshList[index];
}
else {
return 0;
}
}
Mesh * Molecule::meshById(unsigned long id) const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
if(id < d->meshes.size()) {
return d->meshes[id];
}
else {
return 0;
}
}
Residue * Molecule::addResidue()
{
Q_D(Molecule);
Residue *residue = new Residue(this);
d->residues.push_back(residue);
residue->setId(d->residues.size()-1);
d->residueList.push_back(residue);
residue->setIndex(d->residueList.size()-1);
// now that the id is correct, emit the signal
connect(residue, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveAdded(residue);
return(residue);
}
void Molecule::removeResidue(Residue *residue)
{
Q_D(Molecule);
if(residue) {
d->residues[residue->id()] = 0;
// 0 based arrays stored/shown to user
int index = residue->index();
d->residueList.removeAt(index);
for (int i = index; i < d->residueList.size(); ++i) {
d->residueList[i]->setIndex(i);
}
residue->deleteLater();
disconnect(residue, SIGNAL(updated()), this, SLOT(updatePrimitive()));
emit primitiveRemoved(residue);
}
}
void Molecule::removeResidue(unsigned long id)
{
Q_D(Molecule);
if (id < d->residues.size())
removeResidue(d->residues[id]);
}
Fragment * Molecule::addRing()
{
Q_D(Molecule);
Fragment *ring = new Fragment(this);
d->rings.push_back(ring);
ring->setId(d->rings.size()-1);
d->ringList.push_back(ring);
ring->setIndex(d->ringList.size()-1);
// now that the id is correct, emit the signal
connect(ring, SIGNAL(updated()), this, SLOT(updatePrimitive()));
// emit primitiveAdded(ring);
return(ring);
}
void Molecule::removeRing(Fragment *ring)
{
Q_D(Molecule);
if(ring) {
d->rings[ring->id()] = 0;
// 0 based arrays stored/shown to user
int index = ring->index();
d->ringList.removeAt(index);
for (int i = index; i < d->ringList.size(); ++i) {
d->ringList[i]->setIndex(i);
}
ring->deleteLater();
disconnect(ring, SIGNAL(updated()), this, SLOT(updatePrimitive()));
// emit primitiveRemoved(ring);
}
}
void Molecule::removeRing(unsigned long id)
{
Q_D(Molecule);
if (id < d->rings.size())
removeRing(d->rings[id]);
}
ZMatrix * Molecule::addZMatrix()
{
Q_D(Molecule);
ZMatrix *zmatrix = new ZMatrix(this);
d->zMatrixList.push_back(zmatrix);
return zmatrix;
}
void Molecule::removeZMatrix(ZMatrix *zmatrix)
{
Q_D(Molecule);
if (zmatrix) {
d->zMatrixList.removeAll(zmatrix);
delete zmatrix;
}
}
ZMatrix * Molecule::zMatrix(int index) const
{
Q_D(const Molecule);
if (index < d->zMatrixList.size())
return d->zMatrixList.at(index);
else
return 0;
}
QList Molecule::zMatrices() const
{
Q_D(const Molecule);
return d->zMatrixList;
}
unsigned int Molecule::numZMatrices() const
{
Q_D(const Molecule);
return d->zMatrixList.size();
}
void Molecule::addHydrogens(Atom *a,
const QList &atomIds,
const QList &bondIds)
{
if (atomIds.size() != bondIds.size()) {
qDebug() << "Error, addHydrogens called with atom & bond id lists of different size!";
}
// Construct an OBMol, call AddHydrogens and translate the changes
OpenBabel::OBMol obmol = OBMol();
if (a)
obmol.AddHydrogens(obmol.GetAtom(a->index()+1));
else
obmol.AddHydrogens();
// All new atoms in the OBMol must be the additional hydrogens
unsigned int numberAtoms = numAtoms();
int j = 0;
for (unsigned int i = numberAtoms+1; i <= obmol.NumAtoms(); ++i, ++j) {
if (obmol.GetAtom(i)->IsHydrogen()) {
OpenBabel::OBAtom *obatom = obmol.GetAtom(i);
Atom *atom;
if (atomIds.isEmpty())
atom = addAtom();
else if (j < atomIds.size())
atom = addAtom(atomIds.at(j));
else {
qDebug() << "Error - not enough unique ids in addHydrogens.";
break;
}
atom->setOBAtom(obatom);
// Get the neighbor atom
OpenBabel::OBBondIterator iter;
OpenBabel::OBAtom *next = obatom->BeginNbrAtom(iter);
Bond *bond;
if (bondIds.isEmpty())
bond = addBond();
else // Already confirmed by atom ids
bond = addBond(bondIds.at(j));
bond->setEnd(Molecule::atom(atom->index()));
bond->setBegin(Molecule::atom(next->GetIdx()-1));
}
}
for (unsigned int i = 1; i <= numberAtoms; ++i) {
// Warning -- OB atom index off-by-one here
atom(i-1)->setPartialCharge(obmol.GetAtom(i)->GetPartialCharge());
}
}
void Molecule::removeHydrogens(Atom *atom)
{
if (atom) {
// Delete any connected hydrogen atoms
QList neighbors = atom->neighbors();
foreach (unsigned long a, neighbors) {
Atom *nbrAtom = atomById(a);
// we need to check if the atom still exists
if (nbrAtom) {
if (nbrAtom->isHydrogen()) {
removeAtom(a);
}
}
else {
qDebug() << "Error, atom advertising deleted neighbor" << atom->id()
<< a;
}
}
}
// Delete all of the hydrogens
else {
foreach (Atom *atom, m_atomList) {
if (atom->isHydrogen()) {
removeAtom(atom);
}
}
}
}
void Molecule::setDipoleMoment(const Eigen::Vector3d &moment)
{
// Don't leak memory
if (m_dipoleMoment)
delete m_dipoleMoment;
m_dipoleMoment = new Vector3d(moment);
}
const Eigen::Vector3d * Molecule::dipoleMoment() const
{
if (m_dipoleMoment)
return m_dipoleMoment;
else {
// Calculate an estimate
m_dipoleMoment = new Vector3d(0.0, 0.0, 0.0);
foreach (Atom *a, atoms()) {
*m_dipoleMoment += *a->pos() * a->partialCharge();
}
return m_dipoleMoment;
}
}
void Molecule::calculatePartialCharges() const
{
if (numAtoms() < 1 || !m_invalidPartialCharges) {
return;
}
OpenBabel::OBMol obmol = OBMol();
for (unsigned int i = 0; i < numAtoms(); ++i) {
// Warning: OB off-by-one index
atom(i)->setPartialCharge(obmol.GetAtom(i+1)->GetPartialCharge());
}
m_invalidPartialCharges = false;
}
void Molecule::calculateAromaticity() const
{
if (numBonds() < 1 || !m_invalidAromaticity)
return;
OpenBabel::OBMol obmol = OBMol();
for (unsigned int i = 0; i < numBonds(); ++i) {
bond(i)->setAromaticity(obmol.GetBond(i)->IsAromatic());
}
m_invalidAromaticity = false;
}
unsigned int Molecule::numAtoms() const
{
QReadLocker lock(m_lock);
return m_atomList.size();
}
unsigned int Molecule::numBonds() const
{
QReadLocker lock(m_lock);
return m_bondList.size();
}
unsigned int Molecule::numCubes() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->cubeList.size();
}
unsigned int Molecule::numMeshes() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->meshList.size();
}
unsigned int Molecule::numResidues() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->residueList.size();
}
unsigned int Molecule::numRings() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->ringList.size();
}
void Molecule::updatePrimitive()
{
Q_D(Molecule);
Primitive *primitive = qobject_cast(sender());
d->invalidGeomInfo = true;
emit primitiveUpdated(primitive);
}
void Molecule::updateAtom()
{
Q_D(Molecule);
Atom *atom = qobject_cast(sender());
d->invalidGeomInfo = true;
emit atomUpdated(atom);
}
void Molecule::updateBond()
{
Q_D(Molecule);
Bond *bond = qobject_cast(sender());
d->invalidGeomInfo = true;
emit bondUpdated(bond);
}
void Molecule::update()
{
Q_D(Molecule);
d->invalidGeomInfo = true;
emit updated();
}
Bond* Molecule::bond(unsigned long id1, unsigned long id2)
{
// Take two atom IDs and see if we have a bond between the two
if (atomById(id1)) {
QList bonds = atomById(id1)->bonds();
foreach (unsigned long id, bonds) {
Bond *bond = bondById(id);
if (bond) {
if (bond->otherAtom(id1) == id2) {
return bond;
}
}
}
}
return 0;
}
Bond* Molecule::bond(const Atom *a1, const Atom *a2)
{
if (a1 && a2) {
return bond(a1->id(), a2->id());
}
else {
return 0;
}
}
bool Molecule::addConformer(const std::vector &conformer,
unsigned int index)
{
if (conformer.size() != m_atomPos->size())
return false;
if (m_atomConformers.size() < index+1) {
unsigned int size = m_atomConformers.size();
for (unsigned int i = size; i <= index; ++i)
m_atomConformers.push_back( new vector(m_atomPos->size()) );
}
*m_atomConformers[index] = conformer;
return true;
}
vector * Molecule::addConformer(unsigned int index)
{
if (index < m_atomConformers.size())
return m_atomConformers[index];
else {
unsigned int size = m_atomConformers.size();
m_atomConformers.resize(index+1);
for (unsigned int i = size; i <= index; ++i)
m_atomConformers[i] = new vector(m_atomPos->size());
return m_atomConformers[index];
}
}
vector * Molecule::conformer(unsigned int index)
{
if (index && index < m_atomConformers.size())
return m_atomConformers[index];
else if (index == 0)
return m_atomPos;
else
return NULL;
}
bool Molecule::setConformer(unsigned int index)
{
// If the index is higher than the size
if (m_atomConformers.size() < index + 1)
return false;
else {
unsigned int size = m_atomPos->size();
m_atomPos = m_atomConformers[index];
while (m_atomPos->size() < size)
m_atomPos->push_back(Eigen::Vector3d::Zero());
return true;
}
}
void Molecule::setAllConformers(const std::vector< std::vector* > conformers)
{
// clearConformers();
m_atomConformers.resize(1);
for (unsigned int i = 0; i < conformers.size(); ++ i) {
m_atomConformers.push_back(conformers[i]);
}
}
void Molecule::clearConformers()
{
for (unsigned int i = 1; i < m_atomConformers.size(); ++i)
delete m_atomConformers[i];
m_atomConformers.resize(1);
}
unsigned int Molecule::numConformers() const
{
return m_atomConformers.size();
}
const std::vector& Molecule::energies() const
{
Q_D(const Molecule);
while (d->energies.size() != numConformers())
d->energies.push_back(0.0);
return d->energies;
}
double Molecule::energy(unsigned int index) const
{
Q_D(const Molecule);
if (index < d->energies.size())
return d->energies[index];
else
return 0.0;
}
void Molecule::setEnergies(const std::vector& energies)
{
Q_D(const Molecule);
d->energies = energies;
}
QList Molecule::atoms() const
{
QReadLocker lock(m_lock);
return m_atomList;
}
QList Molecule::bonds() const
{
QReadLocker lock(m_lock);
return m_bondList;
}
QList Molecule::cubes() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->cubeList;
}
QList Molecule::meshes() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->meshList;
}
QList Molecule::residues() const
{
Q_D(const Molecule);
QReadLocker lock(m_lock);
return d->residueList;
}
QList Molecule::rings()
{
Q_D(Molecule);
// Check is the rings need updating before returning the list
if(d->invalidRings) {
// Now update the rings
foreach(Fragment *ring, d->ringList) {
removeRing(ring);
}
OpenBabel::OBMol obmol = OBMol();
std::vector rings;
rings = obmol.GetSSSR();
foreach(OpenBabel::OBRing *r, rings) {
Fragment *ring = addRing();
foreach(int index, r->_path) {
ring->addAtom(atom(index-1)->id());
}
}
d->invalidRings = false;
}
QReadLocker lock(m_lock);
return d->ringList;
}
OpenBabel::OBMol Molecule::OBMol() const
{
Q_D(const Molecule);
// Right now we make an OBMol each time
OpenBabel::OBMol obmol;
obmol.BeginModify();
foreach (Atom *atom, m_atomList) {
OpenBabel::OBAtom *a = obmol.NewAtom();
OpenBabel::OBAtom obatom = atom->OBAtom();
*a = obatom;
}
foreach (Bond *bond, m_bondList) {
Atom *beginAtom = atomById(bond->beginAtomId());
if (!beginAtom)
continue;
Atom *endAtom = atomById(bond->endAtomId());
if (!endAtom)
continue;
obmol.AddBond(beginAtom->index() + 1,
endAtom->index() + 1, bond->order());
}
obmol.EndModify();
// Copy unit cells
if (d->obunitcell != NULL) {
OpenBabel::OBUnitCell *obunitcell = new OpenBabel::OBUnitCell;
*obunitcell = *d->obunitcell;
obmol.SetData(obunitcell);
}
// Copy OBPairData, if needed
OpenBabel::OBPairData *obproperty;
foreach(const QByteArray &propertyName, dynamicPropertyNames()) {
obproperty = new OpenBabel::OBPairData;
obproperty->SetAttribute(propertyName.data());
obproperty->SetValue(property(propertyName).toByteArray().data());
obmol.SetData(obproperty);
}
// Copy vibrations, if needed
if (d->obvibdata != NULL) {
obmol.SetData(d->obvibdata->Clone(&obmol));
}
// TODO: Copy residue information, cubes, etc.
return obmol;
}
bool Molecule::setOBMol(OpenBabel::OBMol *obmol)
{
// Take an OBMol, copy everything we need and store this object
Q_D(Molecule);
qDebug() << "setOBMol called.";
clear();
// Copy all the parts of the OBMol to our Molecule
blockSignals(true);
qDebug() << "Copying atoms...";
// Begin by copying all of the atoms
std::vector::iterator i;
for (OpenBabel::OBAtom *obatom = obmol->BeginAtom(i); obatom; obatom = obmol->NextAtom(i)) {
Atom *atom = addAtom();
atom->setOBAtom(obatom);
}
qDebug() << "Copying bonds...";
// Now bonds, we use the indices of the atoms to get the bonding right
std::vector::iterator j;
for (OpenBabel::OBBond *obbond = obmol->BeginBond(j); obbond; obbond = obmol->NextBond(j)) {
Bond *bond = addBond();
// Get the begin and end atoms - we use a 0 based index, OB uses 1 based
bond->setAtoms(obbond->GetBeginAtom()->GetIdx()-1,
obbond->GetEndAtom()->GetIdx()-1,
obbond->GetBondOrder());
}
qDebug() << "Copying cubes...";
// Now for the volumetric data
std::vector data = obmol->GetAllData(OpenBabel::OBGenericDataType::GridData);
for (unsigned int i = 0; i < data.size(); ++i) {
QString name = QString(data[i]->GetAttribute().c_str());
OpenBabel::OBGridData *grid = static_cast(data[i]);
OpenBabel::vector3 obmin = grid->GetOriginVector();
Eigen::Vector3d min(obmin.x(), obmin.y(), obmin.z());
OpenBabel::vector3 obmax = grid->GetMaxVector();
Eigen::Vector3d max(obmax.x(), obmax.y(), obmax.z());
int x, y, z;
grid->GetNumberOfPoints(x, y, z);
Eigen::Vector3i points(x, y, z);
Cube *cube = addCube();
cube->setLimits(min, max, points);
cube->setData(grid->GetValues());
cube->setName(name);
// qDebug() << "Cube" << i << "added.";
}
qDebug() << "Copying residues...";
// Copy the residues across...
std::vector residues;
OpenBabel::OBResidueIterator iResidue;
short chainNumber = 0;
QHash chains;
for (OpenBabel::OBResidue *obres = static_cast(obmol->BeginResidue(iResidue));
obres; obres = static_cast(obmol->NextResidue(iResidue))) {
/// Copy these residues!
Residue *residue = addResidue();
residue->setName(obres->GetName().c_str());
residue->setNumber(obres->GetNumString().c_str());
if (!chains.contains(obres->GetChain())) {
chains[obres->GetChain()] = chainNumber;
chainNumber++;
}
residue->setChainID(obres->GetChain());
residue->setChainNumber(chains.value(obres->GetChain()));
std::vector obatoms = obres->GetAtoms();
foreach (OpenBabel::OBAtom *obatom, obatoms) {
unsigned long atomId = atom(obatom->GetIdx()-1)->id();
residue->addAtom(atomId);
residue->setAtomId(atomId, obres->GetAtomID(obatom).c_str());
}
std::vector obbonds = obres->GetBonds();
foreach (OpenBabel::OBBond *obbond, obbonds) {
residue->addBond(bond(obbond->GetIdx())->id());
}
}
qDebug() << "Copying other data...";
// Copy the dipole moment of the molecule
OpenBabel::OBVectorData *vd = (OpenBabel::OBVectorData*)obmol->GetData("Dipole Moment");
if (vd) {
OpenBabel::vector3 moment = vd->GetData();
m_dipoleMoment = new Vector3d(moment.x(), moment.y(), moment.z());
}
// If available, copy the unit cell
OpenBabel::OBUnitCell *obunitcell = static_cast(obmol->GetData(OpenBabel::OBGenericDataType::UnitCell));
if (obunitcell) {
d->obunitcell = new OpenBabel::OBUnitCell;
*d->obunitcell = *obunitcell;
}
// (that could return NULL, but other methods know they could get NULL)
// Copy forces, if present and valid
if (obmol->HasData(OpenBabel::OBGenericDataType::ConformerData)) {
OpenBabel::OBConformerData *cd = static_cast(obmol->GetData(OpenBabel::OBGenericDataType::ConformerData));
if (cd) {
std::vector< std::vector > allForces = cd->GetForces();
// check for validity (i.e., we have some forces, one for each atom
if (allForces.size() && allForces[0].size() == numAtoms()) {
OpenBabel::vector3 force;
foreach (Atom *atom, m_atomList) { // loop through each atom
force = allForces[0][atom->index()];
qDebug() << " copying force " << force.x() << force.y() << force.z();
atom->setForceVector(Eigen::Vector3d(force.x(), force.y(), force.z()));
} // end setting forces on each atom
}
}
} // end HasData(ConformerData)
// Copy any vibration data if possible
if (obmol->HasData(OpenBabel::OBGenericDataType::VibrationData)) {
OpenBabel::OBVibrationData *vibData = static_cast(obmol->GetData(OpenBabel::OBGenericDataType::VibrationData));
d->obvibdata = vibData;
}
// Finally, sync OBPairData to dynamic properties
OpenBabel::OBDataIterator dIter;
OpenBabel::OBPairData *property;
data = obmol->GetAllData(OpenBabel::OBGenericDataType::PairData);
for (dIter = data.begin(); dIter != data.end(); ++dIter) {
property = static_cast(*dIter);
setProperty(property->GetAttribute().c_str(), property->GetValue().c_str());
}
blockSignals(false);
return true;
}
OpenBabel::OBUnitCell *Molecule::OBUnitCell() const
{
Q_D(const Molecule);
return d->obunitcell;
}
bool Molecule::setOBUnitCell(OpenBabel::OBUnitCell *obunitcell)
{
Q_D(Molecule);
d->obunitcell = obunitcell;
if (obunitcell == NULL) {
// delete it from our private obmol
if (d->obmol)
d->obmol->DeleteData(OpenBabel::OBGenericDataType::UnitCell);
}
return true;
}
const Eigen::Vector3d Molecule::center() const
{
Q_D(const Molecule);
if( d->invalidGeomInfo ) computeGeomInfo();
return d->center;
}
const Eigen::Vector3d Molecule::normalVector() const
{
Q_D(const Molecule);
if( d->invalidGeomInfo ) computeGeomInfo();
return d->normalVector;
}
double Molecule::radius() const
{
Q_D(const Molecule);
if( d->invalidGeomInfo ) computeGeomInfo();
return d->radius;
}
const Atom * Molecule::farthestAtom() const
{
Q_D(const Molecule);
if( d->invalidGeomInfo ) computeGeomInfo();
return d->farthestAtom;
}
void Molecule::translate(const Eigen::Vector3d& offset)
{
m_lock->lockForWrite();
if (!m_atomPos)
return; // nothing to do
foreach (Atom *atom, m_atomList) {
(*m_atomPos)[atom->id()] += offset;
emit atomUpdated(atom);
}
m_lock->unlock();
}
void Molecule::clear()
{
Q_D(Molecule);
m_lock->lockForWrite();
m_atoms.resize(0);
foreach (Atom *atom, m_atomList) {
atom->deleteLater();
emit primitiveRemoved(atom);
}
m_atomList.clear();
clearConformers();
delete m_atomPos;
m_atomPos = 0;
delete m_dipoleMoment;
m_dipoleMoment = 0;
delete d->obunitcell;
d->obunitcell = 0;
m_bonds.resize(0);
foreach (Bond *bond, m_bondList) {
bond->deleteLater();
emit primitiveRemoved(bond);
}
m_bondList.clear();
d->cubes.resize(0);
foreach (Cube *cube, d->cubeList) {
cube->deleteLater();
emit primitiveRemoved(cube);
}
d->cubeList.clear();
d->meshes.resize(0);
foreach (Mesh *mesh, d->meshList) {
mesh->deleteLater();
emit primitiveRemoved(mesh);
}
d->meshList.clear();
d->residues.resize(0);
foreach (Residue *residue, d->residueList) {
residue->deleteLater();
emit primitiveRemoved(residue);
}
d->residueList.clear();
d->rings.resize(0);
foreach (Fragment *ring, d->ringList) {
ring->deleteLater();
emit primitiveRemoved(ring);
}
d->ringList.clear();
m_lock->unlock();
}
Molecule &Molecule::operator=(const Molecule& other)
{
// FIXME: Copy all the other stuff in the molecule!
//Q_D(Molecule);
clear();
//const MoleculePrivate *e = other.d_func();
m_atoms.resize(other.m_atoms.size(), 0);
if (other.m_atomPos) {
m_atomConformers.resize(1);
m_atomConformers[0] = new vector;
m_atomPos = m_atomConformers[0];
m_atomPos->reserve(100);
m_atomPos->clear();
m_atomPos->resize(other.m_atomPos->size());
}
else
qDebug() << "Other atom has a position list of size zero!";
m_bonds.resize(other.m_bonds.size(), 0);
// Copy the atoms and bonds over
unsigned int size = other.m_atoms.size();
for (unsigned int i = 0; i < size; ++i) {
if (other.m_atoms.at(i) > 0) {
Atom *atom = new Atom(this);
atom->setId(other.m_atoms[i]->id());
atom->setIndex(other.m_atoms[i]->index());
m_atoms[i] = atom;
m_atomList.push_back(atom);
*atom = *(other.m_atoms[i]);
emit primitiveAdded(atom);
}
}
size = other.m_bonds.size();
for (unsigned int i = 0; i < size; ++i) {
if (other.m_bonds.at(i)) {
Bond *bond = new Bond(this);
*bond = *(other.m_bonds[i]);
bond->setId(other.m_bonds[i]->id());
bond->setIndex(other.m_bonds[i]->index());
m_bonds[i] = bond;
m_bondList.push_back(bond);
// Add the bond to it's atoms
bond->beginAtom()->addBond(bond);
bond->endAtom()->addBond(bond);
emit primitiveAdded(bond);
}
}
return *this;
}
Molecule &Molecule::operator+=(const Molecule& other)
{
// FIXME: Copy all the other stuff in the molecule!
//const MoleculePrivate *e = other.d_func();
// Create a temporary map from the old indices to the new for bonding
QList map;
foreach (Atom *a, other.m_atomList) {
Atom *atom = addAtom();
*atom = *a;
map.push_back(atom->id());
emit primitiveAdded(atom);
}
foreach (Bond *b, other.m_bondList) {
Bond *bond = addBond();
*bond = *b;
bond->setBegin(atomById(map.at(other.atomById(b->beginAtomId())->index())));
bond->setEnd(atomById(map.at(other.atomById(b->endAtomId())->index())));
emit primitiveAdded(bond);
}
return *this;
}
void Molecule::computeGeomInfo() const
{
Q_D(const Molecule);
d->invalidGeomInfo = true;
d->farthestAtom = 0;
d->center.setZero();
d->normalVector.setZero();
d->radius = 1.0;
// In order to calculate many parameters we need at least two atoms
if(numAtoms() > 1) {
// compute center
foreach (Atom *atom, m_atomList)
d->center += *atom->pos();
d->center /= numAtoms();
// compute the normal vector to the molecule's best-fitting plane
int i = 0;
Vector3d ** atomPositions = new Vector3d*[numAtoms()];
foreach (Atom *atom, m_atomList)
atomPositions[i++] = &m_atomPos->at(atom->id());
Eigen::Hyperplane planeCoeffs;
Eigen::fitHyperplane(numAtoms(), atomPositions, &planeCoeffs);
delete[] atomPositions;
d->normalVector = planeCoeffs.normal();
// compute radius and the farthest atom
d->radius = -1.0; // so that ( squaredDistanceToCenter > d->radius ) is true for at least one atom.
foreach (Atom *atom, m_atomList) {
double distanceToCenter = (*atom->pos() - d->center).norm();
if(distanceToCenter > d->radius) {
d->radius = distanceToCenter;
d->farthestAtom = atom;
}
}
}
d->invalidGeomInfo = false;
}
} // End namespace Avogadro
#include "molecule.moc"