/*********************************************************************
NeighborList - NeighborList class
Copyright (C) 2009 by Tim Vandermeersch
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
#include
using namespace std;
namespace Avogadro
{
NeighborList::NeighborList(Molecule* mol, double rcut, int boxSize) : m_mol(mol), m_rcut(rcut)
{
m_rcut2 = rcut*rcut;
m_boxSize = boxSize;
m_edgeLength = m_rcut / m_boxSize;
m_updateCounter = 0;
initOffsetMap();
initOneTwo();
initCells();
initGhostMap();
}
QList NeighborList::nbrs(Atom *atom)
{
m_r2.clear();
m_r2.reserve(m_mol->numAtoms());
QList atoms;
Eigen::Vector3i index(cellIndexes(atom->pos()));
std::vector::const_iterator i;
// Use the offset map to find neighboring cells
for (i = m_offsetMap.begin(); i != m_offsetMap.end(); ++i) {
// add the offset to the cell index for atom's cell
Eigen::Vector3i offset = index + *i;
// use the ghost map to handle indexes near border:
// a) periodic boundary conditions --> wrap around
// b) otherwise --> last empty cell
unsigned int cell = cellIndex(m_ghostMap.at(ghostIndex(offset)));
for (atom_iter j = m_cells[cell].begin(); j != m_cells[cell].end(); ++j) {
// make sure to only return unique pairs
if (atom->index() >= (*j)->index())
continue;
if (IsOneTwo(atom->index(), (*j)->index()))
continue;
if (IsOneThree(atom->index(), (*j)->index()))
continue;
const double R2 = ( *((*j)->pos()) - *(atom->pos()) ).squaredNorm();
if (R2 > m_rcut2)
continue;
m_r2.push_back(R2);
atoms.append(*j);
}
}
return atoms;
}
QList NeighborList::nbrs(const Eigen::Vector3f *pos)
{
m_r2.clear();
m_r2.reserve(m_mol->numAtoms());
QList atoms;
Eigen::Vector3d dpos(pos->cast());
Eigen::Vector3i index(cellIndexes(&dpos));
std::vector::const_iterator i;
// Use the offset map to find neighboring cells
for (i = m_offsetMap.begin(); i != m_offsetMap.end(); ++i) {
// add the offset to the cell index for atom's cell
Eigen::Vector3i offset = index + *i;
// use the ghost map to handle indexes near border:
// a) periodic boundary conditions --> wrap around
// b) otherwise --> last empty cell
unsigned int cell = cellIndex(m_ghostMap.at(ghostIndex(offset)));
for (atom_iter j = m_cells[cell].begin(); j != m_cells[cell].end(); ++j) {
const double R2 = ( *((*j)->pos()) - dpos).squaredNorm();
if (R2 > m_rcut2)
continue;
m_r2.push_back(R2);
atoms.append(*j);
}
}
return atoms;
}
void NeighborList::update()
{
m_updateCounter++;
if (m_updateCounter > 10) {
initCells();
updateCells();
m_updateCounter = 0;
}
}
void NeighborList::initOneTwo()
{
m_oneTwo.resize(m_mol->numAtoms());
m_oneThree.resize(m_mol->numAtoms());
foreach (Atom *atom, m_mol->atoms()) {
foreach (unsigned long id1, atom->neighbors()) {
Atom *nbr1 = m_mol->atomById(id1);
m_oneTwo[atom->index()].push_back(nbr1->index());
m_oneTwo[nbr1->index()].push_back(atom->index());
foreach (unsigned long id2, nbr1->neighbors()) {
Atom *nbr2 = m_mol->atomById(id2);
if (atom->index() == nbr2->index())
continue;
m_oneThree[atom->index()].push_back(nbr2->index());
m_oneThree[nbr2->index()].push_back(atom->index());
}
}
}
}
void NeighborList::initCells()
{
// find min & max
foreach (Atom *atom, m_mol->atoms()) {
Eigen::Vector3d pos = *(atom->pos());
if (!atom->index()) {
m_min = m_max = pos;
} else {
if (pos.x() > m_max.x())
m_max.x() = pos.x();
else if (pos.x() < m_min.x())
m_min.x() = pos.x();
if (pos.y() > m_max.y())
m_max.y() = pos.y();
else if (pos.y() < m_min.y())
m_min.y() = pos.y();
if (pos.z() > m_max.z())
m_max.z() = pos.z();
else if (pos.z() < m_min.z())
m_min.z() = pos.z();
}
}
// set the dimentions
m_dim.x() = floor( (m_max.x() - m_min.x()) / m_edgeLength) + 1;
m_dim.y() = floor( (m_max.y() - m_min.y()) / m_edgeLength) + 1;
m_dim.z() = floor( (m_max.z() - m_min.z()) / m_edgeLength) + 1;
m_xyDim = m_dim.x() * m_dim.y();
updateCells();
}
void NeighborList::updateCells()
{
// add atoms to their cells
m_cells.clear();
// the last cell is always empty and can be used for all ghost cells
// in non-periodic boundary conditions.
m_cells.resize(m_xyDim * m_dim.z() + 1);
foreach (Atom *atom, m_mol->atoms()) {
m_cells[cellIndex(*(atom->pos()))].push_back(&*atom);
}
}
bool NeighborList::insideShpere(const Eigen::Vector3i &index)
{
int i = abs(index.x());
if (i) i--;
int j = abs(index.y());
if (j) j--;
int k = abs(index.z());
if (k) k--;
if (Eigen::Vector3i(i, j, k).squaredNorm() < m_rcut2)
return true;
return false;
}
void NeighborList::initOffsetMap()
{
int dim = 2 * m_boxSize + 1;
m_offsetMap.clear();
for (int i = 0; i < dim; ++i)
for (int j = 0; j < dim; ++j)
for (int k = 0; k < dim; ++k) {
Eigen::Vector3i index(i - m_boxSize, j - m_boxSize, k - m_boxSize);
if (insideShpere(index))
m_offsetMap.push_back( index );
}
}
void NeighborList::initGhostMap(bool periodic)
{
int xDim = 2 * m_boxSize + m_dim.x() + 2;
int yDim = 2 * m_boxSize + m_dim.y() + 2;
int zDim = 2 * m_boxSize + m_dim.z() + 2;
m_ghostX = xDim;
m_ghostXY = xDim * yDim;
int start = - m_boxSize - 1;
m_ghostMap.resize(xDim * yDim * zDim);
for (int i = start; i < m_dim.x() - start; ++i)
for (int j = start; j < m_dim.y() - start; ++j)
for (int k = start; k < m_dim.z() - start; ++k) {
unsigned int ghostCell = ghostIndex(i, j, k);
int u = i, v = j, w = k;
if (periodic) {
// wrap around
if (i < 0)
u = m_dim.x() + i + 1;
else if (i >= m_dim.x())
u = i - m_dim.x();
if (j < 0)
v = m_dim.y() + j + 1;
else if (j >= m_dim.y())
v = j - m_dim.y();
if (k < 0)
w = m_dim.z() + k + 1;
else if (k >= m_dim.z())
w = k - m_dim.z();
} else {
if ( (i < 0) || (j < 0) || (k < 0) ||
(i >= m_dim.x()) || (j >= m_dim.y()) || (k >= m_dim.z()) ) {
// point to last cell which is always empty
u = m_cells.size() - 1;
v = 0;
w = 0;
}
}
m_ghostMap[ghostCell] = Eigen::Vector3i(u, v, w);
}
}
} // end namespace OpenBabel
//! \file nbrlist.cpp
//! \brief NbrList class