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 TestPosition() : refMinDist(0.0), refNearestPoint(-1)
107 explicit TestPosition(const rvec x)
108 : refMinDist(0.0), refNearestPoint(-1)
110 copy_rvec(x, this->x);
116 std::vector<RefPair> refPairs;
119 typedef std::vector<TestPosition> TestPositionList;
121 NeighborhoodSearchTestData(int seed, real cutoff);
122 ~NeighborhoodSearchTestData();
124 gmx::AnalysisNeighborhoodPositions refPositions() const
126 return gmx::AnalysisNeighborhoodPositions(refPos_, refPosCount_);
128 gmx::AnalysisNeighborhoodPositions testPositions() const
130 if (testPos_ == NULL)
132 snew(testPos_, testPositions_.size());
133 for (size_t i = 0; i < testPositions_.size(); ++i)
135 copy_rvec(testPositions_[i].x, testPos_[i]);
138 return gmx::AnalysisNeighborhoodPositions(testPos_,
139 testPositions_.size());
141 gmx::AnalysisNeighborhoodPositions testPosition(int index) const
143 return testPositions().selectSingleFromArray(index);
146 void addTestPosition(const rvec x)
148 GMX_RELEASE_ASSERT(testPos_ == NULL,
149 "Cannot add positions after testPositions() call");
150 testPositions_.push_back(TestPosition(x));
152 void generateRandomPosition(rvec x);
153 void generateRandomRefPositions(int count);
154 void generateRandomTestPositions(int count);
155 void computeReferences(t_pbc *pbc)
157 computeReferencesInternal(pbc, false);
159 void computeReferencesXY(t_pbc *pbc)
161 computeReferencesInternal(pbc, true);
164 bool containsPair(int testIndex, const RefPair &pair) const
166 const std::vector<RefPair> &refPairs = testPositions_[testIndex].refPairs;
167 std::vector<RefPair>::const_iterator foundRefPair
168 = std::lower_bound(refPairs.begin(), refPairs.end(), pair);
169 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex)
182 TestPositionList testPositions_;
185 void computeReferencesInternal(t_pbc *pbc, bool bXY);
187 mutable rvec *testPos_;
190 //! Shorthand for a collection of reference pairs.
191 typedef std::vector<NeighborhoodSearchTestData::RefPair> RefPairList;
193 NeighborhoodSearchTestData::NeighborhoodSearchTestData(int seed, real cutoff)
194 : rng_(NULL), cutoff_(cutoff), refPosCount_(0), refPos_(NULL), testPos_(NULL)
196 // TODO: Handle errors.
197 rng_ = gmx_rng_init(seed);
199 set_pbc(&pbc_, epbcNONE, box_);
202 NeighborhoodSearchTestData::~NeighborhoodSearchTestData()
206 gmx_rng_destroy(rng_);
212 void NeighborhoodSearchTestData::generateRandomPosition(rvec x)
215 fx[XX] = gmx_rng_uniform_real(rng_);
216 fx[YY] = gmx_rng_uniform_real(rng_);
217 fx[ZZ] = gmx_rng_uniform_real(rng_);
219 // Add a small displacement to allow positions outside the box
220 x[XX] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
221 x[YY] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
222 x[ZZ] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
225 void NeighborhoodSearchTestData::generateRandomRefPositions(int count)
227 refPosCount_ = count;
228 snew(refPos_, refPosCount_);
229 for (int i = 0; i < refPosCount_; ++i)
231 generateRandomPosition(refPos_[i]);
235 void NeighborhoodSearchTestData::generateRandomTestPositions(int count)
237 testPositions_.reserve(count);
238 for (int i = 0; i < count; ++i)
241 generateRandomPosition(x);
246 void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc *pbc, bool bXY)
248 real cutoff = cutoff_;
251 cutoff = std::numeric_limits<real>::max();
253 TestPositionList::iterator i;
254 for (i = testPositions_.begin(); i != testPositions_.end(); ++i)
256 i->refMinDist = cutoff;
257 i->refNearestPoint = -1;
259 for (int j = 0; j < refPosCount_; ++j)
264 pbc_dx(pbc, i->x, refPos_[j], dx);
268 rvec_sub(i->x, refPos_[j], dx);
270 // TODO: This may not work intuitively for 2D with the third box
271 // vector not parallel to the Z axis, but neither does the actual
272 // neighborhood search.
274 !bXY ? norm(dx) : sqrt(sqr(dx[XX]) + sqr(dx[YY]));
275 if (dist < i->refMinDist)
277 i->refMinDist = dist;
278 i->refNearestPoint = j;
282 RefPair pair(j, dist);
283 GMX_RELEASE_ASSERT(i->refPairs.empty() || i->refPairs.back() < pair,
284 "Reference pairs should be generated in sorted order");
285 i->refPairs.push_back(pair);
291 /********************************************************************
295 class ExclusionsHelper
298 static void markExcludedPairs(RefPairList *refPairs, int testIndex,
299 const t_blocka *excls);
301 ExclusionsHelper(int refPosCount, int testPosCount);
303 void generateExclusions();
305 const t_blocka *exclusions() const { return &excls_; }
307 gmx::ConstArrayRef<int> refPosIds() const
309 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
310 exclusionIds_.begin() + refPosCount_);
312 gmx::ConstArrayRef<int> testPosIds() const
314 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
315 exclusionIds_.begin() + testPosCount_);
321 std::vector<int> exclusionIds_;
322 std::vector<int> exclsIndex_;
323 std::vector<int> exclsAtoms_;
328 void ExclusionsHelper::markExcludedPairs(RefPairList *refPairs, int testIndex,
329 const t_blocka *excls)
332 for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
334 const int excludedIndex = excls->a[i];
335 NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
336 RefPairList::iterator excludedRefPair
337 = std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
338 if (excludedRefPair != refPairs->end()
339 && excludedRefPair->refIndex == excludedIndex)
341 excludedRefPair->bFound = true;
342 excludedRefPair->bExcluded = true;
348 ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount)
349 : refPosCount_(refPosCount), testPosCount_(testPosCount)
351 // Generate an array of 0, 1, 2, ...
352 // TODO: Make the tests work also with non-trivial exclusion IDs,
354 exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
355 exclusionIds_[0] = 0;
356 std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(),
357 exclusionIds_.begin());
363 excls_.nalloc_index = 0;
367 void ExclusionsHelper::generateExclusions()
369 // TODO: Consider a better set of test data, where the density of the
370 // particles would be higher, or where the exclusions would not be random,
371 // to make a higher percentage of the exclusions to actually be within the
373 exclsIndex_.reserve(testPosCount_ + 1);
374 exclsAtoms_.reserve(testPosCount_ * 20);
375 exclsIndex_.push_back(0);
376 for (int i = 0; i < testPosCount_; ++i)
378 for (int j = 0; j < 20; ++j)
380 exclsAtoms_.push_back(i + j*3);
382 exclsIndex_.push_back(exclsAtoms_.size());
384 excls_.nr = exclsIndex_.size();
385 excls_.index = &exclsIndex_[0];
386 excls_.nra = exclsAtoms_.size();
387 excls_.a = &exclsAtoms_[0];
390 /********************************************************************
391 * NeighborhoodSearchTest
394 class NeighborhoodSearchTest : public ::testing::Test
397 void testIsWithin(gmx::AnalysisNeighborhoodSearch *search,
398 const NeighborhoodSearchTestData &data);
399 void testMinimumDistance(gmx::AnalysisNeighborhoodSearch *search,
400 const NeighborhoodSearchTestData &data);
401 void testNearestPoint(gmx::AnalysisNeighborhoodSearch *search,
402 const NeighborhoodSearchTestData &data);
403 void testPairSearch(gmx::AnalysisNeighborhoodSearch *search,
404 const NeighborhoodSearchTestData &data);
405 void testPairSearchFull(gmx::AnalysisNeighborhoodSearch *search,
406 const NeighborhoodSearchTestData &data,
407 const gmx::AnalysisNeighborhoodPositions &pos,
408 const t_blocka *excls);
410 gmx::AnalysisNeighborhood nb_;
413 void NeighborhoodSearchTest::testIsWithin(
414 gmx::AnalysisNeighborhoodSearch *search,
415 const NeighborhoodSearchTestData &data)
417 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
418 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
420 const bool bWithin = (i->refMinDist <= data.cutoff_);
421 EXPECT_EQ(bWithin, search->isWithin(i->x))
422 << "Distance is " << i->refMinDist;
426 void NeighborhoodSearchTest::testMinimumDistance(
427 gmx::AnalysisNeighborhoodSearch *search,
428 const NeighborhoodSearchTestData &data)
430 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
431 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
433 const real refDist = i->refMinDist;
434 EXPECT_REAL_EQ_TOL(refDist, search->minimumDistance(i->x),
435 gmx::test::ulpTolerance(20));
439 void NeighborhoodSearchTest::testNearestPoint(
440 gmx::AnalysisNeighborhoodSearch *search,
441 const NeighborhoodSearchTestData &data)
443 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
444 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
446 const gmx::AnalysisNeighborhoodPair pair = search->nearestPoint(i->x);
449 EXPECT_EQ(i->refNearestPoint, pair.refIndex());
450 EXPECT_EQ(0, pair.testIndex());
451 EXPECT_REAL_EQ_TOL(i->refMinDist, sqrt(pair.distance2()),
452 gmx::test::ulpTolerance(64));
456 EXPECT_EQ(i->refNearestPoint, -1);
462 * Helper function to check that all expected pairs were found.
464 static void checkAllPairsFound(const RefPairList &refPairs)
466 // This could be elegantly expressed with Google Mock matchers, but that
467 // has a significant effect on the runtime of the tests...
468 for (RefPairList::const_iterator i = refPairs.begin(); i != refPairs.end(); ++i)
473 << "Some pairs within the cutoff were not found.";
479 void NeighborhoodSearchTest::testPairSearch(
480 gmx::AnalysisNeighborhoodSearch *search,
481 const NeighborhoodSearchTestData &data)
483 testPairSearchFull(search, data, data.testPositions(), NULL);
486 void NeighborhoodSearchTest::testPairSearchFull(
487 gmx::AnalysisNeighborhoodSearch *search,
488 const NeighborhoodSearchTestData &data,
489 const gmx::AnalysisNeighborhoodPositions &pos,
490 const t_blocka *excls)
492 // TODO: Some parts of this code do not work properly if pos does not
493 // contain all the test positions.
494 std::set<int> remainingTestPositions;
495 for (size_t i = 0; i < data.testPositions_.size(); ++i)
497 remainingTestPositions.insert(i);
499 gmx::AnalysisNeighborhoodPairSearch pairSearch
500 = search->startPairSearch(pos);
501 gmx::AnalysisNeighborhoodPair pair;
502 // TODO: There is an ordering assumption here that may break in the future:
503 // all pairs for a test position are assumed to be returned consencutively.
504 RefPairList refPairs;
505 int prevTestPos = -1;
506 while (pairSearch.findNextPair(&pair))
508 if (pair.testIndex() != prevTestPos)
510 if (prevTestPos != -1)
512 checkAllPairsFound(refPairs);
514 const int testIndex = pair.testIndex();
515 if (remainingTestPositions.count(testIndex) == 0)
518 << "Pairs for test position " << testIndex
519 << " are returned more than once.";
521 remainingTestPositions.erase(testIndex);
522 refPairs = data.testPositions_[testIndex].refPairs;
525 ExclusionsHelper::markExcludedPairs(&refPairs, testIndex, excls);
527 prevTestPos = testIndex;
530 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(),
531 sqrt(pair.distance2()));
532 RefPairList::iterator foundRefPair
533 = std::lower_bound(refPairs.begin(), refPairs.end(), searchPair);
534 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex())
537 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
538 << pair.testIndex() << ") is not within the cutoff.\n"
539 << " Actual: It is returned.";
541 else if (foundRefPair->bExcluded)
544 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
545 << pair.testIndex() << ") is excluded from the search.\n"
546 << " Actual: It is returned.";
548 else if (foundRefPair->bFound)
551 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
552 << pair.testIndex() << ") is returned only once.\n"
553 << " Actual: It is returned multiple times.";
557 foundRefPair->bFound = true;
558 EXPECT_REAL_EQ_TOL(foundRefPair->distance, searchPair.distance,
559 gmx::test::ulpTolerance(64))
560 << "Distance computed by the neighborhood search does not match.";
563 checkAllPairsFound(refPairs);
564 for (std::set<int>::const_iterator i = remainingTestPositions.begin();
565 i != remainingTestPositions.end(); ++i)
567 if (!data.testPositions_[*i].refPairs.empty())
570 << "Expected: Pairs would be returned for test position " << *i << ".\n"
571 << " Actual: None were returned.";
577 /********************************************************************
578 * Test data generation
581 class TrivialTestData
584 static const NeighborhoodSearchTestData &get()
586 static TrivialTestData singleton;
587 return singleton.data_;
590 TrivialTestData() : data_(12345, 1.0)
592 data_.box_[XX][XX] = 5.0;
593 data_.box_[YY][YY] = 5.0;
594 data_.box_[ZZ][ZZ] = 5.0;
595 data_.generateRandomRefPositions(10);
596 data_.generateRandomTestPositions(5);
597 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
598 data_.computeReferences(&data_.pbc_);
602 NeighborhoodSearchTestData data_;
605 class RandomBoxFullPBCData
608 static const NeighborhoodSearchTestData &get()
610 static RandomBoxFullPBCData singleton;
611 return singleton.data_;
614 RandomBoxFullPBCData() : data_(12345, 1.0)
616 data_.box_[XX][XX] = 10.0;
617 data_.box_[YY][YY] = 5.0;
618 data_.box_[ZZ][ZZ] = 7.0;
619 // TODO: Consider whether manually picking some positions would give better
621 data_.generateRandomRefPositions(1000);
622 data_.generateRandomTestPositions(100);
623 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
624 data_.computeReferences(&data_.pbc_);
628 NeighborhoodSearchTestData data_;
631 class RandomBoxXYFullPBCData
634 static const NeighborhoodSearchTestData &get()
636 static RandomBoxXYFullPBCData singleton;
637 return singleton.data_;
640 RandomBoxXYFullPBCData() : data_(54321, 1.0)
642 data_.box_[XX][XX] = 10.0;
643 data_.box_[YY][YY] = 5.0;
644 data_.box_[ZZ][ZZ] = 7.0;
645 // TODO: Consider whether manually picking some positions would give better
647 data_.generateRandomRefPositions(1000);
648 data_.generateRandomTestPositions(100);
649 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
650 data_.computeReferencesXY(&data_.pbc_);
654 NeighborhoodSearchTestData data_;
657 class RandomTriclinicFullPBCData
660 static const NeighborhoodSearchTestData &get()
662 static RandomTriclinicFullPBCData singleton;
663 return singleton.data_;
666 RandomTriclinicFullPBCData() : data_(12345, 1.0)
668 data_.box_[XX][XX] = 5.0;
669 data_.box_[YY][XX] = 2.5;
670 data_.box_[YY][YY] = 2.5*sqrt(3.0);
671 data_.box_[ZZ][XX] = 2.5;
672 data_.box_[ZZ][YY] = 2.5*sqrt(1.0/3.0);
673 data_.box_[ZZ][ZZ] = 5.0*sqrt(2.0/3.0);
674 // TODO: Consider whether manually picking some positions would give better
676 data_.generateRandomRefPositions(1000);
677 data_.generateRandomTestPositions(100);
678 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
679 data_.computeReferences(&data_.pbc_);
683 NeighborhoodSearchTestData data_;
686 class RandomBox2DPBCData
689 static const NeighborhoodSearchTestData &get()
691 static RandomBox2DPBCData singleton;
692 return singleton.data_;
695 RandomBox2DPBCData() : data_(12345, 1.0)
697 data_.box_[XX][XX] = 10.0;
698 data_.box_[YY][YY] = 7.0;
699 data_.box_[ZZ][ZZ] = 5.0;
700 // TODO: Consider whether manually picking some positions would give better
702 data_.generateRandomRefPositions(1000);
703 data_.generateRandomTestPositions(100);
704 set_pbc(&data_.pbc_, epbcXY, data_.box_);
705 data_.computeReferences(&data_.pbc_);
709 NeighborhoodSearchTestData data_;
712 /********************************************************************
716 TEST_F(NeighborhoodSearchTest, SimpleSearch)
718 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
720 nb_.setCutoff(data.cutoff_);
721 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
722 gmx::AnalysisNeighborhoodSearch search =
723 nb_.initSearch(&data.pbc_, data.refPositions());
724 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
726 testIsWithin(&search, data);
727 testMinimumDistance(&search, data);
728 testNearestPoint(&search, data);
729 testPairSearch(&search, data);
732 TEST_F(NeighborhoodSearchTest, GridSearchBox)
734 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
736 nb_.setCutoff(data.cutoff_);
737 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
738 gmx::AnalysisNeighborhoodSearch search =
739 nb_.initSearch(&data.pbc_, data.refPositions());
740 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
742 testIsWithin(&search, data);
743 testMinimumDistance(&search, data);
744 testNearestPoint(&search, data);
745 testPairSearch(&search, data);
748 TEST_F(NeighborhoodSearchTest, GridSearchTriclinic)
750 const NeighborhoodSearchTestData &data = RandomTriclinicFullPBCData::get();
752 nb_.setCutoff(data.cutoff_);
753 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
754 gmx::AnalysisNeighborhoodSearch search =
755 nb_.initSearch(&data.pbc_, data.refPositions());
756 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
758 testPairSearch(&search, data);
761 TEST_F(NeighborhoodSearchTest, GridSearch2DPBC)
763 const NeighborhoodSearchTestData &data = RandomBox2DPBCData::get();
765 nb_.setCutoff(data.cutoff_);
766 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
767 gmx::AnalysisNeighborhoodSearch search =
768 nb_.initSearch(&data.pbc_, data.refPositions());
769 // Currently, grid searching not supported with 2D PBC.
770 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
772 testIsWithin(&search, data);
773 testMinimumDistance(&search, data);
774 testNearestPoint(&search, data);
775 testPairSearch(&search, data);
778 TEST_F(NeighborhoodSearchTest, GridSearchXYBox)
780 const NeighborhoodSearchTestData &data = RandomBoxXYFullPBCData::get();
782 nb_.setCutoff(data.cutoff_);
783 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
785 gmx::AnalysisNeighborhoodSearch search =
786 nb_.initSearch(&data.pbc_, data.refPositions());
787 // Currently, grid searching not supported with XY.
788 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
790 testIsWithin(&search, data);
791 testMinimumDistance(&search, data);
792 testNearestPoint(&search, data);
793 testPairSearch(&search, data);
796 TEST_F(NeighborhoodSearchTest, HandlesConcurrentSearches)
798 const NeighborhoodSearchTestData &data = TrivialTestData::get();
800 nb_.setCutoff(data.cutoff_);
801 gmx::AnalysisNeighborhoodSearch search1 =
802 nb_.initSearch(&data.pbc_, data.refPositions());
803 gmx::AnalysisNeighborhoodSearch search2 =
804 nb_.initSearch(&data.pbc_, data.refPositions());
806 gmx::AnalysisNeighborhoodPairSearch pairSearch1 =
807 search1.startPairSearch(data.testPosition(0));
808 gmx::AnalysisNeighborhoodPairSearch pairSearch2 =
809 search1.startPairSearch(data.testPosition(1));
811 testPairSearch(&search2, data);
813 gmx::AnalysisNeighborhoodPair pair;
814 ASSERT_TRUE(pairSearch1.findNextPair(&pair))
815 << "Test data did not contain any pairs for position 0 (problem in the test).";
816 EXPECT_EQ(0, pair.testIndex());
818 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
819 EXPECT_TRUE(data.containsPair(0, searchPair));
822 ASSERT_TRUE(pairSearch2.findNextPair(&pair))
823 << "Test data did not contain any pairs for position 1 (problem in the test).";
824 EXPECT_EQ(1, pair.testIndex());
826 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
827 EXPECT_TRUE(data.containsPair(1, searchPair));
831 TEST_F(NeighborhoodSearchTest, HandlesSkippingPairs)
833 const NeighborhoodSearchTestData &data = TrivialTestData::get();
835 nb_.setCutoff(data.cutoff_);
836 gmx::AnalysisNeighborhoodSearch search =
837 nb_.initSearch(&data.pbc_, data.refPositions());
838 gmx::AnalysisNeighborhoodPairSearch pairSearch =
839 search.startPairSearch(data.testPositions());
840 gmx::AnalysisNeighborhoodPair pair;
841 // TODO: This test needs to be adjusted if the grid search gets optimized
842 // to loop over the test positions in cell order (first, the ordering
843 // assumption here breaks, and second, it then needs to be tested
844 // separately for simple and grid searches).
845 int currentIndex = 0;
846 while (pairSearch.findNextPair(&pair))
848 while (currentIndex < pair.testIndex())
852 EXPECT_EQ(currentIndex, pair.testIndex());
853 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
854 EXPECT_TRUE(data.containsPair(currentIndex, searchPair));
855 pairSearch.skipRemainingPairsForTestPosition();
860 TEST_F(NeighborhoodSearchTest, SimpleSearchExclusions)
862 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
864 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
865 helper.generateExclusions();
867 nb_.setCutoff(data.cutoff_);
868 nb_.setTopologyExclusions(helper.exclusions());
869 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
870 gmx::AnalysisNeighborhoodSearch search =
871 nb_.initSearch(&data.pbc_,
872 data.refPositions().exclusionIds(helper.refPosIds()));
873 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
875 testPairSearchFull(&search, data,
876 data.testPositions().exclusionIds(helper.testPosIds()),
877 helper.exclusions());
880 TEST_F(NeighborhoodSearchTest, GridSearchExclusions)
882 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
884 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
885 helper.generateExclusions();
887 nb_.setCutoff(data.cutoff_);
888 nb_.setTopologyExclusions(helper.exclusions());
889 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
890 gmx::AnalysisNeighborhoodSearch search =
891 nb_.initSearch(&data.pbc_,
892 data.refPositions().exclusionIds(helper.refPosIds()));
893 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
895 testPairSearchFull(&search, data,
896 data.testPositions().exclusionIds(helper.testPosIds()),
897 helper.exclusions());