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