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