be25e7a3fdfd90f0f3e4e08df14fcc6d0eeb4387
[alexxy/gromacs.git] / src / gromacs / selection / nbsearch.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \file
36  * \brief API for neighborhood searching for analysis.
37  *
38  * The main part of the API is the class gmx::AnalysisNeighborhood.
39  * See the class documentation for usage.
40  *
41  * The classes within this file can be used independently of the other parts
42  * of the library.
43  * The library also uses the classes internally.
44  *
45  * \author Teemu Murtola <teemu.murtola@gmail.com>
46  * \inpublicapi
47  * \ingroup module_selection
48  */
49 #ifndef GMX_SELECTION_NBSEARCH_H
50 #define GMX_SELECTION_NBSEARCH_H
51
52 #include <boost/shared_ptr.hpp>
53
54 #include "../math/vectypes.h"
55 #include "../utility/arrayref.h"
56 #include "../utility/common.h"
57 #include "../utility/gmxassert.h"
58 #include "../utility/real.h"
59
60 struct t_blocka;
61 struct t_pbc;
62
63 namespace gmx
64 {
65
66 namespace internal
67 {
68 class AnalysisNeighborhoodSearchImpl;
69 class AnalysisNeighborhoodPairSearchImpl;
70 };
71
72 class AnalysisNeighborhoodSearch;
73 class AnalysisNeighborhoodPairSearch;
74
75 /*! \brief
76  * Input positions for neighborhood searching.
77  *
78  * This class supports uniformly specifying sets of positions for various
79  * methods in the analysis neighborhood searching classes
80  * (AnalysisNeighborhood and AnalysisNeighborhoodSearch).
81  *
82  * Note that copies are not made: only a reference to the positions passed to
83  * the constructors are kept.  The caller is responsible to ensure that those
84  * positions remain in scope as long as the neighborhood search object requires
85  * access to them.
86  *
87  * Also note that in addition to constructors here, Selection and
88  * SelectionPosition provide conversions operators to this type.  It is done
89  * this way to not introduce a cyclic dependency between the selection code and
90  * the neighborhood search code, which in turn allows splitting this search
91  * code into a separate lower-level module if desired at some point.
92  *
93  * Methods in this class do not throw.
94  *
95  * \inpublicapi
96  * \ingroup module_selection
97  */
98 class AnalysisNeighborhoodPositions
99 {
100     public:
101         /*! \brief
102          * Initializes positions from a single position vector.
103          *
104          * For positions initialized this way, AnalysisNeighborhoodPair always
105          * returns zero in the corresponding index.
106          *
107          * This constructor is not explicit to allow directly passing an rvec
108          * to methods that accept positions.
109          */
110         AnalysisNeighborhoodPositions(const rvec &x)
111             : count_(1), index_(-1), x_(&x), exclusionIds_(NULL)
112         {
113         }
114         /*! \brief
115          * Initializes positions from an array of position vectors.
116          */
117         AnalysisNeighborhoodPositions(const rvec x[], int count)
118             : count_(count), index_(-1), x_(x), exclusionIds_(NULL)
119         {
120         }
121
122         /*! \brief
123          * Sets indices to use for mapping exclusions to these positions.
124          *
125          * The exclusion IDs can always be set, but they are ignored unless
126          * actual exclusions have been set with
127          * AnalysisNeighborhood::setTopologyExclusions().
128          */
129         AnalysisNeighborhoodPositions &
130         exclusionIds(ConstArrayRef<int> ids)
131         {
132             GMX_ASSERT(static_cast<int>(ids.size()) == count_,
133                        "Exclusion id array should match the number of positions");
134             exclusionIds_ = ids.data();
135             return *this;
136         }
137
138         /*! \brief
139          * Selects a single position to use from an array.
140          *
141          * If called, a single position from the array of positions passed to
142          * the constructor is used instead of the whole array.
143          * In contrast to the AnalysisNeighborhoodPositions(const rvec &)
144          * constructor, AnalysisNeighborhoodPair objects return \p index
145          * instead of zero.
146          */
147         AnalysisNeighborhoodPositions &selectSingleFromArray(int index)
148         {
149             GMX_ASSERT(index >= 0 && index < count_, "Invalid position index");
150             index_ = index;
151             return *this;
152         }
153
154     private:
155         int                     count_;
156         int                     index_;
157         const rvec             *x_;
158         const int              *exclusionIds_;
159
160         //! To access the positions for initialization.
161         friend class internal::AnalysisNeighborhoodSearchImpl;
162         //! To access the positions for initialization.
163         friend class internal::AnalysisNeighborhoodPairSearchImpl;
164 };
165
166 /*! \brief
167  * Neighborhood searching for analysis tools.
168  *
169  * This class implements neighborhood searching routines for analysis tools.
170  * The emphasis is in flexibility and ease of use; one main driver is to have
171  * a common implementation of grid-based searching to avoid replicating this in
172  * multiple tools (and to make more tools take advantage of the significant
173  * performance improvement this allows).
174  *
175  * To use the search, create an object of this type, call setCutoff() to
176  * initialize it, and then repeatedly call initSearch() to start a search with
177  * different sets of reference positions.  For each set of reference positions,
178  * use methods in the returned AnalysisNeighborhoodSearch to find the reference
179  * positions that are within the given cutoff from a provided position.
180  *
181  * initSearch() is thread-safe and can be called from multiple threads.  Each
182  * call returns a different instance of the search object that can be used
183  * independently of the others.  The returned AnalysisNeighborhoodSearch
184  * objects are also thread-safe, and can be used concurrently from multiple
185  * threads.  It is also possible to create multiple concurrent searches within
186  * a single thread.
187  *
188  * \todo
189  * Generalize the exclusion machinery to make it easier to use for other cases
190  * than atom-atom exclusions from the topology.
191  *
192  * \inpublicapi
193  * \ingroup module_selection
194  */
195 class AnalysisNeighborhood
196 {
197     public:
198         //! Searching algorithm to use.
199         enum SearchMode
200         {
201             //! Select algorithm based on heuristic efficiency considerations.
202             eSearchMode_Automatic,
203             //! Use a simple loop over all pairs.
204             eSearchMode_Simple,
205             //! Use grid-based searching whenever possible.
206             eSearchMode_Grid
207         };
208
209         //! Creates an uninitialized neighborhood search.
210         AnalysisNeighborhood();
211         ~AnalysisNeighborhood();
212
213         /*! \brief
214          * Sets cutoff distance for the neighborhood searching.
215          *
216          * \param[in]  cutoff Cutoff distance for the search
217          *   (<=0 stands for no cutoff).
218          *
219          * Currently, can only be called before the first call to initSearch().
220          * If this method is not called, no cutoff is used in the searches.
221          *
222          * Does not throw.
223          */
224         void setCutoff(real cutoff);
225         /*! \brief
226          * Sets the search to only happen in the XY plane.
227          *
228          * Z component of the coordinates is not used in the searching,
229          * and returned distances are computed in the XY plane.
230          * Only boxes with the third box vector parallel to the Z axis are
231          * currently implemented.
232          *
233          * Does not throw.
234          */
235         void setXYMode(bool bXY);
236         /*! \brief
237          * Sets atom exclusions from a topology.
238          *
239          * The \p excls structure specifies the exclusions from test positions
240          * to reference positions, i.e., a block starting at `excls->index[i]`
241          * specifies the exclusions for test position `i`, and the indices in
242          * `excls->a` are indices of the reference positions.  If `excls->nr`
243          * is smaller than a test position id, then such test positions do not
244          * have any exclusions.
245          * It is assumed that the indices within a block of indices in
246          * `excls->a` is ascending.
247          *
248          * Does not throw.
249          *
250          * \see AnalysisNeighborhoodPositions::exclusionIds()
251          */
252         void setTopologyExclusions(const t_blocka *excls);
253         /*! \brief
254          * Sets the algorithm to use for searching.
255          *
256          * \param[in] mode  Search mode to use.
257          *
258          * Note that if \p mode is \ref eSearchMode_Grid, it is still only a
259          * suggestion: grid-based searching may not be possible with the
260          * provided input, in which case a simple search is still used.
261          * This is mainly useful for testing purposes to force a mode.
262          *
263          * Does not throw.
264          */
265         void setMode(SearchMode mode);
266         //! Returns the currently active search mode.
267         SearchMode mode() const;
268
269         /*! \brief
270          * Initializes neighborhood search for a set of positions.
271          *
272          * \param[in] pbc        PBC information for the frame.
273          * \param[in] positions  Set of reference positions to use.
274          * \returns   Search object that can be used to find positions from
275          *      \p x within the given cutoff.
276          * \throws    std::bad_alloc if out of memory.
277          *
278          * Currently, the input positions cannot use
279          * AnalysisNeighborhoodPositions::selectSingleFromArray().
280          */
281         AnalysisNeighborhoodSearch
282         initSearch(const t_pbc                         *pbc,
283                    const AnalysisNeighborhoodPositions &positions);
284
285     private:
286         class Impl;
287
288         PrivateImplPointer<Impl> impl_;
289 };
290
291 /*! \brief
292  * Value type to represent a pair of positions found in neighborhood searching.
293  *
294  * Methods in this class do not throw.
295  *
296  * \inpublicapi
297  * \ingroup module_selection
298  */
299 class AnalysisNeighborhoodPair
300 {
301     public:
302         //! Initializes an invalid pair.
303         AnalysisNeighborhoodPair() : refIndex_(-1), testIndex_(0), distance2_(0.0) {}
304         //! Initializes a pair object with the given data.
305         AnalysisNeighborhoodPair(int refIndex, int testIndex, real distance2)
306             : refIndex_(refIndex), testIndex_(testIndex), distance2_(distance2)
307         {
308         }
309
310         /*! \brief
311          * Whether this pair is valid.
312          *
313          * If isValid() returns false, other methods should not be called.
314          */
315         bool isValid() const { return refIndex_ >= 0; }
316
317         /*! \brief
318          * Returns the index of the reference position in the pair.
319          *
320          * This index is always the index into the position array provided to
321          * AnalysisNeighborhood::initSearch().
322          */
323         int refIndex() const
324         {
325             GMX_ASSERT(isValid(), "Accessing invalid object");
326             return refIndex_;
327         }
328         /*! \brief
329          * Returns the index of the test position in the pair.
330          *
331          * The contents of this index depends on the context (method call) that
332          * produces the pair.
333          * If there was no array in the call, this index is zero.
334          */
335         int testIndex() const
336         {
337             GMX_ASSERT(isValid(), "Accessing invalid object");
338             return testIndex_;
339         }
340         /*! \brief
341          * Returns the squared distance between the pair of positions.
342          */
343         real distance2() const
344         {
345             GMX_ASSERT(isValid(), "Accessing invalid object");
346             return distance2_;
347         }
348
349     private:
350         int                     refIndex_;
351         int                     testIndex_;
352         real                    distance2_;
353 };
354
355 /*! \brief
356  * Initialized neighborhood search with a fixed set of reference positions.
357  *
358  * An instance of this class is obtained through
359  * AnalysisNeighborhood::initSearch(), and can be used to do multiple searches
360  * against the provided set of reference positions.
361  * It is possible to create concurrent pair searches (including from different
362  * threads), as well as call other methods in this class while a pair search is
363  * in progress.
364  *
365  * This class works like a pointer: copies of it point to the same search.
366  * In general, avoid creating copies, and only use the copy/assignment support
367  * for moving the variable around.  With C++11, this class would best be
368  * movable.
369  *
370  * Methods in this class do not throw unless otherwise indicated.
371  *
372  * \todo
373  * Make it such that reset() is not necessary to call in code that repeatedly
374  * assigns the result of AnalysisNeighborhood::initSearch() to the same
375  * variable (see sm_distance.cpp).
376  *
377  * \todo
378  * Consider merging nearestPoint() and minimumDistance() by adding the distance
379  * to AnalysisNeighborhoodPair.
380  *
381  * \inpublicapi
382  * \ingroup module_selection
383  */
384 class AnalysisNeighborhoodSearch
385 {
386     public:
387         /*! \brief
388          * Internal short-hand type for a pointer to the implementation class.
389          *
390          * shared_ptr is used here to automatically keep a reference count to
391          * track whether an implementation class is still used outside the
392          * AnalysisNeighborhood object.  Ownership currently always stays with
393          * AnalysisNeighborhood; it always keeps one instance of the pointer.
394          */
395         typedef boost::shared_ptr<internal::AnalysisNeighborhoodSearchImpl>
396             ImplPointer;
397
398         /*! \brief
399          * Initializes an invalid search.
400          *
401          * Such an object cannot be used for searching.  It needs to be
402          * assigned a value from AnalysisNeighborhood::initSearch() before it
403          * can be used.  Provided to allow declaring a variable to hold the
404          * search before calling AnalysisNeighborhood::initSearch().
405          */
406         AnalysisNeighborhoodSearch();
407         /*! \brief
408          * Internally initialize the search.
409          *
410          * Used to implement AnalysisNeighborhood::initSearch().
411          * Cannot be called from user code.
412          */
413         explicit AnalysisNeighborhoodSearch(const ImplPointer &impl);
414
415         /*! \brief
416          * Clears this search.
417          *
418          * Equivalent to \c "*this = AnalysisNeighborhoodSearch();".
419          * Currently, this is necessary to avoid unnecessary memory allocation
420          * if the previous search variable is still in scope when you want to
421          * call AnalysisNeighborhood::initSearch() again.
422          */
423         void reset();
424
425         /*! \brief
426          * Returns the searching algorithm that this search is using.
427          *
428          * The return value is never AnalysisNeighborhood::eSearchMode_Automatic.
429          */
430         AnalysisNeighborhood::SearchMode mode() const;
431
432         /*! \brief
433          * Check whether a point is within a neighborhood.
434          *
435          * \param[in] positions  Set of test positions to use.
436          * \returns   true if any of the test positions is within the cutoff of
437          *     any reference position.
438          */
439         bool isWithin(const AnalysisNeighborhoodPositions &positions) const;
440         /*! \brief
441          * Calculates the minimum distance from the reference points.
442          *
443          * \param[in] positions  Set of test positions to use.
444          * \returns   The distance to the nearest reference position, or the
445          *     cutoff value if there are no reference positions within the
446          *     cutoff.
447          */
448         real minimumDistance(const AnalysisNeighborhoodPositions &positions) const;
449         /*! \brief
450          * Finds the closest reference point.
451          *
452          * \param[in] positions  Set of test positions to use.
453          * \returns   The reference index identifies the reference position
454          *     that is closest to the test positions.
455          *     The test index identifies the test position that is closest to
456          *     the provided test position.  The returned pair is invalid if
457          *     no reference position is within the cutoff.
458          */
459         AnalysisNeighborhoodPair
460         nearestPoint(const AnalysisNeighborhoodPositions &positions) const;
461
462         /*! \brief
463          * Start a search to find reference positions within a cutoff.
464          *
465          * \param[in] positions  Set of test positions to use.
466          * \returns   Initialized search object to loop through all reference
467          *     positions within the configured cutoff.
468          * \throws    std::bad_alloc if out of memory.
469          */
470         AnalysisNeighborhoodPairSearch
471         startPairSearch(const AnalysisNeighborhoodPositions &positions) const;
472
473     private:
474         typedef internal::AnalysisNeighborhoodSearchImpl Impl;
475
476         ImplPointer             impl_;
477 };
478
479 /*! \brief
480  * Initialized neighborhood pair search with a fixed set of positions.
481  *
482  * This class is used to loop through pairs of neighbors within the cutoff
483  * provided to AnalysisNeighborhood.  The following code demonstrates its use:
484  * \code
485    gmx::AnalysisNeighborhood       nb;
486    nb.setCutoff(cutoff);
487    gmx::AnalysisNeighborhoodPositions refPos(xref, nref);
488    gmx::AnalysisNeighborhoodSearch search = nb.initSearch(pbc, refPos);
489    gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(selection);
490    gmx::AnalysisNeighborhoodPair pair;
491    while (pairSearch.findNextPair(&pair))
492    {
493        // <do something for each found pair the information in pair>
494    }
495  * \endcode
496  *
497  * It is not possible to use a single search object from multiple threads
498  * concurrently.
499  *
500  * This class works like a pointer: copies of it point to the same search.
501  * In general, avoid creating copies, and only use the copy/assignment support
502  * for moving the variable around.  With C++11, this class would best be
503  * movable.
504  *
505  * Methods in this class do not throw.
506  *
507  * \inpublicapi
508  * \ingroup module_selection
509  */
510 class AnalysisNeighborhoodPairSearch
511 {
512     public:
513         /*! \brief
514          * Internal short-hand type for a pointer to the implementation class.
515          *
516          * See AnalysisNeighborhoodSearch::ImplPointer for rationale of using
517          * shared_ptr and ownership semantics.
518          */
519         typedef boost::shared_ptr<internal::AnalysisNeighborhoodPairSearchImpl>
520             ImplPointer;
521
522         /*! \brief
523          * Internally initialize the search.
524          *
525          * Used to implement AnalysisNeighborhoodSearch::startPairSearch().
526          * Cannot be called from user code.
527          */
528         explicit AnalysisNeighborhoodPairSearch(const ImplPointer &impl);
529
530         /*! \brief
531          * Finds the next pair within the cutoff.
532          *
533          * \param[out] pair  Information about the found pair.
534          * \returns    false if there were no more pairs.
535          *
536          * If the method returns false, \p pair will be invalid.
537          *
538          * \see AnalysisNeighborhoodPair
539          * \see AnalysisNeighborhoodSearch::startPairSearch()
540          */
541         bool findNextPair(AnalysisNeighborhoodPair *pair);
542         /*! \brief
543          * Skip remaining pairs for a test position in the search.
544          *
545          * When called after findNextPair(), makes subsequent calls to
546          * findNextPair() skip any pairs that have the same test position as
547          * that previously returned.
548          * This is useful if the caller wants to search whether any reference
549          * position within the cutoff satisfies some condition.  This method
550          * can be used to skip remaining pairs after the first such position
551          * has been found if the remaining pairs would not have an effect on
552          * the outcome.
553          */
554         void skipRemainingPairsForTestPosition();
555
556     private:
557         ImplPointer             impl_;
558 };
559
560 } // namespace gmx
561
562 #endif