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"
66 #include "testutils/testasserts.h"
71 /********************************************************************
72 * NeighborhoodSearchTestData
75 class NeighborhoodSearchTestData
80 RefPair(int refIndex, real distance)
81 : refIndex(refIndex), distance(distance), bFound(false),
86 bool operator<(const RefPair &other) const
88 return refIndex < other.refIndex;
93 // The variables below are state variables that are only used
94 // during the actual testing after creating a copy of the reference
95 // pair list, not as part of the reference data.
96 // Simpler to have just a single structure for both purposes.
103 explicit TestPosition(const rvec x)
104 : refMinDist(0.0), refNearestPoint(-1)
106 copy_rvec(x, this->x);
112 std::vector<RefPair> refPairs;
115 typedef std::vector<TestPosition> TestPositionList;
117 NeighborhoodSearchTestData(int seed, real cutoff);
118 ~NeighborhoodSearchTestData();
120 gmx::AnalysisNeighborhoodPositions refPositions() const
122 return gmx::AnalysisNeighborhoodPositions(refPos_, refPosCount_);
124 gmx::AnalysisNeighborhoodPositions testPositions() const
126 if (testPos_ == NULL)
128 snew(testPos_, testPositions_.size());
129 for (size_t i = 0; i < testPositions_.size(); ++i)
131 copy_rvec(testPositions_[i].x, testPos_[i]);
134 return gmx::AnalysisNeighborhoodPositions(testPos_,
135 testPositions_.size());
137 gmx::AnalysisNeighborhoodPositions testPosition(int index) const
139 return testPositions().selectSingleFromArray(index);
142 void addTestPosition(const rvec x)
144 GMX_RELEASE_ASSERT(testPos_ == NULL,
145 "Cannot add positions after testPositions() call");
146 testPositions_.push_back(TestPosition(x));
148 void generateRandomPosition(rvec x);
149 void generateRandomRefPositions(int count);
150 void generateRandomTestPositions(int count);
151 void computeReferences(t_pbc *pbc)
153 computeReferencesInternal(pbc, false);
155 void computeReferencesXY(t_pbc *pbc)
157 computeReferencesInternal(pbc, true);
160 bool containsPair(int testIndex, const RefPair &pair) const
162 const std::vector<RefPair> &refPairs = testPositions_[testIndex].refPairs;
163 std::vector<RefPair>::const_iterator foundRefPair
164 = std::lower_bound(refPairs.begin(), refPairs.end(), pair);
165 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex)
178 TestPositionList testPositions_;
181 void computeReferencesInternal(t_pbc *pbc, bool bXY);
183 mutable rvec *testPos_;
186 //! Shorthand for a collection of reference pairs.
187 typedef std::vector<NeighborhoodSearchTestData::RefPair> RefPairList;
189 NeighborhoodSearchTestData::NeighborhoodSearchTestData(int seed, real cutoff)
190 : rng_(NULL), cutoff_(cutoff), refPosCount_(0), refPos_(NULL), testPos_(NULL)
192 // TODO: Handle errors.
193 rng_ = gmx_rng_init(seed);
195 set_pbc(&pbc_, epbcNONE, box_);
198 NeighborhoodSearchTestData::~NeighborhoodSearchTestData()
202 gmx_rng_destroy(rng_);
208 void NeighborhoodSearchTestData::generateRandomPosition(rvec x)
211 fx[XX] = gmx_rng_uniform_real(rng_);
212 fx[YY] = gmx_rng_uniform_real(rng_);
213 fx[ZZ] = gmx_rng_uniform_real(rng_);
215 // Add a small displacement to allow positions outside the box
216 x[XX] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
217 x[YY] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
218 x[ZZ] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
221 void NeighborhoodSearchTestData::generateRandomRefPositions(int count)
223 refPosCount_ = count;
224 snew(refPos_, refPosCount_);
225 for (int i = 0; i < refPosCount_; ++i)
227 generateRandomPosition(refPos_[i]);
231 void NeighborhoodSearchTestData::generateRandomTestPositions(int count)
233 testPositions_.reserve(count);
234 for (int i = 0; i < count; ++i)
237 generateRandomPosition(x);
242 void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc *pbc, bool bXY)
244 real cutoff = cutoff_;
247 cutoff = std::numeric_limits<real>::max();
249 TestPositionList::iterator i;
250 for (i = testPositions_.begin(); i != testPositions_.end(); ++i)
252 i->refMinDist = cutoff;
253 i->refNearestPoint = -1;
255 for (int j = 0; j < refPosCount_; ++j)
260 pbc_dx(pbc, i->x, refPos_[j], dx);
264 rvec_sub(i->x, refPos_[j], dx);
266 // TODO: This may not work intuitively for 2D with the third box
267 // vector not parallel to the Z axis, but neither does the actual
268 // neighborhood search.
270 !bXY ? norm(dx) : sqrt(sqr(dx[XX]) + sqr(dx[YY]));
271 if (dist < i->refMinDist)
273 i->refMinDist = dist;
274 i->refNearestPoint = j;
278 RefPair pair(j, dist);
279 GMX_RELEASE_ASSERT(i->refPairs.empty() || i->refPairs.back() < pair,
280 "Reference pairs should be generated in sorted order");
281 i->refPairs.push_back(pair);
287 /********************************************************************
291 class ExclusionsHelper
294 static void markExcludedPairs(RefPairList *refPairs, int testIndex,
295 const t_blocka *excls);
297 ExclusionsHelper(int refPosCount, int testPosCount);
299 void generateExclusions();
301 const t_blocka *exclusions() const { return &excls_; }
303 gmx::ConstArrayRef<int> refPosIds() const
305 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
306 exclusionIds_.begin() + refPosCount_);
308 gmx::ConstArrayRef<int> testPosIds() const
310 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
311 exclusionIds_.begin() + testPosCount_);
317 std::vector<int> exclusionIds_;
318 std::vector<int> exclsIndex_;
319 std::vector<int> exclsAtoms_;
324 void ExclusionsHelper::markExcludedPairs(RefPairList *refPairs, int testIndex,
325 const t_blocka *excls)
328 for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
330 const int excludedIndex = excls->a[i];
331 NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
332 RefPairList::iterator excludedRefPair
333 = std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
334 if (excludedRefPair != refPairs->end()
335 && excludedRefPair->refIndex == excludedIndex)
337 excludedRefPair->bFound = true;
338 excludedRefPair->bExcluded = true;
344 ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount)
345 : refPosCount_(refPosCount), testPosCount_(testPosCount)
347 // Generate an array of 0, 1, 2, ...
348 // TODO: Make the tests work also with non-trivial exclusion IDs,
350 exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
351 exclusionIds_[0] = 0;
352 std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(),
353 exclusionIds_.begin());
359 excls_.nalloc_index = 0;
363 void ExclusionsHelper::generateExclusions()
365 // TODO: Consider a better set of test data, where the density of the
366 // particles would be higher, or where the exclusions would not be random,
367 // to make a higher percentage of the exclusions to actually be within the
369 exclsIndex_.reserve(testPosCount_ + 1);
370 exclsAtoms_.reserve(testPosCount_ * 20);
371 exclsIndex_.push_back(0);
372 for (int i = 0; i < testPosCount_; ++i)
374 for (int j = 0; j < 20; ++j)
376 exclsAtoms_.push_back(i + j*3);
378 exclsIndex_.push_back(exclsAtoms_.size());
380 excls_.nr = exclsIndex_.size();
381 excls_.index = &exclsIndex_[0];
382 excls_.nra = exclsAtoms_.size();
383 excls_.a = &exclsAtoms_[0];
386 /********************************************************************
387 * NeighborhoodSearchTest
390 class NeighborhoodSearchTest : public ::testing::Test
393 void testIsWithin(gmx::AnalysisNeighborhoodSearch *search,
394 const NeighborhoodSearchTestData &data);
395 void testMinimumDistance(gmx::AnalysisNeighborhoodSearch *search,
396 const NeighborhoodSearchTestData &data);
397 void testNearestPoint(gmx::AnalysisNeighborhoodSearch *search,
398 const NeighborhoodSearchTestData &data);
399 void testPairSearch(gmx::AnalysisNeighborhoodSearch *search,
400 const NeighborhoodSearchTestData &data);
401 void testPairSearchFull(gmx::AnalysisNeighborhoodSearch *search,
402 const NeighborhoodSearchTestData &data,
403 const gmx::AnalysisNeighborhoodPositions &pos,
404 const t_blocka *excls);
406 gmx::AnalysisNeighborhood nb_;
409 void NeighborhoodSearchTest::testIsWithin(
410 gmx::AnalysisNeighborhoodSearch *search,
411 const NeighborhoodSearchTestData &data)
413 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
414 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
416 const bool bWithin = (i->refMinDist <= data.cutoff_);
417 EXPECT_EQ(bWithin, search->isWithin(i->x))
418 << "Distance is " << i->refMinDist;
422 void NeighborhoodSearchTest::testMinimumDistance(
423 gmx::AnalysisNeighborhoodSearch *search,
424 const NeighborhoodSearchTestData &data)
426 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
427 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
429 const real refDist = i->refMinDist;
430 EXPECT_REAL_EQ_TOL(refDist, search->minimumDistance(i->x),
431 gmx::test::ulpTolerance(20));
435 void NeighborhoodSearchTest::testNearestPoint(
436 gmx::AnalysisNeighborhoodSearch *search,
437 const NeighborhoodSearchTestData &data)
439 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
440 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
442 const gmx::AnalysisNeighborhoodPair pair = search->nearestPoint(i->x);
445 EXPECT_EQ(i->refNearestPoint, pair.refIndex());
446 EXPECT_EQ(0, pair.testIndex());
447 EXPECT_REAL_EQ_TOL(i->refMinDist, sqrt(pair.distance2()),
448 gmx::test::ulpTolerance(64));
452 EXPECT_EQ(i->refNearestPoint, -1);
458 * Helper function to check that all expected pairs were found.
460 static void checkAllPairsFound(const RefPairList &refPairs)
462 // This could be elegantly expressed with Google Mock matchers, but that
463 // has a significant effect on the runtime of the tests...
464 for (RefPairList::const_iterator i = refPairs.begin(); i != refPairs.end(); ++i)
469 << "Some pairs within the cutoff were not found.";
475 void NeighborhoodSearchTest::testPairSearch(
476 gmx::AnalysisNeighborhoodSearch *search,
477 const NeighborhoodSearchTestData &data)
479 testPairSearchFull(search, data, data.testPositions(), NULL);
482 void NeighborhoodSearchTest::testPairSearchFull(
483 gmx::AnalysisNeighborhoodSearch *search,
484 const NeighborhoodSearchTestData &data,
485 const gmx::AnalysisNeighborhoodPositions &pos,
486 const t_blocka *excls)
488 // TODO: Some parts of this code do not work properly if pos does not
489 // contain all the test positions.
490 std::set<int> remainingTestPositions;
491 for (size_t i = 0; i < data.testPositions_.size(); ++i)
493 remainingTestPositions.insert(i);
495 gmx::AnalysisNeighborhoodPairSearch pairSearch
496 = search->startPairSearch(pos);
497 gmx::AnalysisNeighborhoodPair pair;
498 // TODO: There is an ordering assumption here that may break in the future:
499 // all pairs for a test position are assumed to be returned consencutively.
500 RefPairList refPairs;
501 int prevTestPos = -1;
502 while (pairSearch.findNextPair(&pair))
504 if (pair.testIndex() != prevTestPos)
506 if (prevTestPos != -1)
508 checkAllPairsFound(refPairs);
510 const int testIndex = pair.testIndex();
511 if (remainingTestPositions.count(testIndex) == 0)
514 << "Pairs for test position " << testIndex
515 << " are returned more than once.";
517 remainingTestPositions.erase(testIndex);
518 refPairs = data.testPositions_[testIndex].refPairs;
521 ExclusionsHelper::markExcludedPairs(&refPairs, testIndex, excls);
523 prevTestPos = testIndex;
526 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(),
527 sqrt(pair.distance2()));
528 RefPairList::iterator foundRefPair
529 = std::lower_bound(refPairs.begin(), refPairs.end(), searchPair);
530 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex())
533 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
534 << pair.testIndex() << ") is not within the cutoff.\n"
535 << " Actual: It is returned.";
537 else if (foundRefPair->bExcluded)
540 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
541 << pair.testIndex() << ") is excluded from the search.\n"
542 << " Actual: It is returned.";
544 else if (foundRefPair->bFound)
547 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
548 << pair.testIndex() << ") is returned only once.\n"
549 << " Actual: It is returned multiple times.";
553 foundRefPair->bFound = true;
554 EXPECT_REAL_EQ_TOL(foundRefPair->distance, searchPair.distance,
555 gmx::test::ulpTolerance(64))
556 << "Distance computed by the neighborhood search does not match.";
559 checkAllPairsFound(refPairs);
560 for (std::set<int>::const_iterator i = remainingTestPositions.begin();
561 i != remainingTestPositions.end(); ++i)
563 if (!data.testPositions_[*i].refPairs.empty())
566 << "Expected: Pairs would be returned for test position " << *i << ".\n"
567 << " Actual: None were returned.";
573 /********************************************************************
574 * Test data generation
577 class TrivialTestData
580 static const NeighborhoodSearchTestData &get()
582 static TrivialTestData singleton;
583 return singleton.data_;
586 TrivialTestData() : data_(12345, 1.0)
588 data_.box_[XX][XX] = 5.0;
589 data_.box_[YY][YY] = 5.0;
590 data_.box_[ZZ][ZZ] = 5.0;
591 data_.generateRandomRefPositions(10);
592 data_.generateRandomTestPositions(5);
593 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
594 data_.computeReferences(&data_.pbc_);
598 NeighborhoodSearchTestData data_;
601 class RandomBoxFullPBCData
604 static const NeighborhoodSearchTestData &get()
606 static RandomBoxFullPBCData singleton;
607 return singleton.data_;
610 RandomBoxFullPBCData() : data_(12345, 1.0)
612 data_.box_[XX][XX] = 10.0;
613 data_.box_[YY][YY] = 5.0;
614 data_.box_[ZZ][ZZ] = 7.0;
615 // TODO: Consider whether manually picking some positions would give better
617 data_.generateRandomRefPositions(1000);
618 data_.generateRandomTestPositions(100);
619 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
620 data_.computeReferences(&data_.pbc_);
624 NeighborhoodSearchTestData data_;
627 class RandomBoxXYFullPBCData
630 static const NeighborhoodSearchTestData &get()
632 static RandomBoxXYFullPBCData singleton;
633 return singleton.data_;
636 RandomBoxXYFullPBCData() : data_(54321, 1.0)
638 data_.box_[XX][XX] = 10.0;
639 data_.box_[YY][YY] = 5.0;
640 data_.box_[ZZ][ZZ] = 7.0;
641 // TODO: Consider whether manually picking some positions would give better
643 data_.generateRandomRefPositions(1000);
644 data_.generateRandomTestPositions(100);
645 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
646 data_.computeReferencesXY(&data_.pbc_);
650 NeighborhoodSearchTestData data_;
653 class RandomTriclinicFullPBCData
656 static const NeighborhoodSearchTestData &get()
658 static RandomTriclinicFullPBCData singleton;
659 return singleton.data_;
662 RandomTriclinicFullPBCData() : data_(12345, 1.0)
664 data_.box_[XX][XX] = 5.0;
665 data_.box_[YY][XX] = 2.5;
666 data_.box_[YY][YY] = 2.5*sqrt(3.0);
667 data_.box_[ZZ][XX] = 2.5;
668 data_.box_[ZZ][YY] = 2.5*sqrt(1.0/3.0);
669 data_.box_[ZZ][ZZ] = 5.0*sqrt(2.0/3.0);
670 // TODO: Consider whether manually picking some positions would give better
672 data_.generateRandomRefPositions(1000);
673 data_.generateRandomTestPositions(100);
674 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
675 data_.computeReferences(&data_.pbc_);
679 NeighborhoodSearchTestData data_;
682 class RandomBox2DPBCData
685 static const NeighborhoodSearchTestData &get()
687 static RandomBox2DPBCData singleton;
688 return singleton.data_;
691 RandomBox2DPBCData() : data_(12345, 1.0)
693 data_.box_[XX][XX] = 10.0;
694 data_.box_[YY][YY] = 7.0;
695 data_.box_[ZZ][ZZ] = 5.0;
696 // TODO: Consider whether manually picking some positions would give better
698 data_.generateRandomRefPositions(1000);
699 data_.generateRandomTestPositions(100);
700 set_pbc(&data_.pbc_, epbcXY, data_.box_);
701 data_.computeReferences(&data_.pbc_);
705 NeighborhoodSearchTestData data_;
708 /********************************************************************
712 TEST_F(NeighborhoodSearchTest, SimpleSearch)
714 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
716 nb_.setCutoff(data.cutoff_);
717 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
718 gmx::AnalysisNeighborhoodSearch search =
719 nb_.initSearch(&data.pbc_, data.refPositions());
720 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
722 testIsWithin(&search, data);
723 testMinimumDistance(&search, data);
724 testNearestPoint(&search, data);
725 testPairSearch(&search, data);
728 TEST_F(NeighborhoodSearchTest, GridSearchBox)
730 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
732 nb_.setCutoff(data.cutoff_);
733 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
734 gmx::AnalysisNeighborhoodSearch search =
735 nb_.initSearch(&data.pbc_, data.refPositions());
736 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
738 testIsWithin(&search, data);
739 testMinimumDistance(&search, data);
740 testNearestPoint(&search, data);
741 testPairSearch(&search, data);
744 TEST_F(NeighborhoodSearchTest, GridSearchTriclinic)
746 const NeighborhoodSearchTestData &data = RandomTriclinicFullPBCData::get();
748 nb_.setCutoff(data.cutoff_);
749 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
750 gmx::AnalysisNeighborhoodSearch search =
751 nb_.initSearch(&data.pbc_, data.refPositions());
752 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
754 testPairSearch(&search, data);
757 TEST_F(NeighborhoodSearchTest, GridSearch2DPBC)
759 const NeighborhoodSearchTestData &data = RandomBox2DPBCData::get();
761 nb_.setCutoff(data.cutoff_);
762 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
763 gmx::AnalysisNeighborhoodSearch search =
764 nb_.initSearch(&data.pbc_, data.refPositions());
765 // Currently, grid searching not supported with 2D PBC.
766 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
768 testIsWithin(&search, data);
769 testMinimumDistance(&search, data);
770 testNearestPoint(&search, data);
771 testPairSearch(&search, data);
774 TEST_F(NeighborhoodSearchTest, GridSearchXYBox)
776 const NeighborhoodSearchTestData &data = RandomBoxXYFullPBCData::get();
778 nb_.setCutoff(data.cutoff_);
779 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
781 gmx::AnalysisNeighborhoodSearch search =
782 nb_.initSearch(&data.pbc_, data.refPositions());
783 // Currently, grid searching not supported with XY.
784 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
786 testIsWithin(&search, data);
787 testMinimumDistance(&search, data);
788 testNearestPoint(&search, data);
789 testPairSearch(&search, data);
792 TEST_F(NeighborhoodSearchTest, HandlesConcurrentSearches)
794 const NeighborhoodSearchTestData &data = TrivialTestData::get();
796 nb_.setCutoff(data.cutoff_);
797 gmx::AnalysisNeighborhoodSearch search1 =
798 nb_.initSearch(&data.pbc_, data.refPositions());
799 gmx::AnalysisNeighborhoodSearch search2 =
800 nb_.initSearch(&data.pbc_, data.refPositions());
802 gmx::AnalysisNeighborhoodPairSearch pairSearch1 =
803 search1.startPairSearch(data.testPosition(0));
804 gmx::AnalysisNeighborhoodPairSearch pairSearch2 =
805 search1.startPairSearch(data.testPosition(1));
807 testPairSearch(&search2, data);
809 gmx::AnalysisNeighborhoodPair pair;
810 ASSERT_TRUE(pairSearch1.findNextPair(&pair))
811 << "Test data did not contain any pairs for position 0 (problem in the test).";
812 EXPECT_EQ(0, pair.testIndex());
814 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
815 EXPECT_TRUE(data.containsPair(0, searchPair));
818 ASSERT_TRUE(pairSearch2.findNextPair(&pair))
819 << "Test data did not contain any pairs for position 1 (problem in the test).";
820 EXPECT_EQ(1, pair.testIndex());
822 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
823 EXPECT_TRUE(data.containsPair(1, searchPair));
827 TEST_F(NeighborhoodSearchTest, HandlesSkippingPairs)
829 const NeighborhoodSearchTestData &data = TrivialTestData::get();
831 nb_.setCutoff(data.cutoff_);
832 gmx::AnalysisNeighborhoodSearch search =
833 nb_.initSearch(&data.pbc_, data.refPositions());
834 gmx::AnalysisNeighborhoodPairSearch pairSearch =
835 search.startPairSearch(data.testPositions());
836 gmx::AnalysisNeighborhoodPair pair;
837 // TODO: This test needs to be adjusted if the grid search gets optimized
838 // to loop over the test positions in cell order (first, the ordering
839 // assumption here breaks, and second, it then needs to be tested
840 // separately for simple and grid searches).
841 int currentIndex = 0;
842 while (pairSearch.findNextPair(&pair))
844 while (currentIndex < pair.testIndex())
848 EXPECT_EQ(currentIndex, pair.testIndex());
849 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
850 EXPECT_TRUE(data.containsPair(currentIndex, searchPair));
851 pairSearch.skipRemainingPairsForTestPosition();
856 TEST_F(NeighborhoodSearchTest, SimpleSearchExclusions)
858 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
860 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
861 helper.generateExclusions();
863 nb_.setCutoff(data.cutoff_);
864 nb_.setTopologyExclusions(helper.exclusions());
865 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
866 gmx::AnalysisNeighborhoodSearch search =
867 nb_.initSearch(&data.pbc_,
868 data.refPositions().exclusionIds(helper.refPosIds()));
869 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
871 testPairSearchFull(&search, data,
872 data.testPositions().exclusionIds(helper.testPosIds()),
873 helper.exclusions());
876 TEST_F(NeighborhoodSearchTest, GridSearchExclusions)
878 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
880 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
881 helper.generateExclusions();
883 nb_.setCutoff(data.cutoff_);
884 nb_.setTopologyExclusions(helper.exclusions());
885 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
886 gmx::AnalysisNeighborhoodSearch search =
887 nb_.initSearch(&data.pbc_,
888 data.refPositions().exclusionIds(helper.refPosIds()));
889 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
891 testPairSearchFull(&search, data,
892 data.testPositions().exclusionIds(helper.testPosIds()),
893 helper.exclusions());