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
47 #include "gromacs/selection/nbsearch.h"
49 #include <gtest/gtest.h>
58 #include "gromacs/math/vec.h"
59 #include "gromacs/pbcutil/pbc.h"
60 #include "gromacs/random/random.h"
61 #include "gromacs/topology/block.h"
62 #include "gromacs/utility/smalloc.h"
64 #include "testutils/testasserts.h"
69 /********************************************************************
70 * NeighborhoodSearchTestData
73 class NeighborhoodSearchTestData
78 RefPair(int refIndex, real distance)
79 : refIndex(refIndex), distance(distance), bFound(false),
84 bool operator<(const RefPair &other) const
86 return refIndex < other.refIndex;
91 // The variables below are state variables that are only used
92 // during the actual testing after creating a copy of the reference
93 // pair list, not as part of the reference data.
94 // Simpler to have just a single structure for both purposes.
101 TestPosition() : refMinDist(0.0), refNearestPoint(-1)
105 explicit TestPosition(const rvec x)
106 : refMinDist(0.0), refNearestPoint(-1)
108 copy_rvec(x, this->x);
114 std::vector<RefPair> refPairs;
117 typedef std::vector<TestPosition> TestPositionList;
119 NeighborhoodSearchTestData(int seed, real cutoff);
120 ~NeighborhoodSearchTestData();
122 gmx::AnalysisNeighborhoodPositions refPositions() const
124 return gmx::AnalysisNeighborhoodPositions(refPos_, refPosCount_);
126 gmx::AnalysisNeighborhoodPositions testPositions() const
128 if (testPos_ == NULL)
130 snew(testPos_, testPositions_.size());
131 for (size_t i = 0; i < testPositions_.size(); ++i)
133 copy_rvec(testPositions_[i].x, testPos_[i]);
136 return gmx::AnalysisNeighborhoodPositions(testPos_,
137 testPositions_.size());
139 gmx::AnalysisNeighborhoodPositions testPosition(int index) const
141 return testPositions().selectSingleFromArray(index);
144 void addTestPosition(const rvec x)
146 GMX_RELEASE_ASSERT(testPos_ == NULL,
147 "Cannot add positions after testPositions() call");
148 testPositions_.push_back(TestPosition(x));
150 void generateRandomPosition(rvec x);
151 void generateRandomRefPositions(int count);
152 void generateRandomTestPositions(int count);
153 void computeReferences(t_pbc *pbc)
155 computeReferencesInternal(pbc, false);
157 void computeReferencesXY(t_pbc *pbc)
159 computeReferencesInternal(pbc, true);
162 bool containsPair(int testIndex, const RefPair &pair) const
164 const std::vector<RefPair> &refPairs = testPositions_[testIndex].refPairs;
165 std::vector<RefPair>::const_iterator foundRefPair
166 = std::lower_bound(refPairs.begin(), refPairs.end(), pair);
167 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex)
180 TestPositionList testPositions_;
183 void computeReferencesInternal(t_pbc *pbc, bool bXY);
185 mutable rvec *testPos_;
188 //! Shorthand for a collection of reference pairs.
189 typedef std::vector<NeighborhoodSearchTestData::RefPair> RefPairList;
191 NeighborhoodSearchTestData::NeighborhoodSearchTestData(int seed, real cutoff)
192 : rng_(NULL), cutoff_(cutoff), refPosCount_(0), refPos_(NULL), testPos_(NULL)
194 // TODO: Handle errors.
195 rng_ = gmx_rng_init(seed);
197 set_pbc(&pbc_, epbcNONE, box_);
200 NeighborhoodSearchTestData::~NeighborhoodSearchTestData()
204 gmx_rng_destroy(rng_);
210 void NeighborhoodSearchTestData::generateRandomPosition(rvec x)
213 fx[XX] = gmx_rng_uniform_real(rng_);
214 fx[YY] = gmx_rng_uniform_real(rng_);
215 fx[ZZ] = gmx_rng_uniform_real(rng_);
217 // Add a small displacement to allow positions outside the box
218 x[XX] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
219 x[YY] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
220 x[ZZ] += 0.2 * gmx_rng_uniform_real(rng_) - 0.1;
223 void NeighborhoodSearchTestData::generateRandomRefPositions(int count)
225 refPosCount_ = count;
226 snew(refPos_, refPosCount_);
227 for (int i = 0; i < refPosCount_; ++i)
229 generateRandomPosition(refPos_[i]);
233 void NeighborhoodSearchTestData::generateRandomTestPositions(int count)
235 testPositions_.reserve(count);
236 for (int i = 0; i < count; ++i)
239 generateRandomPosition(x);
244 void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc *pbc, bool bXY)
246 real cutoff = cutoff_;
249 cutoff = std::numeric_limits<real>::max();
251 TestPositionList::iterator i;
252 for (i = testPositions_.begin(); i != testPositions_.end(); ++i)
254 i->refMinDist = cutoff;
255 i->refNearestPoint = -1;
257 for (int j = 0; j < refPosCount_; ++j)
262 pbc_dx(pbc, i->x, refPos_[j], dx);
266 rvec_sub(i->x, refPos_[j], dx);
268 // TODO: This may not work intuitively for 2D with the third box
269 // vector not parallel to the Z axis, but neither does the actual
270 // neighborhood search.
272 !bXY ? norm(dx) : sqrt(sqr(dx[XX]) + sqr(dx[YY]));
273 if (dist < i->refMinDist)
275 i->refMinDist = dist;
276 i->refNearestPoint = j;
280 RefPair pair(j, dist);
281 GMX_RELEASE_ASSERT(i->refPairs.empty() || i->refPairs.back() < pair,
282 "Reference pairs should be generated in sorted order");
283 i->refPairs.push_back(pair);
289 /********************************************************************
293 class ExclusionsHelper
296 static void markExcludedPairs(RefPairList *refPairs, int testIndex,
297 const t_blocka *excls);
299 ExclusionsHelper(int refPosCount, int testPosCount);
301 void generateExclusions();
303 const t_blocka *exclusions() const { return &excls_; }
305 gmx::ConstArrayRef<int> refPosIds() const
307 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
308 exclusionIds_.begin() + refPosCount_);
310 gmx::ConstArrayRef<int> testPosIds() const
312 return gmx::constArrayRefFromVector<int>(exclusionIds_.begin(),
313 exclusionIds_.begin() + testPosCount_);
319 std::vector<int> exclusionIds_;
320 std::vector<int> exclsIndex_;
321 std::vector<int> exclsAtoms_;
326 void ExclusionsHelper::markExcludedPairs(RefPairList *refPairs, int testIndex,
327 const t_blocka *excls)
330 for (int i = excls->index[testIndex]; i < excls->index[testIndex + 1]; ++i)
332 const int excludedIndex = excls->a[i];
333 NeighborhoodSearchTestData::RefPair searchPair(excludedIndex, 0.0);
334 RefPairList::iterator excludedRefPair
335 = std::lower_bound(refPairs->begin(), refPairs->end(), searchPair);
336 if (excludedRefPair != refPairs->end()
337 && excludedRefPair->refIndex == excludedIndex)
339 excludedRefPair->bFound = true;
340 excludedRefPair->bExcluded = true;
346 ExclusionsHelper::ExclusionsHelper(int refPosCount, int testPosCount)
347 : refPosCount_(refPosCount), testPosCount_(testPosCount)
349 // Generate an array of 0, 1, 2, ...
350 // TODO: Make the tests work also with non-trivial exclusion IDs,
352 exclusionIds_.resize(std::max(refPosCount, testPosCount), 1);
353 exclusionIds_[0] = 0;
354 std::partial_sum(exclusionIds_.begin(), exclusionIds_.end(),
355 exclusionIds_.begin());
361 excls_.nalloc_index = 0;
365 void ExclusionsHelper::generateExclusions()
367 // TODO: Consider a better set of test data, where the density of the
368 // particles would be higher, or where the exclusions would not be random,
369 // to make a higher percentage of the exclusions to actually be within the
371 exclsIndex_.reserve(testPosCount_ + 1);
372 exclsAtoms_.reserve(testPosCount_ * 20);
373 exclsIndex_.push_back(0);
374 for (int i = 0; i < testPosCount_; ++i)
376 for (int j = 0; j < 20; ++j)
378 exclsAtoms_.push_back(i + j*3);
380 exclsIndex_.push_back(exclsAtoms_.size());
382 excls_.nr = exclsIndex_.size();
383 excls_.index = &exclsIndex_[0];
384 excls_.nra = exclsAtoms_.size();
385 excls_.a = &exclsAtoms_[0];
388 /********************************************************************
389 * NeighborhoodSearchTest
392 class NeighborhoodSearchTest : public ::testing::Test
395 void testIsWithin(gmx::AnalysisNeighborhoodSearch *search,
396 const NeighborhoodSearchTestData &data);
397 void testMinimumDistance(gmx::AnalysisNeighborhoodSearch *search,
398 const NeighborhoodSearchTestData &data);
399 void testNearestPoint(gmx::AnalysisNeighborhoodSearch *search,
400 const NeighborhoodSearchTestData &data);
401 void testPairSearch(gmx::AnalysisNeighborhoodSearch *search,
402 const NeighborhoodSearchTestData &data);
403 void testPairSearchFull(gmx::AnalysisNeighborhoodSearch *search,
404 const NeighborhoodSearchTestData &data,
405 const gmx::AnalysisNeighborhoodPositions &pos,
406 const t_blocka *excls);
408 gmx::AnalysisNeighborhood nb_;
411 void NeighborhoodSearchTest::testIsWithin(
412 gmx::AnalysisNeighborhoodSearch *search,
413 const NeighborhoodSearchTestData &data)
415 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
416 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
418 const bool bWithin = (i->refMinDist <= data.cutoff_);
419 EXPECT_EQ(bWithin, search->isWithin(i->x))
420 << "Distance is " << i->refMinDist;
424 void NeighborhoodSearchTest::testMinimumDistance(
425 gmx::AnalysisNeighborhoodSearch *search,
426 const NeighborhoodSearchTestData &data)
428 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
429 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
431 const real refDist = i->refMinDist;
432 EXPECT_REAL_EQ_TOL(refDist, search->minimumDistance(i->x),
433 gmx::test::ulpTolerance(20));
437 void NeighborhoodSearchTest::testNearestPoint(
438 gmx::AnalysisNeighborhoodSearch *search,
439 const NeighborhoodSearchTestData &data)
441 NeighborhoodSearchTestData::TestPositionList::const_iterator i;
442 for (i = data.testPositions_.begin(); i != data.testPositions_.end(); ++i)
444 const gmx::AnalysisNeighborhoodPair pair = search->nearestPoint(i->x);
447 EXPECT_EQ(i->refNearestPoint, pair.refIndex());
448 EXPECT_EQ(0, pair.testIndex());
449 EXPECT_REAL_EQ_TOL(i->refMinDist, sqrt(pair.distance2()),
450 gmx::test::ulpTolerance(64));
454 EXPECT_EQ(i->refNearestPoint, -1);
460 * Helper function to check that all expected pairs were found.
462 static void checkAllPairsFound(const RefPairList &refPairs)
464 // This could be elegantly expressed with Google Mock matchers, but that
465 // has a significant effect on the runtime of the tests...
466 for (RefPairList::const_iterator i = refPairs.begin(); i != refPairs.end(); ++i)
471 << "Some pairs within the cutoff were not found.";
477 void NeighborhoodSearchTest::testPairSearch(
478 gmx::AnalysisNeighborhoodSearch *search,
479 const NeighborhoodSearchTestData &data)
481 testPairSearchFull(search, data, data.testPositions(), NULL);
484 void NeighborhoodSearchTest::testPairSearchFull(
485 gmx::AnalysisNeighborhoodSearch *search,
486 const NeighborhoodSearchTestData &data,
487 const gmx::AnalysisNeighborhoodPositions &pos,
488 const t_blocka *excls)
490 // TODO: Some parts of this code do not work properly if pos does not
491 // contain all the test positions.
492 std::set<int> remainingTestPositions;
493 for (size_t i = 0; i < data.testPositions_.size(); ++i)
495 remainingTestPositions.insert(i);
497 gmx::AnalysisNeighborhoodPairSearch pairSearch
498 = search->startPairSearch(pos);
499 gmx::AnalysisNeighborhoodPair pair;
500 // TODO: There is an ordering assumption here that may break in the future:
501 // all pairs for a test position are assumed to be returned consencutively.
502 RefPairList refPairs;
503 int prevTestPos = -1;
504 while (pairSearch.findNextPair(&pair))
506 if (pair.testIndex() != prevTestPos)
508 if (prevTestPos != -1)
510 checkAllPairsFound(refPairs);
512 const int testIndex = pair.testIndex();
513 if (remainingTestPositions.count(testIndex) == 0)
516 << "Pairs for test position " << testIndex
517 << " are returned more than once.";
519 remainingTestPositions.erase(testIndex);
520 refPairs = data.testPositions_[testIndex].refPairs;
523 ExclusionsHelper::markExcludedPairs(&refPairs, testIndex, excls);
525 prevTestPos = testIndex;
528 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(),
529 sqrt(pair.distance2()));
530 RefPairList::iterator foundRefPair
531 = std::lower_bound(refPairs.begin(), refPairs.end(), searchPair);
532 if (foundRefPair == refPairs.end() || foundRefPair->refIndex != pair.refIndex())
535 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
536 << pair.testIndex() << ") is not within the cutoff.\n"
537 << " Actual: It is returned.";
539 else if (foundRefPair->bExcluded)
542 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
543 << pair.testIndex() << ") is excluded from the search.\n"
544 << " Actual: It is returned.";
546 else if (foundRefPair->bFound)
549 << "Expected: Pair (ref: " << pair.refIndex() << ", test: "
550 << pair.testIndex() << ") is returned only once.\n"
551 << " Actual: It is returned multiple times.";
555 foundRefPair->bFound = true;
556 EXPECT_REAL_EQ_TOL(foundRefPair->distance, searchPair.distance,
557 gmx::test::ulpTolerance(64))
558 << "Distance computed by the neighborhood search does not match.";
561 checkAllPairsFound(refPairs);
562 for (std::set<int>::const_iterator i = remainingTestPositions.begin();
563 i != remainingTestPositions.end(); ++i)
565 if (!data.testPositions_[*i].refPairs.empty())
568 << "Expected: Pairs would be returned for test position " << *i << ".\n"
569 << " Actual: None were returned.";
575 /********************************************************************
576 * Test data generation
579 class TrivialTestData
582 static const NeighborhoodSearchTestData &get()
584 static TrivialTestData singleton;
585 return singleton.data_;
588 TrivialTestData() : data_(12345, 1.0)
590 data_.box_[XX][XX] = 5.0;
591 data_.box_[YY][YY] = 5.0;
592 data_.box_[ZZ][ZZ] = 5.0;
593 data_.generateRandomRefPositions(10);
594 data_.generateRandomTestPositions(5);
595 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
596 data_.computeReferences(&data_.pbc_);
600 NeighborhoodSearchTestData data_;
603 class RandomBoxFullPBCData
606 static const NeighborhoodSearchTestData &get()
608 static RandomBoxFullPBCData singleton;
609 return singleton.data_;
612 RandomBoxFullPBCData() : data_(12345, 1.0)
614 data_.box_[XX][XX] = 10.0;
615 data_.box_[YY][YY] = 5.0;
616 data_.box_[ZZ][ZZ] = 7.0;
617 // TODO: Consider whether manually picking some positions would give better
619 data_.generateRandomRefPositions(1000);
620 data_.generateRandomTestPositions(100);
621 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
622 data_.computeReferences(&data_.pbc_);
626 NeighborhoodSearchTestData data_;
629 class RandomBoxXYFullPBCData
632 static const NeighborhoodSearchTestData &get()
634 static RandomBoxXYFullPBCData singleton;
635 return singleton.data_;
638 RandomBoxXYFullPBCData() : data_(54321, 1.0)
640 data_.box_[XX][XX] = 10.0;
641 data_.box_[YY][YY] = 5.0;
642 data_.box_[ZZ][ZZ] = 7.0;
643 // TODO: Consider whether manually picking some positions would give better
645 data_.generateRandomRefPositions(1000);
646 data_.generateRandomTestPositions(100);
647 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
648 data_.computeReferencesXY(&data_.pbc_);
652 NeighborhoodSearchTestData data_;
655 class RandomTriclinicFullPBCData
658 static const NeighborhoodSearchTestData &get()
660 static RandomTriclinicFullPBCData singleton;
661 return singleton.data_;
664 RandomTriclinicFullPBCData() : data_(12345, 1.0)
666 data_.box_[XX][XX] = 5.0;
667 data_.box_[YY][XX] = 2.5;
668 data_.box_[YY][YY] = 2.5*sqrt(3.0);
669 data_.box_[ZZ][XX] = 2.5;
670 data_.box_[ZZ][YY] = 2.5*sqrt(1.0/3.0);
671 data_.box_[ZZ][ZZ] = 5.0*sqrt(2.0/3.0);
672 // TODO: Consider whether manually picking some positions would give better
674 data_.generateRandomRefPositions(1000);
675 data_.generateRandomTestPositions(100);
676 set_pbc(&data_.pbc_, epbcXYZ, data_.box_);
677 data_.computeReferences(&data_.pbc_);
681 NeighborhoodSearchTestData data_;
684 class RandomBox2DPBCData
687 static const NeighborhoodSearchTestData &get()
689 static RandomBox2DPBCData singleton;
690 return singleton.data_;
693 RandomBox2DPBCData() : data_(12345, 1.0)
695 data_.box_[XX][XX] = 10.0;
696 data_.box_[YY][YY] = 7.0;
697 data_.box_[ZZ][ZZ] = 5.0;
698 // TODO: Consider whether manually picking some positions would give better
700 data_.generateRandomRefPositions(1000);
701 data_.generateRandomTestPositions(100);
702 set_pbc(&data_.pbc_, epbcXY, data_.box_);
703 data_.computeReferences(&data_.pbc_);
707 NeighborhoodSearchTestData data_;
710 /********************************************************************
714 TEST_F(NeighborhoodSearchTest, SimpleSearch)
716 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
718 nb_.setCutoff(data.cutoff_);
719 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
720 gmx::AnalysisNeighborhoodSearch search =
721 nb_.initSearch(&data.pbc_, data.refPositions());
722 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
724 testIsWithin(&search, data);
725 testMinimumDistance(&search, data);
726 testNearestPoint(&search, data);
727 testPairSearch(&search, data);
730 TEST_F(NeighborhoodSearchTest, GridSearchBox)
732 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
734 nb_.setCutoff(data.cutoff_);
735 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
736 gmx::AnalysisNeighborhoodSearch search =
737 nb_.initSearch(&data.pbc_, data.refPositions());
738 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
740 testIsWithin(&search, data);
741 testMinimumDistance(&search, data);
742 testNearestPoint(&search, data);
743 testPairSearch(&search, data);
746 TEST_F(NeighborhoodSearchTest, GridSearchTriclinic)
748 const NeighborhoodSearchTestData &data = RandomTriclinicFullPBCData::get();
750 nb_.setCutoff(data.cutoff_);
751 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
752 gmx::AnalysisNeighborhoodSearch search =
753 nb_.initSearch(&data.pbc_, data.refPositions());
754 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
756 testPairSearch(&search, data);
759 TEST_F(NeighborhoodSearchTest, GridSearch2DPBC)
761 const NeighborhoodSearchTestData &data = RandomBox2DPBCData::get();
763 nb_.setCutoff(data.cutoff_);
764 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
765 gmx::AnalysisNeighborhoodSearch search =
766 nb_.initSearch(&data.pbc_, data.refPositions());
767 // Currently, grid searching not supported with 2D PBC.
768 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
770 testIsWithin(&search, data);
771 testMinimumDistance(&search, data);
772 testNearestPoint(&search, data);
773 testPairSearch(&search, data);
776 TEST_F(NeighborhoodSearchTest, GridSearchXYBox)
778 const NeighborhoodSearchTestData &data = RandomBoxXYFullPBCData::get();
780 nb_.setCutoff(data.cutoff_);
781 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
783 gmx::AnalysisNeighborhoodSearch search =
784 nb_.initSearch(&data.pbc_, data.refPositions());
785 // Currently, grid searching not supported with XY.
786 //ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
788 testIsWithin(&search, data);
789 testMinimumDistance(&search, data);
790 testNearestPoint(&search, data);
791 testPairSearch(&search, data);
794 TEST_F(NeighborhoodSearchTest, HandlesConcurrentSearches)
796 const NeighborhoodSearchTestData &data = TrivialTestData::get();
798 nb_.setCutoff(data.cutoff_);
799 gmx::AnalysisNeighborhoodSearch search1 =
800 nb_.initSearch(&data.pbc_, data.refPositions());
801 gmx::AnalysisNeighborhoodSearch search2 =
802 nb_.initSearch(&data.pbc_, data.refPositions());
804 gmx::AnalysisNeighborhoodPairSearch pairSearch1 =
805 search1.startPairSearch(data.testPosition(0));
806 gmx::AnalysisNeighborhoodPairSearch pairSearch2 =
807 search1.startPairSearch(data.testPosition(1));
809 testPairSearch(&search2, data);
811 gmx::AnalysisNeighborhoodPair pair;
812 ASSERT_TRUE(pairSearch1.findNextPair(&pair))
813 << "Test data did not contain any pairs for position 0 (problem in the test).";
814 EXPECT_EQ(0, pair.testIndex());
816 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
817 EXPECT_TRUE(data.containsPair(0, searchPair));
820 ASSERT_TRUE(pairSearch2.findNextPair(&pair))
821 << "Test data did not contain any pairs for position 1 (problem in the test).";
822 EXPECT_EQ(1, pair.testIndex());
824 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
825 EXPECT_TRUE(data.containsPair(1, searchPair));
829 TEST_F(NeighborhoodSearchTest, HandlesSkippingPairs)
831 const NeighborhoodSearchTestData &data = TrivialTestData::get();
833 nb_.setCutoff(data.cutoff_);
834 gmx::AnalysisNeighborhoodSearch search =
835 nb_.initSearch(&data.pbc_, data.refPositions());
836 gmx::AnalysisNeighborhoodPairSearch pairSearch =
837 search.startPairSearch(data.testPositions());
838 gmx::AnalysisNeighborhoodPair pair;
839 // TODO: This test needs to be adjusted if the grid search gets optimized
840 // to loop over the test positions in cell order (first, the ordering
841 // assumption here breaks, and second, it then needs to be tested
842 // separately for simple and grid searches).
843 int currentIndex = 0;
844 while (pairSearch.findNextPair(&pair))
846 while (currentIndex < pair.testIndex())
850 EXPECT_EQ(currentIndex, pair.testIndex());
851 NeighborhoodSearchTestData::RefPair searchPair(pair.refIndex(), sqrt(pair.distance2()));
852 EXPECT_TRUE(data.containsPair(currentIndex, searchPair));
853 pairSearch.skipRemainingPairsForTestPosition();
858 TEST_F(NeighborhoodSearchTest, SimpleSearchExclusions)
860 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
862 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
863 helper.generateExclusions();
865 nb_.setCutoff(data.cutoff_);
866 nb_.setTopologyExclusions(helper.exclusions());
867 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple);
868 gmx::AnalysisNeighborhoodSearch search =
869 nb_.initSearch(&data.pbc_,
870 data.refPositions().exclusionIds(helper.refPosIds()));
871 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple, search.mode());
873 testPairSearchFull(&search, data,
874 data.testPositions().exclusionIds(helper.testPosIds()),
875 helper.exclusions());
878 TEST_F(NeighborhoodSearchTest, GridSearchExclusions)
880 const NeighborhoodSearchTestData &data = RandomBoxFullPBCData::get();
882 ExclusionsHelper helper(data.refPosCount_, data.testPositions_.size());
883 helper.generateExclusions();
885 nb_.setCutoff(data.cutoff_);
886 nb_.setTopologyExclusions(helper.exclusions());
887 nb_.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid);
888 gmx::AnalysisNeighborhoodSearch search =
889 nb_.initSearch(&data.pbc_,
890 data.refPositions().exclusionIds(helper.refPosIds()));
891 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid, search.mode());
893 testPairSearchFull(&search, data,
894 data.testPositions().exclusionIds(helper.testPosIds()),
895 helper.exclusions());