2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009-2018, The GROMACS development team.
5 * Copyright (c) 2019,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
37 * \brief API for neighborhood searching for analysis.
39 * The main part of the API is the class gmx::AnalysisNeighborhood.
40 * See \ref page_analysisnbsearch for an overview.
42 * The classes within this file can be used independently of the other parts
43 * of the selection module.
45 * \author Teemu Murtola <teemu.murtola@gmail.com>
47 * \ingroup module_selection
49 #ifndef GMX_SELECTION_NBSEARCH_H
50 #define GMX_SELECTION_NBSEARCH_H
55 #include "gromacs/math/vec.h"
56 #include "gromacs/math/vectypes.h"
57 #include "gromacs/utility/arrayref.h"
58 #include "gromacs/utility/basedefinitions.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/real.h"
71 class AnalysisNeighborhoodSearchImpl;
72 class AnalysisNeighborhoodPairSearchImpl;
73 }; // namespace internal
75 class AnalysisNeighborhoodSearch;
76 class AnalysisNeighborhoodPairSearch;
79 * Input positions for neighborhood searching.
81 * This class supports uniformly specifying sets of positions for various
82 * methods in the analysis neighborhood searching classes
83 * (AnalysisNeighborhood and AnalysisNeighborhoodSearch).
85 * Note that copies are not made: only a reference to the positions passed to
86 * the constructors are kept. The caller is responsible to ensure that those
87 * positions remain in scope as long as the neighborhood search object requires
90 * Also note that in addition to constructors here, Selection and
91 * SelectionPosition provide conversions operators to this type. It is done
92 * this way to not introduce a cyclic dependency between the selection code and
93 * the neighborhood search code, which in turn allows splitting this search
94 * code into a separate lower-level module if desired at some point.
96 * Methods in this class do not throw.
99 * \ingroup module_selection
101 class AnalysisNeighborhoodPositions
105 * Initializes positions from a single position vector.
107 * For positions initialized this way, AnalysisNeighborhoodPair always
108 * returns zero in the corresponding index.
110 * This constructor is not explicit to allow directly passing an rvec
111 * to methods that accept positions.
113 AnalysisNeighborhoodPositions(const rvec& x) :
117 exclusionIds_(nullptr),
122 * Initializes positions from an array of position vectors.
124 AnalysisNeighborhoodPositions(const rvec x[], int count) :
128 exclusionIds_(nullptr),
133 * Initializes positions from a vector of position vectors.
135 AnalysisNeighborhoodPositions(const std::vector<RVec>& x) :
138 x_(as_rvec_array(x.data())),
139 exclusionIds_(nullptr),
145 * Sets indices to use for mapping exclusions to these positions.
147 * The exclusion IDs can always be set, but they are ignored unless
148 * actual exclusions have been set with
149 * AnalysisNeighborhood::setTopologyExclusions().
151 AnalysisNeighborhoodPositions& exclusionIds(ArrayRef<const int> ids)
153 GMX_ASSERT(ssize(ids) == count_, "Exclusion id array should match the number of positions");
154 exclusionIds_ = ids.data();
158 * Sets indices that select a subset of all positions from the array.
160 * If called, selected positions from the array of positions passed to
161 * the constructor is used instead of the whole array.
162 * All returned indices from AnalysisNeighborhoodPair objects are
163 * indices to the \p indices array passed here.
165 AnalysisNeighborhoodPositions& indexed(ArrayRef<const int> indices)
167 count_ = ssize(indices);
168 indices_ = indices.data();
173 * Selects a single position to use from an array.
175 * If called, a single position from the array of positions passed to
176 * the constructor is used instead of the whole array.
177 * In contrast to the AnalysisNeighborhoodPositions(const rvec &)
178 * constructor, AnalysisNeighborhoodPair objects return \p index
181 * If used together with indexed(), \p index references the index array
182 * passed to indexed() instead of the position array.
184 AnalysisNeighborhoodPositions& selectSingleFromArray(int index)
186 GMX_ASSERT(index >= 0 && index < count_, "Invalid position index");
195 const int* exclusionIds_;
198 //! To access the positions for initialization.
199 friend class internal::AnalysisNeighborhoodSearchImpl;
200 //! To access the positions for initialization.
201 friend class internal::AnalysisNeighborhoodPairSearchImpl;
205 * Neighborhood searching for analysis tools.
207 * See \ref page_analysisnbsearch for an overview.
209 * To use the search, create an object of this type, call setCutoff() to
210 * initialize it, and then repeatedly call initSearch() to start a search with
211 * different sets of reference positions. For each set of reference positions,
212 * use methods in the returned AnalysisNeighborhoodSearch to find the reference
213 * positions that are within the given cutoff from a provided position.
215 * initSearch() is thread-safe and can be called from multiple threads. Each
216 * call returns a different instance of the search object that can be used
217 * independently of the others. The returned AnalysisNeighborhoodSearch
218 * objects are also thread-safe, and can be used concurrently from multiple
219 * threads. It is also possible to create multiple concurrent searches within
223 * Generalize the exclusion machinery to make it easier to use for other cases
224 * than atom-atom exclusions from the topology.
227 * \ingroup module_selection
229 class AnalysisNeighborhood
232 //! Searching algorithm to use.
235 //! Select algorithm based on heuristic efficiency considerations.
236 eSearchMode_Automatic,
237 //! Use a simple loop over all pairs.
239 //! Use grid-based searching whenever possible.
243 //! Creates an uninitialized neighborhood search.
244 AnalysisNeighborhood();
245 ~AnalysisNeighborhood();
248 * Sets cutoff distance for the neighborhood searching.
250 * \param[in] cutoff Cutoff distance for the search
251 * (<=0 stands for no cutoff).
253 * Currently, can only be called before the first call to initSearch().
254 * If this method is not called, no cutoff is used in the searches.
258 void setCutoff(real cutoff);
260 * Sets the search to only happen in the XY plane.
262 * Z component of the coordinates is not used in the searching,
263 * and returned distances are computed in the XY plane.
264 * Only boxes with the third box vector parallel to the Z axis are
265 * currently implemented.
269 void setXYMode(bool bXY);
271 * Sets atom exclusions from a topology.
273 * The \p excls structure specifies the exclusions from test positions
274 * to reference positions, i.e., a block starting at `excls->index[i]`
275 * specifies the exclusions for test position `i`, and the indices in
276 * `excls->a` are indices of the reference positions. If `excls->nr`
277 * is smaller than a test position id, then such test positions do not
278 * have any exclusions.
279 * It is assumed that the indices within a block of indices in
280 * `excls->a` is ascending.
284 * \see AnalysisNeighborhoodPositions::exclusionIds()
286 void setTopologyExclusions(const ListOfLists<int>* excls);
288 * Sets the algorithm to use for searching.
290 * \param[in] mode Search mode to use.
292 * Note that if \p mode is \ref eSearchMode_Grid, it is still only a
293 * suggestion: grid-based searching may not be possible with the
294 * provided input, in which case a simple search is still used.
295 * This is mainly useful for testing purposes to force a mode.
299 void setMode(SearchMode mode);
300 //! Returns the currently active search mode.
301 SearchMode mode() const;
304 * Initializes neighborhood search for a set of positions.
306 * \param[in] pbc PBC information for the frame.
307 * \param[in] positions Set of reference positions to use.
308 * \returns Search object that can be used to find positions from
309 * \p x within the given cutoff.
310 * \throws std::bad_alloc if out of memory.
312 * Currently, the input positions cannot use
313 * AnalysisNeighborhoodPositions::selectSingleFromArray().
315 AnalysisNeighborhoodSearch initSearch(const t_pbc* pbc, const AnalysisNeighborhoodPositions& positions);
320 std::unique_ptr<Impl> impl_;
324 * Value type to represent a pair of positions found in neighborhood searching.
326 * Methods in this class do not throw.
329 * \ingroup module_selection
331 class AnalysisNeighborhoodPair
334 //! Initializes an invalid pair.
335 AnalysisNeighborhoodPair() : refIndex_(-1), testIndex_(0), distance2_(0.0), dx_() {}
336 //! Initializes a pair object with the given data.
337 AnalysisNeighborhoodPair(int refIndex, int testIndex, real distance2, const rvec dx) :
339 testIndex_(testIndex),
340 distance2_(distance2),
347 * Whether this pair is valid.
349 * If isValid() returns false, other methods should not be called.
351 bool isValid() const { return refIndex_ >= 0; }
354 * Returns the index of the reference position in the pair.
356 * This index is always the index into the position array provided to
357 * AnalysisNeighborhood::initSearch().
361 GMX_ASSERT(isValid(), "Accessing invalid object");
365 * Returns the index of the test position in the pair.
367 * The contents of this index depends on the context (method call) that
369 * If there was no array in the call, this index is zero.
371 int testIndex() const
373 GMX_ASSERT(isValid(), "Accessing invalid object");
377 * Returns the squared distance between the pair of positions.
379 real distance2() const
381 GMX_ASSERT(isValid(), "Accessing invalid object");
385 * Returns the shortest vector between the pair of positions.
387 * The vector is from the test position to the reference position.
389 const rvec& dx() const
391 GMX_ASSERT(isValid(), "Accessing invalid object");
403 * Initialized neighborhood search with a fixed set of reference positions.
405 * An instance of this class is obtained through
406 * AnalysisNeighborhood::initSearch(), and can be used to do multiple searches
407 * against the provided set of reference positions.
408 * It is possible to create concurrent pair searches (including from different
409 * threads), as well as call other methods in this class while a pair search is
412 * This class works like a pointer: copies of it point to the same search.
413 * In general, avoid creating copies, and only use the copy/assignment support
414 * for moving the variable around. With C++11, this class would best be
417 * Methods in this class do not throw unless otherwise indicated.
420 * Make it such that reset() is not necessary to call in code that repeatedly
421 * assigns the result of AnalysisNeighborhood::initSearch() to the same
422 * variable (see sm_distance.cpp).
425 * Consider removing minimumDistance(), as nearestPoint() already returns the
429 * \ingroup module_selection
431 class AnalysisNeighborhoodSearch
435 * Internal short-hand type for a pointer to the implementation class.
437 * shared_ptr is used here to automatically keep a reference count to
438 * track whether an implementation class is still used outside the
439 * AnalysisNeighborhood object. Ownership currently always stays with
440 * AnalysisNeighborhood; it always keeps one instance of the pointer.
442 typedef std::shared_ptr<internal::AnalysisNeighborhoodSearchImpl> ImplPointer;
445 * Initializes an invalid search.
447 * Such an object cannot be used for searching. It needs to be
448 * assigned a value from AnalysisNeighborhood::initSearch() before it
449 * can be used. Provided to allow declaring a variable to hold the
450 * search before calling AnalysisNeighborhood::initSearch().
452 AnalysisNeighborhoodSearch();
454 * Internally initialize the search.
456 * Used to implement AnalysisNeighborhood::initSearch().
457 * Cannot be called from user code.
459 explicit AnalysisNeighborhoodSearch(const ImplPointer& impl);
462 * Clears this search.
464 * Equivalent to \c "*this = AnalysisNeighborhoodSearch();".
465 * Currently, this is necessary to avoid unnecessary memory allocation
466 * if the previous search variable is still in scope when you want to
467 * call AnalysisNeighborhood::initSearch() again.
472 * Returns the searching algorithm that this search is using.
474 * The return value is never AnalysisNeighborhood::eSearchMode_Automatic.
476 AnalysisNeighborhood::SearchMode mode() const;
479 * Checks whether a point is within a neighborhood.
481 * \param[in] positions Set of test positions to use.
482 * \returns true if any of the test positions is within the cutoff of
483 * any reference position.
485 bool isWithin(const AnalysisNeighborhoodPositions& positions) const;
487 * Calculates the minimum distance from the reference points.
489 * \param[in] positions Set of test positions to use.
490 * \returns The distance to the nearest reference position, or the
491 * cutoff value if there are no reference positions within the
494 real minimumDistance(const AnalysisNeighborhoodPositions& positions) const;
496 * Finds the closest reference point.
498 * \param[in] positions Set of test positions to use.
499 * \returns The reference index identifies the reference position
500 * that is closest to the test positions.
501 * The test index identifies the test position that is closest to
502 * the provided test position. The returned pair is invalid if
503 * no reference position is within the cutoff.
505 AnalysisNeighborhoodPair nearestPoint(const AnalysisNeighborhoodPositions& positions) const;
508 * Starts a search to find all reference position pairs within a cutoff.
510 * \returns Initialized search object to loop through all reference
511 * position pairs within the configured cutoff.
512 * \throws std::bad_alloc if out of memory.
514 * This works as if the reference positions were passed to
515 * startPairSearch(), except that it only returns each pair once,
516 * instead of returning both i-j and j-i pairs, as startPairSearch()
517 * does. i-i pairs are not returned. Note that the order of ref/test
518 * indices in the returned pairs is not predictable. That is, one of
519 * i-j or j-i is always returned, but there is no control which one.
521 AnalysisNeighborhoodPairSearch startSelfPairSearch() const;
524 * Starts a search to find reference positions within a cutoff.
526 * \param[in] positions Set of test positions to use.
527 * \returns Initialized search object to loop through all reference
528 * positions within the configured cutoff.
529 * \throws std::bad_alloc if out of memory.
531 * If you want to pass the same positions here as you used for the
532 * reference positions, consider using startSelfPairSearch().
533 * It can be up to 50% faster.
535 AnalysisNeighborhoodPairSearch startPairSearch(const AnalysisNeighborhoodPositions& positions) const;
538 typedef internal::AnalysisNeighborhoodSearchImpl Impl;
544 * Initialized neighborhood pair search with a fixed set of positions.
546 * This class is used to loop through pairs of neighbors within the cutoff
547 * provided to AnalysisNeighborhood. The following code demonstrates its use:
549 gmx::AnalysisNeighborhood nb;
550 nb.setCutoff(cutoff);
551 gmx::AnalysisNeighborhoodPositions refPos(xref, nref);
552 gmx::AnalysisNeighborhoodSearch search = nb.initSearch(pbc, refPos);
553 gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(selection);
554 gmx::AnalysisNeighborhoodPair pair;
555 while (pairSearch.findNextPair(&pair))
557 // <do something for each found pair the information in pair>
561 * It is not possible to use a single search object from multiple threads
564 * This class works like a pointer: copies of it point to the same search.
565 * In general, avoid creating copies, and only use the copy/assignment support
566 * for moving the variable around. With C++11, this class would best be
569 * Methods in this class do not throw.
572 * \ingroup module_selection
574 class AnalysisNeighborhoodPairSearch
578 * Internal short-hand type for a pointer to the implementation class.
580 * See AnalysisNeighborhoodSearch::ImplPointer for rationale of using
581 * shared_ptr and ownership semantics.
583 typedef std::shared_ptr<internal::AnalysisNeighborhoodPairSearchImpl> ImplPointer;
586 * Internally initialize the search.
588 * Used to implement AnalysisNeighborhoodSearch::startPairSearch().
589 * Cannot be called from user code.
591 explicit AnalysisNeighborhoodPairSearch(const ImplPointer& impl);
594 * Finds the next pair within the cutoff.
596 * \param[out] pair Information about the found pair.
597 * \returns false if there were no more pairs.
599 * If the method returns false, \p pair will be invalid.
601 * \see AnalysisNeighborhoodPair
602 * \see AnalysisNeighborhoodSearch::startPairSearch()
604 bool findNextPair(AnalysisNeighborhoodPair* pair);
606 * Skip remaining pairs for a test position in the search.
608 * When called after findNextPair(), makes subsequent calls to
609 * findNextPair() skip any pairs that have the same test position as
610 * that previously returned.
611 * This is useful if the caller wants to search whether any reference
612 * position within the cutoff satisfies some condition. This method
613 * can be used to skip remaining pairs after the first such position
614 * has been found if the remaining pairs would not have an effect on
617 void skipRemainingPairsForTestPosition();