2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Tests selection neighborhood searching.
40 * Increase coverage of these tests for different corner cases: other PBC cases
41 * than full 3D, large cutoffs (larger than half the box size), etc.
42 * At least some of these probably don't work correctly.
44 * \author Teemu Murtola <teemu.murtola@gmail.com>
45 * \ingroup module_selection
49 #include "gromacs/selection/nbsearch.h"
51 #include <gtest/gtest.h>
60 #include "gromacs/math/vec.h"
61 #include "gromacs/pbcutil/pbc.h"
62 #include "gromacs/random/random.h"
63 #include "gromacs/topology/block.h"
64 #include "gromacs/utility/smalloc.h"
65 #include "gromacs/utility/stringutil.h"
67 #include "testutils/testasserts.h"
72 /********************************************************************
73 * NeighborhoodSearchTestData
76 class NeighborhoodSearchTestData
81 RefPair(int refIndex, real distance)
82 : refIndex(refIndex), distance(distance), bFound(false),
87 bool operator<(const RefPair &other) const
89 return refIndex < other.refIndex;
94 // The variables below are state variables that are only used
95 // during the actual testing after creating a copy of the reference
96 // pair list, not as part of the reference data.
97 // Simpler to have just a single structure for both purposes.
104 explicit TestPosition(const rvec x)
105 : refMinDist(0.0), refNearestPoint(-1)
107 copy_rvec(x, this->x);
113 std::vector<RefPair> refPairs;
116 typedef std::vector<TestPosition> TestPositionList;
118 NeighborhoodSearchTestData(int seed, real cutoff);
119 ~NeighborhoodSearchTestData();
121 gmx::AnalysisNeighborhoodPositions refPositions() const
123 return gmx::AnalysisNeighborhoodPositions(refPos_, refPosCount_);
125 gmx::AnalysisNeighborhoodPositions testPositions() const
127 if (testPos_ == NULL)
129 snew(testPos_, testPositions_.size());
130 for (size_t i = 0; i < testPositions_.size(); ++i)
132 copy_rvec(testPositions_[i].x, testPos_[i]);
135 return gmx::AnalysisNeighborhoodPositions(testPos_,
136 testPositions_.size());
138 gmx::AnalysisNeighborhoodPositions testPosition(int index) const
140 return testPositions().selectSingleFromArray(index);
143 void addTestPosition(const rvec x)
145 GMX_RELEASE_ASSERT(testPos_ == NULL,
146 "Cannot add positions after testPositions() call");
147 testPositions_.push_back(TestPosition(x));
149 void generateRandomPosition(rvec x);
150 void generateRandomRefPositions(int count);
151 void generateRandomTestPositions(int count);
152 void computeReferences(t_pbc *pbc)
154 computeReferencesInternal(pbc, false);
156 void computeReferencesXY(t_pbc *pbc)
158 computeReferencesInternal(pbc, true);
161 bool containsPair(int testIndex, const RefPair &pair) const
163 const std::vector<RefPair> &refPairs = testPositions_[testIndex].refPairs;
164 std::vector<RefPair>::const_iterator foundRefPair
165 = std::lower_bound(refPairs.begin(), refPairs.end(), pair);
166 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex)
179 TestPositionList testPositions_;
182 void computeReferencesInternal(t_pbc *pbc, bool bXY);
184 mutable rvec *testPos_;
187 //! Shorthand for a collection of reference pairs.
188 typedef std::vector<NeighborhoodSearchTestData::RefPair> RefPairList;
190 NeighborhoodSearchTestData::NeighborhoodSearchTestData(int seed, real cutoff)
191 : rng_(NULL), cutoff_(cutoff), refPosCount_(0), refPos_(NULL), testPos_(NULL)
193 // TODO: Handle errors.
194 rng_ = gmx_rng_init(seed);
196 set_pbc(&pbc_, epbcNONE, box_);
199 NeighborhoodSearchTestData::~NeighborhoodSearchTestData()
203 gmx_rng_destroy(rng_);
209 void NeighborhoodSearchTestData::generateRandomPosition(rvec x)
212 fx[XX] = gmx_rng_uniform_real(rng_);
213 fx[YY] = gmx_rng_uniform_real(rng_);
214 fx[ZZ] = gmx_rng_uniform_real(rng_);
216 // Add a small displacement to allow positions outside the box
217 x[XX] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
218 x[YY] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
219 x[ZZ] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
222 void NeighborhoodSearchTestData::generateRandomRefPositions(int count)
224 refPosCount_ = count;
225 snew(refPos_, refPosCount_);
226 for (int i = 0; i < refPosCount_; ++i)
228 generateRandomPosition(refPos_[i]);
232 void NeighborhoodSearchTestData::generateRandomTestPositions(int count)
234 testPositions_.reserve(count);
235 for (int i = 0; i < count; ++i)
238 generateRandomPosition(x);
243 void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc *pbc, bool bXY)
245 real cutoff = cutoff_;
248 cutoff = std::numeric_limits<real>::max();
250 TestPositionList::iterator i;
251 for (i = testPositions_.begin(); i != testPositions_.end(); ++i)
253 i->refMinDist = cutoff;
254 i->refNearestPoint = -1;
256 for (int j = 0; j < refPosCount_; ++j)
261 pbc_dx(pbc, i->x, refPos_[j], dx);
265 rvec_sub(i->x, refPos_[j], dx);
267 // TODO: This may not work intuitively for 2D with the third box
268 // vector not parallel to the Z axis, but neither does the actual
269 // neighborhood search.
271 !bXY ? norm(dx) : sqrt(sqr(dx[XX]) + sqr(dx[YY]));
272 if (dist < i->refMinDist)
274 i->refMinDist = dist;
275 i->refNearestPoint = j;
279 RefPair pair(j, dist);
280 GMX_RELEASE_ASSERT(i->refPairs.empty() || i->refPairs.back() < pair,
281 "Reference pairs should be generated in sorted order");
282 i->refPairs.push_back(pair);
288 /********************************************************************
292 class ExclusionsHelper
295 static void markExcludedPairs(RefPairList *refPairs, int testIndex,
296 const t_blocka *excls);
298 ExclusionsHelper(int refPosCount, int testPosCount);
300 void generateExclusions();
302 const t_blocka *exclusions() const { return &excls_; }
304 gmx::ConstArrayRef<int> refPosIds() const
306 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
307 exclusionIds_.begin() + refPosCount_);
309 gmx::ConstArrayRef<int> testPosIds() const
311 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
312 exclusionIds_.begin() + testPosCount_);
318 std::vector<int> exclusionIds_;
319 std::vector<int> exclsIndex_;
320 std::vector<int> exclsAtoms_;
325 void ExclusionsHelper::markExcludedPairs(RefPairList *refPairs, int testIndex,
326 const t_blocka *excls)
329 for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
331 const int excludedIndex = excls->a[i];
332 NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
333 RefPairList::iterator excludedRefPair
334 = std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
335 if (excludedRefPair != refPairs->end()
336 && excludedRefPair->refIndex == excludedIndex)
338 excludedRefPair->bFound = true;
339 excludedRefPair->bExcluded = true;
345 ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount)
346 : refPosCount_(refPosCount), testPosCount_(testPosCount)
348 // Generate an array of 0, 1, 2, ...
349 // TODO: Make the tests work also with non-trivial exclusion IDs,
351 exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
352 exclusionIds_[0] = 0;
353 std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(),
354 exclusionIds_.begin());
360 excls_.nalloc_index = 0;
364 void ExclusionsHelper::generateExclusions()
366 // TODO: Consider a better set of test data, where the density of the
367 // particles would be higher, or where the exclusions would not be random,
368 // to make a higher percentage of the exclusions to actually be within the
370 exclsIndex_.reserve(testPosCount_ + 1);
371 exclsAtoms_.reserve(testPosCount_ * 20);
372 exclsIndex_.push_back(0);
373 for (int i = 0; i < testPosCount_; ++i)
375 for (int j = 0; j < 20; ++j)
377 exclsAtoms_.push_back(i + j*3);
379 exclsIndex_.push_back(exclsAtoms_.size());
381 excls_.nr = exclsIndex_.size();
382 excls_.index = &exclsIndex_[0];
383 excls_.nra = exclsAtoms_.size();
384 excls_.a = &exclsAtoms_[0];
387 /********************************************************************
388 * NeighborhoodSearchTest
391 class NeighborhoodSearchTest : public ::testing::Test
394 void testIsWithin(gmx::AnalysisNeighborhoodSearch *search,
395 const NeighborhoodSearchTestData &data);
396 void testMinimumDistance(gmx::AnalysisNeighborhoodSearch *search,
397 const NeighborhoodSearchTestData &data);
398 void testNearestPoint(gmx::AnalysisNeighborhoodSearch *search,
399 const NeighborhoodSearchTestData &data);
400 void testPairSearch(gmx::AnalysisNeighborhoodSearch *search,
401 const NeighborhoodSearchTestData &data);
402 void testPairSearchFull(gmx::AnalysisNeighborhoodSearch *search,
403 const NeighborhoodSearchTestData &data,
404 const gmx::AnalysisNeighborhoodPositions &pos,
405 const t_blocka *excls);
407 gmx::AnalysisNeighborhood nb_;
410 void NeighborhoodSearchTest::testIsWithin(
411 gmx::AnalysisNeighborhoodSearch *search,
412 const NeighborhoodSearchTestData &data)
414 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
415 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
417 const bool bWithin = (i->refMinDist <= data.cutoff_);
418 EXPECT_EQ(bWithin, search->isWithin(i->x))
419 << "Distance is " << i->refMinDist;
423 void NeighborhoodSearchTest::testMinimumDistance(
424 gmx::AnalysisNeighborhoodSearch *search,
425 const NeighborhoodSearchTestData &data)
427 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
428 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
430 const real refDist = i->refMinDist;
431 EXPECT_REAL_EQ_TOL(refDist, search->minimumDistance(i->x),
432 gmx::test::ulpTolerance(20));
436 void NeighborhoodSearchTest::testNearestPoint(
437 gmx::AnalysisNeighborhoodSearch *search,
438 const NeighborhoodSearchTestData &data)
440 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
441 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
443 const gmx::AnalysisNeighborhoodPair pair = search->nearestPoint(i->x);
446 EXPECT_EQ(i->refNearestPoint, pair.refIndex());
447 EXPECT_EQ(0, pair.testIndex());
448 EXPECT_REAL_EQ_TOL(i->refMinDist, sqrt(pair.distance2()),
449 gmx::test::ulpTolerance(64));
453 EXPECT_EQ(i->refNearestPoint, -1);
458 //! Helper function for formatting test failure messages.
459 std::string formatVector(const rvec x)
461 return gmx::formatString("[%.3f, %.3f, %.3f]", x[XX], x[YY], x[ZZ]);
465 * Helper function to check that all expected pairs were found.
467 void checkAllPairsFound(const RefPairList &refPairs, const rvec refPos[],
468 int testPosIndex, const rvec testPos)
470 // This could be elegantly expressed with Google Mock matchers, but that
471 // has a significant effect on the runtime of the tests...
473 RefPairList::const_iterator first;
474 for (RefPairList::const_iterator i = refPairs.begin(); i != refPairs.end(); ++i)
485 << "Some pairs (" << count << "/" << refPairs.size() << ") "
486 << "within the cutoff were not found. First pair:\n"
487 << " Ref: " << first->refIndex << " at "
488 << formatVector(refPos[first->refIndex]) << "\n"
489 << "Test: " << testPosIndex << " at " << formatVector(testPos) << "\n"
490 << "Dist: " << first->distance;
494 void NeighborhoodSearchTest::testPairSearch(
495 gmx::AnalysisNeighborhoodSearch *search,
496 const NeighborhoodSearchTestData &data)
498 testPairSearchFull(search, data, data.testPositions(), NULL);
501 void NeighborhoodSearchTest::testPairSearchFull(
502 gmx::AnalysisNeighborhoodSearch *search,
503 const NeighborhoodSearchTestData &data,
504 const gmx::AnalysisNeighborhoodPositions &pos,
505 const t_blocka *excls)
507 // TODO: Some parts of this code do not work properly if pos does not
508 // contain all the test positions.
509 std::set<int> remainingTestPositions;
510 for (size_t i = 0; i < data.testPositions_.size(); ++i)
512 remainingTestPositions.insert(i);
514 gmx::AnalysisNeighborhoodPairSearch pairSearch
515 = search->startPairSearch(pos);
516 gmx::AnalysisNeighborhoodPair pair;
517 // TODO: There is an ordering assumption here that may break in the future:
518 // all pairs for a test position are assumed to be returned consencutively.
519 RefPairList refPairs;
520 int prevTestPos = -1;
521 while (pairSearch.findNextPair(&pair))
523 if (pair.testIndex() != prevTestPos)
525 if (prevTestPos != -1)
527 checkAllPairsFound(refPairs, data.refPos_, prevTestPos,
528 data.testPositions_[prevTestPos].x);
530 const int testIndex = pair.testIndex();
531 if (remainingTestPositions.count(testIndex) == 0)
534 << "Pairs for test position " << testIndex
535 << " are returned more than once.";
537 remainingTestPositions.erase(testIndex);
538 refPairs = data.testPositions_[testIndex].refPairs;
541 ExclusionsHelper::markExcludedPairs(&refPairs, testIndex, excls);
543 prevTestPos = testIndex;
546 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(),
547 sqrt(pair.distance2()));
548 RefPairList::iterator foundRefPair
549 = std::lower_bound(refPairs.begin(), refPairs.end(), searchPair);
550 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex())
553 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
554 << pair.testIndex() << ") is not within the cutoff.\n"
555 << " Actual: It is returned.";
557 else if (foundRefPair->bExcluded)
560 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
561 << pair.testIndex() << ") is excluded from the search.\n"
562 << " Actual: It is returned.";
564 else if (foundRefPair->bFound)
567 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
568 << pair.testIndex() << ") is returned only once.\n"
569 << " Actual: It is returned multiple times.";
573 foundRefPair->bFound = true;
574 EXPECT_REAL_EQ_TOL(foundRefPair->distance, searchPair.distance,
575 gmx::test::ulpTolerance(64))
576 << "Distance computed by the neighborhood search does not match.";
579 checkAllPairsFound(refPairs, data.refPos_, prevTestPos,
580 data.testPositions_[prevTestPos].x);
581 for (std::set<int>::const_iterator i = remainingTestPositions.begin();
582 i != remainingTestPositions.end(); ++i)
584 if (!data.testPositions_[*i].refPairs.empty())
587 << "Expected: Pairs would be returned for test position " << *i << ".\n"
588 << " Actual: None were returned.";
594 /********************************************************************
595 * Test data generation
598 class TrivialTestData
601 static const NeighborhoodSearchTestData &get()
603 static TrivialTestData singleton;
604 return singleton.data_;
607 TrivialTestData() : data_(12345, 1.0)
609 data_.box_[XX][XX] = 5.0;
610 data_.box_[YY][YY] = 5.0;
611 data_.box_[ZZ][ZZ] = 5.0;
612 data_.generateRandomRefPositions(10);
613 data_.generateRandomTestPositions(5);
614 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
615 data_.computeReferences(&data_.pbc_);
619 NeighborhoodSearchTestData data_;
622 class RandomBoxFullPBCData
625 static const NeighborhoodSearchTestData &get()
627 static RandomBoxFullPBCData singleton;
628 return singleton.data_;
631 RandomBoxFullPBCData() : data_(12345, 1.0)
633 data_.box_[XX][XX] = 10.0;
634 data_.box_[YY][YY] = 5.0;
635 data_.box_[ZZ][ZZ] = 7.0;
636 // TODO: Consider whether manually picking some positions would give better
638 data_.generateRandomRefPositions(1000);
639 data_.generateRandomTestPositions(100);
640 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
641 data_.computeReferences(&data_.pbc_);
645 NeighborhoodSearchTestData data_;
648 class RandomBoxXYFullPBCData
651 static const NeighborhoodSearchTestData &get()
653 static RandomBoxXYFullPBCData singleton;
654 return singleton.data_;
657 RandomBoxXYFullPBCData() : data_(54321, 1.0)
659 data_.box_[XX][XX] = 10.0;
660 data_.box_[YY][YY] = 5.0;
661 data_.box_[ZZ][ZZ] = 7.0;
662 // TODO: Consider whether manually picking some positions would give better
664 data_.generateRandomRefPositions(1000);
665 data_.generateRandomTestPositions(100);
666 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
667 data_.computeReferencesXY(&data_.pbc_);
671 NeighborhoodSearchTestData data_;
674 class RandomTriclinicFullPBCData
677 static const NeighborhoodSearchTestData &get()
679 static RandomTriclinicFullPBCData singleton;
680 return singleton.data_;
683 RandomTriclinicFullPBCData() : data_(12345, 1.0)
685 data_.box_[XX][XX] = 5.0;
686 data_.box_[YY][XX] = 2.5;
687 data_.box_[YY][YY] = 2.5*sqrt(3.0);
688 data_.box_[ZZ][XX] = 2.5;
689 data_.box_[ZZ][YY] = 2.5*sqrt(1.0/3.0);
690 data_.box_[ZZ][ZZ] = 5.0*sqrt(2.0/3.0);
691 // TODO: Consider whether manually picking some positions would give better
693 data_.generateRandomRefPositions(1000);
694 data_.generateRandomTestPositions(100);
695 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
696 data_.computeReferences(&data_.pbc_);
700 NeighborhoodSearchTestData data_;
703 class RandomBox2DPBCData
706 static const NeighborhoodSearchTestData &get()
708 static RandomBox2DPBCData singleton;
709 return singleton.data_;
712 RandomBox2DPBCData() : data_(12345, 1.0)
714 data_.box_[XX][XX] = 10.0;
715 data_.box_[YY][YY] = 7.0;
716 data_.box_[ZZ][ZZ] = 5.0;
717 // TODO: Consider whether manually picking some positions would give better
719 data_.generateRandomRefPositions(1000);
720 data_.generateRandomTestPositions(100);
721 set_pbc(&data_.pbc_, epbcXY, data_.box_);
722 data_.computeReferences(&data_.pbc_);
726 NeighborhoodSearchTestData data_;
729 /********************************************************************
733 TEST_F(NeighborhoodSearchTest, SimpleSearch)
735 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
737 nb_.setCutoff(data.cutoff_);
738 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
739 gmx::AnalysisNeighborhoodSearch search =
740 nb_.initSearch(&data.pbc_, data.refPositions());
741 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
743 testIsWithin(&search, data);
744 testMinimumDistance(&search, data);
745 testNearestPoint(&search, data);
746 testPairSearch(&search, data);
749 TEST_F(NeighborhoodSearchTest, GridSearchBox)
751 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
753 nb_.setCutoff(data.cutoff_);
754 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
755 gmx::AnalysisNeighborhoodSearch search =
756 nb_.initSearch(&data.pbc_, data.refPositions());
757 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
759 testIsWithin(&search, data);
760 testMinimumDistance(&search, data);
761 testNearestPoint(&search, data);
762 testPairSearch(&search, data);
765 TEST_F(NeighborhoodSearchTest, GridSearchTriclinic)
767 const NeighborhoodSearchTestData &data = RandomTriclinicFullPBCData::get();
769 nb_.setCutoff(data.cutoff_);
770 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
771 gmx::AnalysisNeighborhoodSearch search =
772 nb_.initSearch(&data.pbc_, data.refPositions());
773 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
775 testPairSearch(&search, data);
778 TEST_F(NeighborhoodSearchTest, GridSearch2DPBC)
780 const NeighborhoodSearchTestData &data = RandomBox2DPBCData::get();
782 nb_.setCutoff(data.cutoff_);
783 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
784 gmx::AnalysisNeighborhoodSearch search =
785 nb_.initSearch(&data.pbc_, data.refPositions());
786 // Currently, grid searching not supported with 2D PBC.
787 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
789 testIsWithin(&search, data);
790 testMinimumDistance(&search, data);
791 testNearestPoint(&search, data);
792 testPairSearch(&search, data);
795 TEST_F(NeighborhoodSearchTest, GridSearchXYBox)
797 const NeighborhoodSearchTestData &data = RandomBoxXYFullPBCData::get();
799 nb_.setCutoff(data.cutoff_);
800 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
802 gmx::AnalysisNeighborhoodSearch search =
803 nb_.initSearch(&data.pbc_, data.refPositions());
804 // Currently, grid searching not supported with XY.
805 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
807 testIsWithin(&search, data);
808 testMinimumDistance(&search, data);
809 testNearestPoint(&search, data);
810 testPairSearch(&search, data);
813 TEST_F(NeighborhoodSearchTest, HandlesConcurrentSearches)
815 const NeighborhoodSearchTestData &data = TrivialTestData::get();
817 nb_.setCutoff(data.cutoff_);
818 gmx::AnalysisNeighborhoodSearch search1 =
819 nb_.initSearch(&data.pbc_, data.refPositions());
820 gmx::AnalysisNeighborhoodSearch search2 =
821 nb_.initSearch(&data.pbc_, data.refPositions());
823 gmx::AnalysisNeighborhoodPairSearch pairSearch1 =
824 search1.startPairSearch(data.testPosition(0));
825 gmx::AnalysisNeighborhoodPairSearch pairSearch2 =
826 search1.startPairSearch(data.testPosition(1));
828 testPairSearch(&search2, data);
830 gmx::AnalysisNeighborhoodPair pair;
831 ASSERT_TRUE(pairSearch1.findNextPair(&pair))
832 << "Test data did not contain any pairs for position 0 (problem in the test).";
833 EXPECT_EQ(0, pair.testIndex());
835 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
836 EXPECT_TRUE(data.containsPair(0, searchPair));
839 ASSERT_TRUE(pairSearch2.findNextPair(&pair))
840 << "Test data did not contain any pairs for position 1 (problem in the test).";
841 EXPECT_EQ(1, pair.testIndex());
843 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
844 EXPECT_TRUE(data.containsPair(1, searchPair));
848 TEST_F(NeighborhoodSearchTest, HandlesSkippingPairs)
850 const NeighborhoodSearchTestData &data = TrivialTestData::get();
852 nb_.setCutoff(data.cutoff_);
853 gmx::AnalysisNeighborhoodSearch search =
854 nb_.initSearch(&data.pbc_, data.refPositions());
855 gmx::AnalysisNeighborhoodPairSearch pairSearch =
856 search.startPairSearch(data.testPositions());
857 gmx::AnalysisNeighborhoodPair pair;
858 // TODO: This test needs to be adjusted if the grid search gets optimized
859 // to loop over the test positions in cell order (first, the ordering
860 // assumption here breaks, and second, it then needs to be tested
861 // separately for simple and grid searches).
862 int currentIndex = 0;
863 while (pairSearch.findNextPair(&pair))
865 while (currentIndex < pair.testIndex())
869 EXPECT_EQ(currentIndex, pair.testIndex());
870 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
871 EXPECT_TRUE(data.containsPair(currentIndex, searchPair));
872 pairSearch.skipRemainingPairsForTestPosition();
877 TEST_F(NeighborhoodSearchTest, SimpleSearchExclusions)
879 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
881 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
882 helper.generateExclusions();
884 nb_.setCutoff(data.cutoff_);
885 nb_.setTopologyExclusions(helper.exclusions());
886 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
887 gmx::AnalysisNeighborhoodSearch search =
888 nb_.initSearch(&data.pbc_,
889 data.refPositions().exclusionIds(helper.refPosIds()));
890 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
892 testPairSearchFull(&search, data,
893 data.testPositions().exclusionIds(helper.testPosIds()),
894 helper.exclusions());
897 TEST_F(NeighborhoodSearchTest, GridSearchExclusions)
899 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
901 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
902 helper.generateExclusions();
904 nb_.setCutoff(data.cutoff_);
905 nb_.setTopologyExclusions(helper.exclusions());
906 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
907 gmx::AnalysisNeighborhoodSearch search =
908 nb_.initSearch(&data.pbc_,
909 data.refPositions().exclusionIds(helper.refPosIds()));
910 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
912 testPairSearchFull(&search, data,
913 data.testPositions().exclusionIds(helper.testPosIds()),
914 helper.exclusions());