2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements neighborhood searching for analysis (from nbsearch.h).
40 * The grid implementation could still be optimized in several different ways:
41 * - Triclinic grid cells are not the most efficient shape, but make PBC
43 * - Precalculating the required PBC shift for a pair of cells outside the
44 * inner loop. After this is done, it should be quite straightforward to
45 * move to rectangular cells.
46 * - Pruning grid cells from the search list if they are completely outside
47 * the sphere that is being considered.
48 * - A better heuristic could be added for falling back to simple loops for a
49 * small number of reference particles.
50 * - A better heuristic for selecting the grid size.
51 * - A multi-level grid implementation could be used to be able to use small
52 * grids for short cutoffs with very inhomogeneous particle distributions
53 * without a memory cost.
55 * \author Teemu Murtola <teemu.murtola@gmail.com>
56 * \ingroup module_selection
58 #include "gromacs/selection/nbsearch.h"
65 #include "gromacs/legacyheaders/smalloc.h"
66 #include "gromacs/legacyheaders/typedefs.h"
67 #include "gromacs/legacyheaders/pbc.h"
68 #include "gromacs/legacyheaders/vec.h"
69 #include "gromacs/legacyheaders/thread_mpi/mutex.h"
71 #include "gromacs/selection/position.h"
72 #include "gromacs/utility/gmxassert.h"
80 /********************************************************************
81 * Implementation class declarations
84 class AnalysisNeighborhoodSearchImpl
87 typedef AnalysisNeighborhoodPairSearch::ImplPointer
88 PairSearchImplPointer;
90 explicit AnalysisNeighborhoodSearchImpl(real cutoff);
91 ~AnalysisNeighborhoodSearchImpl();
93 void init(AnalysisNeighborhood::SearchMode mode,
94 const t_pbc *pbc, int n, const rvec x[]);
96 //! Calculates offsets to neighboring grid cells that should be considered.
97 void initGridCellNeighborList();
99 * Determines a suitable grid size and sets up the cells.
101 * \param[in] pbc Information about the box.
102 * \returns false if grid search is not suitable.
104 bool initGridCells(const t_pbc *pbc);
106 * Sets ua a search grid for a given box.
108 * \param[in] pbc Information about the box.
109 * \returns false if grid search is not suitable.
111 bool initGrid(const t_pbc *pbc);
112 //! Clears all grid cells.
113 void clearGridCells();
115 * Maps a point into a grid cell.
117 * \param[in] x Point to map.
118 * \param[out] cell Indices of the grid cell in which \p x lies.
120 * \p x should be within the triclinic unit cell.
122 void mapPointToGridCell(const rvec x, ivec cell) const;
124 * Calculates linear index of a grid cell.
126 * \param[in] cell Cell indices.
127 * \returns Linear index of \p cell.
129 int getGridCellIndex(const ivec cell) const;
131 * Adds an index into a grid cell.
133 * \param[in] cell Cell into which \p i should be added.
134 * \param[in] i Index to add.
136 void addToGridCell(const ivec cell, int i);
138 /** Whether to try grid searching. */
142 /** The cutoff squared. */
145 /** Number of reference points for the current frame. */
147 /** Reference point positions. */
149 /** Reference position ids (NULL if not available). */
154 /** Number of excluded reference positions for current test particle. */
156 /** Exclusions for current test particle. */
159 /** Whether grid searching is actually used for the current positions. */
161 /** Array allocated for storing in-unit-cell reference positions. */
163 /** Allocation count for xref_alloc. */
165 /** false if the box is rectangular. */
167 /** Box vectors of a single grid cell. */
169 /** The reciprocal cell vectors as columns; the inverse of \p cellbox. */
171 /** Number of cells along each dimension. */
173 /** Total number of cells. */
175 /** Number of reference positions in each cell. */
177 /** List of reference positions in each cell. */
179 /** Allocation counts for each \p catom[i]. */
181 /** Allocation count for the per-cell arrays. */
183 /** Number of neighboring cells to consider. */
185 /** Offsets of the neighboring cells to consider. */
187 /** Allocation count for \p gnboffs. */
190 PairSearchImplPointer pairSearch_;
192 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisNeighborhoodSearchImpl);
195 class AnalysisNeighborhoodPairSearchImpl
198 explicit AnalysisNeighborhoodPairSearchImpl(const AnalysisNeighborhoodSearchImpl &search)
199 : search_(search), testIndex_(0)
202 clear_ivec(testcell_);
206 //! Clears the loop indices.
208 //! Initializes a search to find reference positions neighboring \p x.
209 void startSearch(const rvec x, int testIndex);
210 //! Checks whether a reference positiong should be excluded.
211 bool isExcluded(int j);
212 //! Searches for the next neighbor.
213 template <class Action>
214 bool searchNext(Action action);
216 const AnalysisNeighborhoodSearchImpl &search_;
218 /** Stores test position during a pair loop. */
220 /** Stores the previous returned position during a pair loop. */
222 /** Stores the current exclusion index during loops. */
224 /** Stores the test particle cell index during loops. */
226 /** Stores the current cell neighbor index during pair loops. */
228 /** Stores the index within the current cell during pair loops. */
231 GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisNeighborhoodPairSearchImpl);
234 /********************************************************************
235 * AnalysisNeighborhoodSearchImpl
238 AnalysisNeighborhoodSearchImpl::AnalysisNeighborhoodSearchImpl(real cutoff)
239 : pairSearch_(new AnalysisNeighborhoodPairSearchImpl(*this))
245 cutoff_ = GMX_REAL_MAX;
248 cutoff2_ = sqr(cutoff_);
264 clear_mat(recipcell_);
265 clear_ivec(ncelldim_);
277 AnalysisNeighborhoodSearchImpl::~AnalysisNeighborhoodSearchImpl()
279 GMX_RELEASE_ASSERT(pairSearch_.unique(),
280 "Dangling AnalysisNeighborhoodPairSearch reference");
285 for (int ci = 0; ci < ncells_; ++ci)
291 sfree(catom_nalloc_);
295 void AnalysisNeighborhoodSearchImpl::initGridCellNeighborList()
297 int maxx, maxy, maxz;
300 /* Find the extent of the sphere in triclinic coordinates */
301 maxz = (int)(cutoff_ * recipcell_[ZZ][ZZ]) + 1;
302 rvnorm = sqrt(sqr(recipcell_[YY][YY]) + sqr(recipcell_[ZZ][YY]));
303 maxy = (int)(cutoff_ * rvnorm) + 1;
304 rvnorm = sqrt(sqr(recipcell_[XX][XX]) + sqr(recipcell_[YY][XX])
305 + sqr(recipcell_[ZZ][XX]));
306 maxx = (int)(cutoff_ * rvnorm) + 1;
308 /* Calculate the number of cells and reallocate if necessary */
309 ngridnb_ = (2 * maxx + 1) * (2 * maxy + 1) * (2 * maxz + 1);
310 if (gnboffs_nalloc_ < ngridnb_)
312 gnboffs_nalloc_ = ngridnb_;
313 srenew(gnboffs_, gnboffs_nalloc_);
316 /* Store the whole cube */
317 /* TODO: Prune off corners that are not needed */
319 for (int x = -maxx; x <= maxx; ++x)
321 for (int y = -maxy; y <= maxy; ++y)
323 for (int z = -maxz; z <= maxz; ++z)
334 bool AnalysisNeighborhoodSearchImpl::initGridCells(const t_pbc *pbc)
336 const real targetsize =
337 pow(pbc->box[XX][XX] * pbc->box[YY][YY] * pbc->box[ZZ][ZZ]
338 * 10 / nref_, static_cast<real>(1./3.));
341 for (int dd = 0; dd < DIM; ++dd)
343 ncelldim_[dd] = static_cast<int>(pbc->box[dd][dd] / targetsize);
344 ncells_ *= ncelldim_[dd];
345 if (ncelldim_[dd] < 3)
350 /* Reallocate if necessary */
351 if (cells_nalloc_ < ncells_)
353 srenew(ncatoms_, ncells_);
354 srenew(catom_, ncells_);
355 srenew(catom_nalloc_, ncells_);
356 for (int i = cells_nalloc_; i < ncells_; ++i)
359 catom_nalloc_[i] = 0;
361 cells_nalloc_ = ncells_;
366 bool AnalysisNeighborhoodSearchImpl::initGrid(const t_pbc *pbc)
368 /* TODO: This check could be improved. */
369 if (0.5*pbc->max_cutoff2 < cutoff2_)
374 if (!initGridCells(pbc))
379 bTric_ = TRICLINIC(pbc->box);
382 for (int dd = 0; dd < DIM; ++dd)
384 svmul(1.0 / ncelldim_[dd], pbc->box[dd], cellbox_[dd]);
386 m_inv_ur0(cellbox_, recipcell_);
390 for (int dd = 0; dd < DIM; ++dd)
392 cellbox_[dd][dd] = pbc->box[dd][dd] / ncelldim_[dd];
393 recipcell_[dd][dd] = 1.0 / cellbox_[dd][dd];
396 initGridCellNeighborList();
400 void AnalysisNeighborhoodSearchImpl::mapPointToGridCell(const rvec x,
407 tmvmul_ur0(recipcell_, x, tx);
408 for (int dd = 0; dd < DIM; ++dd)
410 cell[dd] = static_cast<int>(tx[dd]);
415 for (int dd = 0; dd < DIM; ++dd)
417 cell[dd] = static_cast<int>(x[dd] * recipcell_[dd][dd]);
422 int AnalysisNeighborhoodSearchImpl::getGridCellIndex(const ivec cell) const
424 return cell[XX] + cell[YY] * ncelldim_[XX]
425 + cell[ZZ] * ncelldim_[XX] * ncelldim_[YY];
428 void AnalysisNeighborhoodSearchImpl::clearGridCells()
430 for (int ci = 0; ci < ncells_; ++ci)
436 void AnalysisNeighborhoodSearchImpl::addToGridCell(const ivec cell, int i)
438 const int ci = getGridCellIndex(cell);
440 if (ncatoms_[ci] == catom_nalloc_[ci])
442 catom_nalloc_[ci] += 10;
443 srenew(catom_[ci], catom_nalloc_[ci]);
445 catom_[ci][ncatoms_[ci]++] = i;
448 void AnalysisNeighborhoodSearchImpl::init(
449 AnalysisNeighborhood::SearchMode mode,
450 const t_pbc *pbc, int n, const rvec x[])
452 pbc_ = const_cast<t_pbc *>(pbc);
454 // TODO: Consider whether it would be possible to support grid searching in
456 if (mode == AnalysisNeighborhood::eSearchMode_Simple
457 || pbc_ == NULL || pbc_->ePBC != epbcXYZ)
463 // TODO: Actually implement forcing eSearchMode_Grid
464 bGrid_ = initGrid(pbc_);
468 if (xref_nalloc_ < n)
470 srenew(xref_alloc_, n);
476 for (int i = 0; i < n; ++i)
478 copy_rvec(x[i], xref_alloc_[i]);
480 put_atoms_in_triclinic_unitcell(ecenterTRIC, pbc_->box, n, xref_alloc_);
481 for (int i = 0; i < n; ++i)
485 mapPointToGridCell(xref_[i], refcell);
486 addToGridCell(refcell, i);
498 * Sets the exclusions for the next neighborhood search.
500 * \param[in,out] d Neighborhood search data structure.
501 * \param[in] nexcl Number of reference positions to exclude from next
503 * \param[in] excl Indices of reference positions to exclude.
505 * The set exclusions remain in effect until the next call of this function.
508 gmx_ana_nbsearch_set_excl(gmx_ana_nbsearch_t *d, int nexcl, int excl[])
516 /********************************************************************
517 * AnalysisNeighborhoodPairSearchImpl
520 void AnalysisNeighborhoodPairSearchImpl::reset()
528 bool AnalysisNeighborhoodPairSearchImpl::isExcluded(int j)
530 if (exclind_ < search_.nexcl_)
534 while (exclind_ < search_.nexcl_
535 && search_.excl_[exclind_] < search_.refid_[j])
539 if (exclind_ < search_.nexcl_
540 && search_.refid_[j] == search_.excl_[exclind_])
548 while (search_.bGrid_ && exclind_ < search_.nexcl_
549 && search_.excl_[exclind_] < j)
553 if (search_.excl_[exclind_] == j)
563 void AnalysisNeighborhoodPairSearchImpl::startSearch(const rvec x, int testIndex)
565 testIndex_ = testIndex;
566 copy_rvec(x, xtest_);
569 put_atoms_in_triclinic_unitcell(ecenterTRIC, search_.pbc_->box,
571 search_.mapPointToGridCell(xtest_, testcell_);
576 template <class Action>
577 bool AnalysisNeighborhoodPairSearchImpl::searchNext(Action action)
582 int cai = prevcai_ + 1;
584 for (; nbi < search_.ngridnb_; ++nbi)
588 ivec_add(testcell_, search_.gnboffs_[nbi], cell);
589 cell[XX] = (cell[XX] + search_.ncelldim_[XX]) % search_.ncelldim_[XX];
590 cell[YY] = (cell[YY] + search_.ncelldim_[YY]) % search_.ncelldim_[YY];
591 cell[ZZ] = (cell[ZZ] + search_.ncelldim_[ZZ]) % search_.ncelldim_[ZZ];
593 const int ci = search_.getGridCellIndex(cell);
594 /* TODO: Calculate the required PBC shift outside the inner loop */
595 for (; cai < search_.ncatoms_[ci]; ++cai)
597 const int i = search_.catom_[ci][cai];
603 pbc_dx_aiuc(search_.pbc_, xtest_, search_.xref_[i], dx);
604 const real r2 = norm2(dx);
605 if (r2 <= search_.cutoff2_)
622 for (int i = previ_ + 1; i < search_.nref_; ++i)
631 pbc_dx(search_.pbc_, xtest_, search_.xref_[i], dx);
635 rvec_sub(xtest_, search_.xref_[i], dx);
637 const real r2 = norm2(dx);
638 if (r2 <= search_.cutoff2_)
651 } // namespace internal
657 * Search action to find the next neighbor.
659 * Used as the action for AnalysisNeighborhoodPairSearchImpl::searchNext() to
660 * find the next neighbor.
662 * Simply breaks the loop on the first found neighbor.
664 bool withinAction(int /*i*/, real /*r2*/)
670 * Search action find the minimum distance.
672 * Used as the action for AnalysisNeighborhoodPairSearchImpl::searchNext() to
673 * find the nearest neighbor.
675 * With this action, AnalysisNeighborhoodPairSearchImpl::searchNext() always
676 * returns false, and the output is put into the variables passed by pointer
677 * into the constructor. If no neighbors are found, the output variables are
678 * not modified, i.e., the caller must initialize them.
684 * Initializes the action with given output locations.
686 * \param[out] closestPoint Index of the closest reference location.
687 * \param[out] minDist2 Minimum distance squared.
689 * The constructor call does not modify the pointed values, but only
690 * stores the pointers for later use.
691 * See the class description for additional semantics.
693 MindistAction(int *closestPoint, real *minDist2)
694 : closestPoint_(*closestPoint), minDist2_(*minDist2)
698 //! Processes a neighbor to find the nearest point.
699 bool operator()(int i, real r2)
713 GMX_DISALLOW_ASSIGN(MindistAction);
718 /********************************************************************
719 * AnalysisNeighborhood::Impl
722 class AnalysisNeighborhood::Impl
725 typedef AnalysisNeighborhoodSearch::ImplPointer SearchImplPointer;
726 typedef std::vector<SearchImplPointer> SearchList;
728 Impl() : cutoff_(0), mode_(eSearchMode_Automatic)
733 SearchList::const_iterator i;
734 for (i = searchList_.begin(); i != searchList_.end(); ++i)
736 GMX_RELEASE_ASSERT(i->unique(),
737 "Dangling AnalysisNeighborhoodSearch reference");
741 SearchImplPointer getSearch();
743 tMPI::mutex createSearchMutex_;
744 SearchList searchList_;
749 AnalysisNeighborhood::Impl::SearchImplPointer
750 AnalysisNeighborhood::Impl::getSearch()
752 tMPI::lock_guard<tMPI::mutex> lock(createSearchMutex_);
753 // TODO: Consider whether this needs to/can be faster, e.g., by keeping a
754 // separate pool of unused search objects.
755 SearchList::const_iterator i;
756 for (i = searchList_.begin(); i != searchList_.end(); ++i)
763 SearchImplPointer search(new internal::AnalysisNeighborhoodSearchImpl(cutoff_));
764 searchList_.push_back(search);
768 /********************************************************************
769 * AnalysisNeighborhood
772 AnalysisNeighborhood::AnalysisNeighborhood()
777 AnalysisNeighborhood::~AnalysisNeighborhood()
781 void AnalysisNeighborhood::setCutoff(real cutoff)
783 GMX_RELEASE_ASSERT(impl_->searchList_.empty(),
784 "Changing the cutoff after initSearch() not currently supported");
785 impl_->cutoff_ = cutoff;
788 void AnalysisNeighborhood::setMode(SearchMode mode)
793 AnalysisNeighborhood::SearchMode AnalysisNeighborhood::mode() const
798 AnalysisNeighborhoodSearch
799 AnalysisNeighborhood::initSearch(const t_pbc *pbc, int n, const rvec x[])
801 Impl::SearchImplPointer search(impl_->getSearch());
802 search->init(mode(), pbc, n, x);
803 return AnalysisNeighborhoodSearch(search);
806 AnalysisNeighborhoodSearch
807 AnalysisNeighborhood::initSearch(const t_pbc *pbc, const gmx_ana_pos_t *p)
809 Impl::SearchImplPointer search(impl_->getSearch());
810 search->init(mode(), pbc, p->nr, p->x);
811 search->refid_ = p->m.refid;
812 return AnalysisNeighborhoodSearch(search);
815 /********************************************************************
816 * AnalysisNeighborhoodSearch
819 AnalysisNeighborhoodSearch::AnalysisNeighborhoodSearch()
823 AnalysisNeighborhoodSearch::AnalysisNeighborhoodSearch(const ImplPointer &impl)
828 void AnalysisNeighborhoodSearch::reset()
833 AnalysisNeighborhood::SearchMode AnalysisNeighborhoodSearch::mode() const
835 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
836 return (impl_->bGrid_
837 ? AnalysisNeighborhood::eSearchMode_Grid
838 : AnalysisNeighborhood::eSearchMode_Simple);
841 bool AnalysisNeighborhoodSearch::isWithin(const rvec x) const
843 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
844 internal::AnalysisNeighborhoodPairSearchImpl pairSearch(*impl_);
845 pairSearch.startSearch(x, 0);
846 return pairSearch.searchNext(&withinAction);
849 bool AnalysisNeighborhoodSearch::isWithin(const gmx_ana_pos_t *p, int i) const
851 return isWithin(p->x[i]);
854 real AnalysisNeighborhoodSearch::minimumDistance(const rvec x) const
856 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
857 internal::AnalysisNeighborhoodPairSearchImpl pairSearch(*impl_);
858 pairSearch.startSearch(x, 0);
859 real minDist2 = impl_->cutoff2_;
860 int closestPoint = -1;
861 MindistAction action(&closestPoint, &minDist2);
862 (void)pairSearch.searchNext(action);
863 return sqrt(minDist2);
866 real AnalysisNeighborhoodSearch::minimumDistance(const gmx_ana_pos_t *p, int i) const
868 return minimumDistance(p->x[i]);
871 AnalysisNeighborhoodPair
872 AnalysisNeighborhoodSearch::nearestPoint(const rvec x) const
874 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
875 internal::AnalysisNeighborhoodPairSearchImpl pairSearch(*impl_);
876 pairSearch.startSearch(x, 0);
877 real minDist2 = impl_->cutoff2_;
878 int closestPoint = -1;
879 MindistAction action(&closestPoint, &minDist2);
880 (void)pairSearch.searchNext(action);
881 return AnalysisNeighborhoodPair(closestPoint, 0);
884 AnalysisNeighborhoodPair
885 AnalysisNeighborhoodSearch::nearestPoint(const gmx_ana_pos_t *p, int i) const
887 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
888 internal::AnalysisNeighborhoodPairSearchImpl pairSearch(*impl_);
889 pairSearch.startSearch(p->x[i], 0);
890 real minDist2 = impl_->cutoff2_;
891 int closestPoint = -1;
892 MindistAction action(&closestPoint, &minDist2);
893 (void)pairSearch.searchNext(action);
894 return AnalysisNeighborhoodPair(closestPoint, i);
897 AnalysisNeighborhoodPairSearch
898 AnalysisNeighborhoodSearch::startPairSearch(const rvec x)
900 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
901 GMX_RELEASE_ASSERT(impl_->pairSearch_.unique(),
902 "Multiple concurrent searches not currently supported");
903 impl_->pairSearch_->startSearch(x, 0);
904 return AnalysisNeighborhoodPairSearch(impl_->pairSearch_);
907 AnalysisNeighborhoodPairSearch
908 AnalysisNeighborhoodSearch::startPairSearch(const gmx_ana_pos_t *p, int i)
910 GMX_RELEASE_ASSERT(impl_, "Accessing an invalid search object");
911 GMX_RELEASE_ASSERT(impl_->pairSearch_.unique(),
912 "Multiple concurrent searches not currently supported");
913 impl_->pairSearch_->startSearch(p->x[i], i);
914 return AnalysisNeighborhoodPairSearch(impl_->pairSearch_);
917 /********************************************************************
918 * AnalysisNeighborhoodPairSearch
921 AnalysisNeighborhoodPairSearch::AnalysisNeighborhoodPairSearch(
922 const ImplPointer &impl)
927 bool AnalysisNeighborhoodPairSearch::findNextPair(AnalysisNeighborhoodPair *pair)
929 if (impl_->searchNext(&withinAction))
931 *pair = AnalysisNeighborhoodPair(impl_->previ_, impl_->testIndex_);
934 *pair = AnalysisNeighborhoodPair();