2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017, 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"
59 #include <gtest/gtest.h>
61 #include "gromacs/math/functions.h"
62 #include "gromacs/math/vec.h"
63 #include "gromacs/pbcutil/pbc.h"
64 #include "gromacs/random/threefry.h"
65 #include "gromacs/random/uniformrealdistribution.h"
66 #include "gromacs/topology/block.h"
67 #include "gromacs/utility/smalloc.h"
68 #include "gromacs/utility/stringutil.h"
70 #include "testutils/testasserts.h"
76 /********************************************************************
77 * NeighborhoodSearchTestData
80 class NeighborhoodSearchTestData
85 RefPair(int refIndex
, real distance
)
86 : refIndex(refIndex
), distance(distance
), bFound(false),
87 bExcluded(false), bIndexed(true)
91 bool operator<(const RefPair
&other
) const
93 return refIndex
< other
.refIndex
;
98 // The variables below are state variables that are only used
99 // during the actual testing after creating a copy of the reference
100 // pair list, not as part of the reference data.
101 // Simpler to have just a single structure for both purposes.
109 explicit TestPosition(const rvec x
)
110 : refMinDist(0.0), refNearestPoint(-1)
112 copy_rvec(x
, this->x
);
118 std::vector
<RefPair
> refPairs
;
121 typedef std::vector
<TestPosition
> TestPositionList
;
123 NeighborhoodSearchTestData(gmx_uint64_t seed
, real cutoff
);
125 gmx::AnalysisNeighborhoodPositions
refPositions() const
127 return gmx::AnalysisNeighborhoodPositions(refPos_
);
129 gmx::AnalysisNeighborhoodPositions
testPositions() const
131 if (testPos_
.empty())
133 testPos_
.reserve(testPositions_
.size());
134 for (size_t i
= 0; i
< testPositions_
.size(); ++i
)
136 testPos_
.emplace_back(testPositions_
[i
].x
);
139 return gmx::AnalysisNeighborhoodPositions(testPos_
);
141 gmx::AnalysisNeighborhoodPositions
testPosition(int index
) const
143 return testPositions().selectSingleFromArray(index
);
146 void addTestPosition(const rvec x
)
148 GMX_RELEASE_ASSERT(testPos_
.empty(),
149 "Cannot add positions after testPositions() call");
150 testPositions_
.emplace_back(x
);
152 gmx::RVec
generateRandomPosition();
153 std::vector
<int> generateIndex(int count
, gmx_uint64_t seed
) const;
154 void generateRandomRefPositions(int count
);
155 void generateRandomTestPositions(int count
);
156 void useRefPositionsAsTestPositions();
157 void computeReferences(t_pbc
*pbc
)
159 computeReferencesInternal(pbc
, false);
161 void computeReferencesXY(t_pbc
*pbc
)
163 computeReferencesInternal(pbc
, true);
166 bool containsPair(int testIndex
, const RefPair
&pair
) const
168 const std::vector
<RefPair
> &refPairs
= testPositions_
[testIndex
].refPairs
;
169 std::vector
<RefPair
>::const_iterator foundRefPair
170 = std::lower_bound(refPairs
.begin(), refPairs
.end(), pair
);
171 if (foundRefPair
== refPairs
.end() || foundRefPair
->refIndex
!= pair
.refIndex
)
178 // Return a tolerance that accounts for the magnitudes of the coordinates
179 // when doing subtractions, so that we set the ULP tolerance relative to the
180 // coordinate values rather than their difference.
181 // i.e., 10.0-9.9999999 will achieve a few ULP accuracy relative
182 // to 10.0, but a much larger error relative to the difference.
183 gmx::test::FloatingPointTolerance
relativeTolerance() const
185 real magnitude
= std::max(box_
[XX
][XX
], std::max(box_
[YY
][YY
], box_
[ZZ
][ZZ
]));
186 return gmx::test::relativeToleranceAsUlp(magnitude
, 4);
189 gmx::DefaultRandomEngine rng_
;
194 std::vector
<gmx::RVec
> refPos_
;
195 TestPositionList testPositions_
;
198 void computeReferencesInternal(t_pbc
*pbc
, bool bXY
);
200 mutable std::vector
<gmx::RVec
> testPos_
;
203 //! Shorthand for a collection of reference pairs.
204 typedef std::vector
<NeighborhoodSearchTestData::RefPair
> RefPairList
;
206 NeighborhoodSearchTestData::NeighborhoodSearchTestData(gmx_uint64_t seed
, real cutoff
)
207 : rng_(seed
), cutoff_(cutoff
), refPosCount_(0)
210 set_pbc(&pbc_
, epbcNONE
, box_
);
213 gmx::RVec
NeighborhoodSearchTestData::generateRandomPosition()
215 gmx::UniformRealDistribution
<real
> dist
;
221 // Add a small displacement to allow positions outside the box
222 x
[XX
] += 0.2 * dist(rng_
) - 0.1;
223 x
[YY
] += 0.2 * dist(rng_
) - 0.1;
224 x
[ZZ
] += 0.2 * dist(rng_
) - 0.1;
228 std::vector
<int> NeighborhoodSearchTestData::generateIndex(int count
, gmx_uint64_t seed
) const
230 gmx::DefaultRandomEngine
rngIndex(seed
);
231 gmx::UniformRealDistribution
<real
> dist
;
232 std::vector
<int> result
;
234 for (int i
= 0; i
< count
; ++i
)
236 if (dist(rngIndex
) > 0.5)
244 void NeighborhoodSearchTestData::generateRandomRefPositions(int count
)
246 refPosCount_
= count
;
247 refPos_
.reserve(count
);
248 for (int i
= 0; i
< count
; ++i
)
250 refPos_
.push_back(generateRandomPosition());
254 void NeighborhoodSearchTestData::generateRandomTestPositions(int count
)
256 testPositions_
.reserve(count
);
257 for (int i
= 0; i
< count
; ++i
)
259 addTestPosition(generateRandomPosition());
263 void NeighborhoodSearchTestData::useRefPositionsAsTestPositions()
265 testPositions_
.reserve(refPosCount_
);
266 for (const auto &refPos
: refPos_
)
268 addTestPosition(refPos
);
272 void NeighborhoodSearchTestData::computeReferencesInternal(t_pbc
*pbc
, bool bXY
)
274 real cutoff
= cutoff_
;
277 cutoff
= std::numeric_limits
<real
>::max();
279 for (TestPosition
&testPos
: testPositions_
)
281 testPos
.refMinDist
= cutoff
;
282 testPos
.refNearestPoint
= -1;
283 testPos
.refPairs
.clear();
284 for (int j
= 0; j
< refPosCount_
; ++j
)
289 pbc_dx(pbc
, testPos
.x
, refPos_
[j
], dx
);
293 rvec_sub(testPos
.x
, refPos_
[j
], dx
);
295 // TODO: This may not work intuitively for 2D with the third box
296 // vector not parallel to the Z axis, but neither does the actual
297 // neighborhood search.
299 !bXY
? norm(dx
) : std::hypot(dx
[XX
], dx
[YY
]);
300 if (dist
< testPos
.refMinDist
)
302 testPos
.refMinDist
= dist
;
303 testPos
.refNearestPoint
= j
;
305 if (dist
> 0 && dist
<= cutoff
)
307 RefPair
pair(j
, dist
);
308 GMX_RELEASE_ASSERT(testPos
.refPairs
.empty() || testPos
.refPairs
.back() < pair
,
309 "Reference pairs should be generated in sorted order");
310 testPos
.refPairs
.push_back(pair
);
316 /********************************************************************
320 class ExclusionsHelper
323 static void markExcludedPairs(RefPairList
*refPairs
, int testIndex
,
324 const t_blocka
*excls
);
326 ExclusionsHelper(int refPosCount
, int testPosCount
);
328 void generateExclusions();
330 const t_blocka
*exclusions() const { return &excls_
; }
332 gmx::ConstArrayRef
<int> refPosIds() const
334 return gmx::constArrayRefFromVector
<int>(exclusionIds_
.begin(),
335 exclusionIds_
.begin() + refPosCount_
);
337 gmx::ConstArrayRef
<int> testPosIds() const
339 return gmx::constArrayRefFromVector
<int>(exclusionIds_
.begin(),
340 exclusionIds_
.begin() + testPosCount_
);
346 std::vector
<int> exclusionIds_
;
347 std::vector
<int> exclsIndex_
;
348 std::vector
<int> exclsAtoms_
;
353 void ExclusionsHelper::markExcludedPairs(RefPairList
*refPairs
, int testIndex
,
354 const t_blocka
*excls
)
357 for (int i
= excls
->index
[testIndex
]; i
< excls
->index
[testIndex
+ 1]; ++i
)
359 const int excludedIndex
= excls
->a
[i
];
360 NeighborhoodSearchTestData::RefPair
searchPair(excludedIndex
, 0.0);
361 RefPairList::iterator excludedRefPair
362 = std::lower_bound(refPairs
->begin(), refPairs
->end(), searchPair
);
363 if (excludedRefPair
!= refPairs
->end()
364 && excludedRefPair
->refIndex
== excludedIndex
)
366 excludedRefPair
->bFound
= true;
367 excludedRefPair
->bExcluded
= true;
373 ExclusionsHelper::ExclusionsHelper(int refPosCount
, int testPosCount
)
374 : refPosCount_(refPosCount
), testPosCount_(testPosCount
)
376 // Generate an array of 0, 1, 2, ...
377 // TODO: Make the tests work also with non-trivial exclusion IDs,
379 exclusionIds_
.resize(std::max(refPosCount
, testPosCount
), 1);
380 exclusionIds_
[0] = 0;
381 std::partial_sum(exclusionIds_
.begin(), exclusionIds_
.end(),
382 exclusionIds_
.begin());
385 excls_
.index
= nullptr;
388 excls_
.nalloc_index
= 0;
392 void ExclusionsHelper::generateExclusions()
394 // TODO: Consider a better set of test data, where the density of the
395 // particles would be higher, or where the exclusions would not be random,
396 // to make a higher percentage of the exclusions to actually be within the
398 exclsIndex_
.reserve(testPosCount_
+ 1);
399 exclsAtoms_
.reserve(testPosCount_
* 20);
400 exclsIndex_
.push_back(0);
401 for (int i
= 0; i
< testPosCount_
; ++i
)
403 for (int j
= 0; j
< 20; ++j
)
405 exclsAtoms_
.push_back(i
+ j
*3);
407 exclsIndex_
.push_back(exclsAtoms_
.size());
409 excls_
.nr
= exclsIndex_
.size();
410 excls_
.index
= exclsIndex_
.data();
411 excls_
.nra
= exclsAtoms_
.size();
412 excls_
.a
= exclsAtoms_
.data();
415 /********************************************************************
416 * NeighborhoodSearchTest
419 class NeighborhoodSearchTest
: public ::testing::Test
422 void testIsWithin(gmx::AnalysisNeighborhoodSearch
*search
,
423 const NeighborhoodSearchTestData
&data
);
424 void testMinimumDistance(gmx::AnalysisNeighborhoodSearch
*search
,
425 const NeighborhoodSearchTestData
&data
);
426 void testNearestPoint(gmx::AnalysisNeighborhoodSearch
*search
,
427 const NeighborhoodSearchTestData
&data
);
428 void testPairSearch(gmx::AnalysisNeighborhoodSearch
*search
,
429 const NeighborhoodSearchTestData
&data
);
430 void testPairSearchIndexed(gmx::AnalysisNeighborhood
*nb
,
431 const NeighborhoodSearchTestData
&data
,
433 void testPairSearchFull(gmx::AnalysisNeighborhoodSearch
*search
,
434 const NeighborhoodSearchTestData
&data
,
435 const gmx::AnalysisNeighborhoodPositions
&pos
,
436 const t_blocka
*excls
,
437 const gmx::ConstArrayRef
<int> &refIndices
,
438 const gmx::ConstArrayRef
<int> &testIndices
,
441 gmx::AnalysisNeighborhood nb_
;
444 void NeighborhoodSearchTest::testIsWithin(
445 gmx::AnalysisNeighborhoodSearch
*search
,
446 const NeighborhoodSearchTestData
&data
)
448 NeighborhoodSearchTestData::TestPositionList::const_iterator i
;
449 for (i
= data
.testPositions_
.begin(); i
!= data
.testPositions_
.end(); ++i
)
451 const bool bWithin
= (i
->refMinDist
<= data
.cutoff_
);
452 EXPECT_EQ(bWithin
, search
->isWithin(i
->x
))
453 << "Distance is " << i
->refMinDist
;
457 void NeighborhoodSearchTest::testMinimumDistance(
458 gmx::AnalysisNeighborhoodSearch
*search
,
459 const NeighborhoodSearchTestData
&data
)
461 NeighborhoodSearchTestData::TestPositionList::const_iterator i
;
463 for (i
= data
.testPositions_
.begin(); i
!= data
.testPositions_
.end(); ++i
)
465 const real refDist
= i
->refMinDist
;
466 EXPECT_REAL_EQ_TOL(refDist
, search
->minimumDistance(i
->x
), data
.relativeTolerance());
470 void NeighborhoodSearchTest::testNearestPoint(
471 gmx::AnalysisNeighborhoodSearch
*search
,
472 const NeighborhoodSearchTestData
&data
)
474 NeighborhoodSearchTestData::TestPositionList::const_iterator i
;
475 for (i
= data
.testPositions_
.begin(); i
!= data
.testPositions_
.end(); ++i
)
477 const gmx::AnalysisNeighborhoodPair pair
= search
->nearestPoint(i
->x
);
480 EXPECT_EQ(i
->refNearestPoint
, pair
.refIndex());
481 EXPECT_EQ(0, pair
.testIndex());
482 EXPECT_REAL_EQ_TOL(i
->refMinDist
, std::sqrt(pair
.distance2()), data
.relativeTolerance());
486 EXPECT_EQ(i
->refNearestPoint
, -1);
491 //! Helper function for formatting test failure messages.
492 std::string
formatVector(const rvec x
)
494 return gmx::formatString("[%.3f, %.3f, %.3f]", x
[XX
], x
[YY
], x
[ZZ
]);
498 * Helper function to check that all expected pairs were found.
500 void checkAllPairsFound(const RefPairList
&refPairs
,
501 const std::vector
<gmx::RVec
> &refPos
,
502 int testPosIndex
, const rvec testPos
)
504 // This could be elegantly expressed with Google Mock matchers, but that
505 // has a significant effect on the runtime of the tests...
507 RefPairList::const_iterator first
;
508 for (RefPairList::const_iterator i
= refPairs
.begin(); i
!= refPairs
.end(); ++i
)
519 << "Some pairs (" << count
<< "/" << refPairs
.size() << ") "
520 << "within the cutoff were not found. First pair:\n"
521 << " Ref: " << first
->refIndex
<< " at "
522 << formatVector(refPos
[first
->refIndex
]) << "\n"
523 << "Test: " << testPosIndex
<< " at " << formatVector(testPos
) << "\n"
524 << "Dist: " << first
->distance
;
528 void NeighborhoodSearchTest::testPairSearch(
529 gmx::AnalysisNeighborhoodSearch
*search
,
530 const NeighborhoodSearchTestData
&data
)
532 testPairSearchFull(search
, data
, data
.testPositions(), nullptr,
533 gmx::EmptyArrayRef(), gmx::EmptyArrayRef(), false);
536 void NeighborhoodSearchTest::testPairSearchIndexed(
537 gmx::AnalysisNeighborhood
*nb
,
538 const NeighborhoodSearchTestData
&data
,
541 std::vector
<int> refIndices(data
.generateIndex(data
.refPos_
.size(), seed
++));
542 std::vector
<int> testIndices(data
.generateIndex(data
.testPositions_
.size(), seed
++));
543 gmx::AnalysisNeighborhoodSearch search
=
544 nb
->initSearch(&data
.pbc_
,
545 data
.refPositions().indexed(refIndices
));
546 testPairSearchFull(&search
, data
, data
.testPositions(), nullptr,
547 refIndices
, testIndices
, false);
550 void NeighborhoodSearchTest::testPairSearchFull(
551 gmx::AnalysisNeighborhoodSearch
*search
,
552 const NeighborhoodSearchTestData
&data
,
553 const gmx::AnalysisNeighborhoodPositions
&pos
,
554 const t_blocka
*excls
,
555 const gmx::ConstArrayRef
<int> &refIndices
,
556 const gmx::ConstArrayRef
<int> &testIndices
,
559 std::map
<int, RefPairList
> refPairs
;
560 // TODO: Some parts of this code do not work properly if pos does not
561 // initially contain all the test positions.
562 if (testIndices
.empty())
564 for (size_t i
= 0; i
< data
.testPositions_
.size(); ++i
)
566 refPairs
[i
] = data
.testPositions_
[i
].refPairs
;
571 for (int index
: testIndices
)
573 refPairs
[index
] = data
.testPositions_
[index
].refPairs
;
576 if (excls
!= nullptr)
578 GMX_RELEASE_ASSERT(!selfPairs
, "Self-pairs testing not implemented with exclusions");
579 for (auto &entry
: refPairs
)
581 const int testIndex
= entry
.first
;
582 ExclusionsHelper::markExcludedPairs(&entry
.second
, testIndex
, excls
);
585 if (!refIndices
.empty())
587 GMX_RELEASE_ASSERT(!selfPairs
, "Self-pairs testing not implemented with indexing");
588 for (auto &entry
: refPairs
)
590 for (auto &refPair
: entry
.second
)
592 refPair
.bIndexed
= false;
594 for (int index
: refIndices
)
596 NeighborhoodSearchTestData::RefPair
searchPair(index
, 0.0);
597 auto refPair
= std::lower_bound(entry
.second
.begin(), entry
.second
.end(), searchPair
);
598 if (refPair
!= entry
.second
.end() && refPair
->refIndex
== index
)
600 refPair
->bIndexed
= true;
603 for (auto &refPair
: entry
.second
)
605 if (!refPair
.bIndexed
)
607 refPair
.bFound
= true;
613 gmx::AnalysisNeighborhoodPositions
posCopy(pos
);
614 if (!testIndices
.empty())
616 posCopy
.indexed(testIndices
);
618 gmx::AnalysisNeighborhoodPairSearch pairSearch
620 ? search
->startSelfPairSearch()
621 : search
->startPairSearch(posCopy
);
622 gmx::AnalysisNeighborhoodPair pair
;
623 while (pairSearch
.findNextPair(&pair
))
625 const int testIndex
=
626 (testIndices
.empty() ? pair
.testIndex() : testIndices
[pair
.testIndex()]);
628 (refIndices
.empty() ? pair
.refIndex() : refIndices
[pair
.refIndex()]);
630 if (refPairs
.count(testIndex
) == 0)
633 << "Expected: No pairs are returned for test position " << testIndex
<< ".\n"
634 << " Actual: Pair with ref " << refIndex
<< " is returned.";
637 NeighborhoodSearchTestData::RefPair
searchPair(refIndex
,
638 std::sqrt(pair
.distance2()));
639 const auto foundRefPair
640 = std::lower_bound(refPairs
[testIndex
].begin(), refPairs
[testIndex
].end(),
642 if (foundRefPair
== refPairs
[testIndex
].end() || foundRefPair
->refIndex
!= refIndex
)
645 << "Expected: Pair (ref: " << refIndex
<< ", test: " << testIndex
646 << ") is not within the cutoff.\n"
647 << " Actual: It is returned.";
649 else if (foundRefPair
->bExcluded
)
652 << "Expected: Pair (ref: " << refIndex
<< ", test: " << testIndex
653 << ") is excluded from the search.\n"
654 << " Actual: It is returned.";
656 else if (!foundRefPair
->bIndexed
)
659 << "Expected: Pair (ref: " << refIndex
<< ", test: " << testIndex
660 << ") is not part of the indexed set.\n"
661 << " Actual: It is returned.";
663 else if (foundRefPair
->bFound
)
666 << "Expected: Pair (ref: " << refIndex
<< ", test: " << testIndex
667 << ") is returned only once.\n"
668 << " Actual: It is returned multiple times.";
673 foundRefPair
->bFound
= true;
675 EXPECT_REAL_EQ_TOL(foundRefPair
->distance
, searchPair
.distance
, data
.relativeTolerance())
676 << "Distance computed by the neighborhood search does not match.";
679 searchPair
= NeighborhoodSearchTestData::RefPair(testIndex
, 0.0);
680 const auto otherRefPair
681 = std::lower_bound(refPairs
[refIndex
].begin(), refPairs
[refIndex
].end(),
683 GMX_RELEASE_ASSERT(otherRefPair
!= refPairs
[refIndex
].end(),
684 "Precomputed reference data is not symmetric");
685 otherRefPair
->bFound
= true;
690 for (auto &entry
: refPairs
)
692 const int testIndex
= entry
.first
;
693 checkAllPairsFound(entry
.second
, data
.refPos_
, testIndex
,
694 data
.testPositions_
[testIndex
].x
);
698 /********************************************************************
699 * Test data generation
702 class TrivialTestData
705 static const NeighborhoodSearchTestData
&get()
707 static TrivialTestData singleton
;
708 return singleton
.data_
;
711 TrivialTestData() : data_(12345, 1.0)
713 // Make the box so small we are virtually guaranteed to have
714 // several neighbors for the five test positions
715 data_
.box_
[XX
][XX
] = 3.0;
716 data_
.box_
[YY
][YY
] = 3.0;
717 data_
.box_
[ZZ
][ZZ
] = 3.0;
718 data_
.generateRandomRefPositions(10);
719 data_
.generateRandomTestPositions(5);
720 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
721 data_
.computeReferences(&data_
.pbc_
);
725 NeighborhoodSearchTestData data_
;
728 class TrivialSelfPairsTestData
731 static const NeighborhoodSearchTestData
&get()
733 static TrivialSelfPairsTestData singleton
;
734 return singleton
.data_
;
737 TrivialSelfPairsTestData() : data_(12345, 1.0)
739 data_
.box_
[XX
][XX
] = 3.0;
740 data_
.box_
[YY
][YY
] = 3.0;
741 data_
.box_
[ZZ
][ZZ
] = 3.0;
742 data_
.generateRandomRefPositions(20);
743 data_
.useRefPositionsAsTestPositions();
744 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
745 data_
.computeReferences(&data_
.pbc_
);
749 NeighborhoodSearchTestData data_
;
752 class TrivialNoPBCTestData
755 static const NeighborhoodSearchTestData
&get()
757 static TrivialNoPBCTestData singleton
;
758 return singleton
.data_
;
761 TrivialNoPBCTestData() : data_(12345, 1.0)
763 data_
.generateRandomRefPositions(10);
764 data_
.generateRandomTestPositions(5);
765 data_
.computeReferences(nullptr);
769 NeighborhoodSearchTestData data_
;
772 class RandomBoxFullPBCData
775 static const NeighborhoodSearchTestData
&get()
777 static RandomBoxFullPBCData singleton
;
778 return singleton
.data_
;
781 RandomBoxFullPBCData() : data_(12345, 1.0)
783 data_
.box_
[XX
][XX
] = 10.0;
784 data_
.box_
[YY
][YY
] = 5.0;
785 data_
.box_
[ZZ
][ZZ
] = 7.0;
786 // TODO: Consider whether manually picking some positions would give better
788 data_
.generateRandomRefPositions(1000);
789 data_
.generateRandomTestPositions(100);
790 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
791 data_
.computeReferences(&data_
.pbc_
);
795 NeighborhoodSearchTestData data_
;
798 class RandomBoxSelfPairsData
801 static const NeighborhoodSearchTestData
&get()
803 static RandomBoxSelfPairsData singleton
;
804 return singleton
.data_
;
807 RandomBoxSelfPairsData() : data_(12345, 1.0)
809 data_
.box_
[XX
][XX
] = 10.0;
810 data_
.box_
[YY
][YY
] = 5.0;
811 data_
.box_
[ZZ
][ZZ
] = 7.0;
812 data_
.generateRandomRefPositions(1000);
813 data_
.useRefPositionsAsTestPositions();
814 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
815 data_
.computeReferences(&data_
.pbc_
);
819 NeighborhoodSearchTestData data_
;
822 class RandomBoxXYFullPBCData
825 static const NeighborhoodSearchTestData
&get()
827 static RandomBoxXYFullPBCData singleton
;
828 return singleton
.data_
;
831 RandomBoxXYFullPBCData() : data_(54321, 1.0)
833 data_
.box_
[XX
][XX
] = 10.0;
834 data_
.box_
[YY
][YY
] = 5.0;
835 data_
.box_
[ZZ
][ZZ
] = 7.0;
836 // TODO: Consider whether manually picking some positions would give better
838 data_
.generateRandomRefPositions(1000);
839 data_
.generateRandomTestPositions(100);
840 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
841 data_
.computeReferencesXY(&data_
.pbc_
);
845 NeighborhoodSearchTestData data_
;
848 class RandomTriclinicFullPBCData
851 static const NeighborhoodSearchTestData
&get()
853 static RandomTriclinicFullPBCData singleton
;
854 return singleton
.data_
;
857 RandomTriclinicFullPBCData() : data_(12345, 1.0)
859 data_
.box_
[XX
][XX
] = 5.0;
860 data_
.box_
[YY
][XX
] = 2.5;
861 data_
.box_
[YY
][YY
] = 2.5*std::sqrt(3.0);
862 data_
.box_
[ZZ
][XX
] = 2.5;
863 data_
.box_
[ZZ
][YY
] = 2.5*std::sqrt(1.0/3.0);
864 data_
.box_
[ZZ
][ZZ
] = 5.0*std::sqrt(2.0/3.0);
865 // TODO: Consider whether manually picking some positions would give better
867 data_
.generateRandomRefPositions(1000);
868 data_
.generateRandomTestPositions(100);
869 set_pbc(&data_
.pbc_
, epbcXYZ
, data_
.box_
);
870 data_
.computeReferences(&data_
.pbc_
);
874 NeighborhoodSearchTestData data_
;
877 class RandomBox2DPBCData
880 static const NeighborhoodSearchTestData
&get()
882 static RandomBox2DPBCData singleton
;
883 return singleton
.data_
;
886 RandomBox2DPBCData() : data_(12345, 1.0)
888 data_
.box_
[XX
][XX
] = 10.0;
889 data_
.box_
[YY
][YY
] = 7.0;
890 data_
.box_
[ZZ
][ZZ
] = 5.0;
891 // TODO: Consider whether manually picking some positions would give better
893 data_
.generateRandomRefPositions(1000);
894 data_
.generateRandomTestPositions(100);
895 set_pbc(&data_
.pbc_
, epbcXY
, data_
.box_
);
896 data_
.computeReferences(&data_
.pbc_
);
900 NeighborhoodSearchTestData data_
;
903 class RandomBoxNoPBCData
906 static const NeighborhoodSearchTestData
&get()
908 static RandomBoxNoPBCData singleton
;
909 return singleton
.data_
;
912 RandomBoxNoPBCData() : data_(12345, 1.0)
914 data_
.box_
[XX
][XX
] = 10.0;
915 data_
.box_
[YY
][YY
] = 5.0;
916 data_
.box_
[ZZ
][ZZ
] = 7.0;
917 // TODO: Consider whether manually picking some positions would give better
919 data_
.generateRandomRefPositions(1000);
920 data_
.generateRandomTestPositions(100);
921 set_pbc(&data_
.pbc_
, epbcNONE
, data_
.box_
);
922 data_
.computeReferences(nullptr);
926 NeighborhoodSearchTestData data_
;
929 /********************************************************************
933 TEST_F(NeighborhoodSearchTest
, SimpleSearch
)
935 const NeighborhoodSearchTestData
&data
= RandomBoxFullPBCData::get();
937 nb_
.setCutoff(data
.cutoff_
);
938 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple
);
939 gmx::AnalysisNeighborhoodSearch search
=
940 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
941 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
943 testIsWithin(&search
, data
);
944 testMinimumDistance(&search
, data
);
945 testNearestPoint(&search
, data
);
946 testPairSearch(&search
, data
);
949 testPairSearchIndexed(&nb_
, data
, 123);
952 TEST_F(NeighborhoodSearchTest
, SimpleSearchXY
)
954 const NeighborhoodSearchTestData
&data
= RandomBoxXYFullPBCData::get();
956 nb_
.setCutoff(data
.cutoff_
);
957 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple
);
959 gmx::AnalysisNeighborhoodSearch search
=
960 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
961 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
963 testIsWithin(&search
, data
);
964 testMinimumDistance(&search
, data
);
965 testNearestPoint(&search
, data
);
966 testPairSearch(&search
, data
);
969 TEST_F(NeighborhoodSearchTest
, GridSearchBox
)
971 const NeighborhoodSearchTestData
&data
= RandomBoxFullPBCData::get();
973 nb_
.setCutoff(data
.cutoff_
);
974 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
975 gmx::AnalysisNeighborhoodSearch search
=
976 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
977 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
979 testIsWithin(&search
, data
);
980 testMinimumDistance(&search
, data
);
981 testNearestPoint(&search
, data
);
982 testPairSearch(&search
, data
);
985 testPairSearchIndexed(&nb_
, data
, 456);
988 TEST_F(NeighborhoodSearchTest
, GridSearchTriclinic
)
990 const NeighborhoodSearchTestData
&data
= RandomTriclinicFullPBCData::get();
992 nb_
.setCutoff(data
.cutoff_
);
993 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
994 gmx::AnalysisNeighborhoodSearch search
=
995 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
996 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
998 testPairSearch(&search
, data
);
1001 TEST_F(NeighborhoodSearchTest
, GridSearch2DPBC
)
1003 const NeighborhoodSearchTestData
&data
= RandomBox2DPBCData::get();
1005 nb_
.setCutoff(data
.cutoff_
);
1006 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
1007 gmx::AnalysisNeighborhoodSearch search
=
1008 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1009 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
1011 testIsWithin(&search
, data
);
1012 testMinimumDistance(&search
, data
);
1013 testNearestPoint(&search
, data
);
1014 testPairSearch(&search
, data
);
1017 TEST_F(NeighborhoodSearchTest
, GridSearchNoPBC
)
1019 const NeighborhoodSearchTestData
&data
= RandomBoxNoPBCData::get();
1021 nb_
.setCutoff(data
.cutoff_
);
1022 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
1023 gmx::AnalysisNeighborhoodSearch search
=
1024 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1025 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
1027 testPairSearch(&search
, data
);
1030 TEST_F(NeighborhoodSearchTest
, GridSearchXYBox
)
1032 const NeighborhoodSearchTestData
&data
= RandomBoxXYFullPBCData::get();
1034 nb_
.setCutoff(data
.cutoff_
);
1035 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
1036 nb_
.setXYMode(true);
1037 gmx::AnalysisNeighborhoodSearch search
=
1038 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1039 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
1041 testIsWithin(&search
, data
);
1042 testMinimumDistance(&search
, data
);
1043 testNearestPoint(&search
, data
);
1044 testPairSearch(&search
, data
);
1047 TEST_F(NeighborhoodSearchTest
, SimpleSelfPairsSearch
)
1049 const NeighborhoodSearchTestData
&data
= TrivialSelfPairsTestData::get();
1051 nb_
.setCutoff(data
.cutoff_
);
1052 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple
);
1053 gmx::AnalysisNeighborhoodSearch search
=
1054 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1055 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
1057 testPairSearchFull(&search
, data
, data
.testPositions(), nullptr,
1058 gmx::EmptyArrayRef(), gmx::EmptyArrayRef(), true);
1061 TEST_F(NeighborhoodSearchTest
, GridSelfPairsSearch
)
1063 const NeighborhoodSearchTestData
&data
= RandomBoxSelfPairsData::get();
1065 nb_
.setCutoff(data
.cutoff_
);
1066 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
1067 gmx::AnalysisNeighborhoodSearch search
=
1068 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1069 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
1071 testPairSearchFull(&search
, data
, data
.testPositions(), nullptr,
1072 gmx::EmptyArrayRef(), gmx::EmptyArrayRef(), true);
1075 TEST_F(NeighborhoodSearchTest
, HandlesConcurrentSearches
)
1077 const NeighborhoodSearchTestData
&data
= TrivialTestData::get();
1079 nb_
.setCutoff(data
.cutoff_
);
1080 gmx::AnalysisNeighborhoodSearch search1
=
1081 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1082 gmx::AnalysisNeighborhoodSearch search2
=
1083 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1085 // These checks are fragile, and unfortunately depend on the random
1086 // engine used to create the test positions. There is no particular reason
1087 // why exactly particles 0 & 2 should have neighbors, but in this case they do.
1088 gmx::AnalysisNeighborhoodPairSearch pairSearch1
=
1089 search1
.startPairSearch(data
.testPosition(0));
1090 gmx::AnalysisNeighborhoodPairSearch pairSearch2
=
1091 search1
.startPairSearch(data
.testPosition(2));
1093 testPairSearch(&search2
, data
);
1095 gmx::AnalysisNeighborhoodPair pair
;
1096 ASSERT_TRUE(pairSearch1
.findNextPair(&pair
))
1097 << "Test data did not contain any pairs for position 0 (problem in the test).";
1098 EXPECT_EQ(0, pair
.testIndex());
1100 NeighborhoodSearchTestData::RefPair
searchPair(pair
.refIndex(), std::sqrt(pair
.distance2()));
1101 EXPECT_TRUE(data
.containsPair(0, searchPair
));
1104 ASSERT_TRUE(pairSearch2
.findNextPair(&pair
))
1105 << "Test data did not contain any pairs for position 2 (problem in the test).";
1106 EXPECT_EQ(2, pair
.testIndex());
1108 NeighborhoodSearchTestData::RefPair
searchPair(pair
.refIndex(), std::sqrt(pair
.distance2()));
1109 EXPECT_TRUE(data
.containsPair(2, searchPair
));
1113 TEST_F(NeighborhoodSearchTest
, HandlesNoPBC
)
1115 const NeighborhoodSearchTestData
&data
= TrivialNoPBCTestData::get();
1117 nb_
.setCutoff(data
.cutoff_
);
1118 gmx::AnalysisNeighborhoodSearch search
=
1119 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1120 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
1122 testIsWithin(&search
, data
);
1123 testMinimumDistance(&search
, data
);
1124 testNearestPoint(&search
, data
);
1125 testPairSearch(&search
, data
);
1128 TEST_F(NeighborhoodSearchTest
, HandlesNullPBC
)
1130 const NeighborhoodSearchTestData
&data
= TrivialNoPBCTestData::get();
1132 nb_
.setCutoff(data
.cutoff_
);
1133 gmx::AnalysisNeighborhoodSearch search
=
1134 nb_
.initSearch(nullptr, data
.refPositions());
1135 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
1137 testIsWithin(&search
, data
);
1138 testMinimumDistance(&search
, data
);
1139 testNearestPoint(&search
, data
);
1140 testPairSearch(&search
, data
);
1143 TEST_F(NeighborhoodSearchTest
, HandlesSkippingPairs
)
1145 const NeighborhoodSearchTestData
&data
= TrivialTestData::get();
1147 nb_
.setCutoff(data
.cutoff_
);
1148 gmx::AnalysisNeighborhoodSearch search
=
1149 nb_
.initSearch(&data
.pbc_
, data
.refPositions());
1150 gmx::AnalysisNeighborhoodPairSearch pairSearch
=
1151 search
.startPairSearch(data
.testPositions());
1152 gmx::AnalysisNeighborhoodPair pair
;
1153 // TODO: This test needs to be adjusted if the grid search gets optimized
1154 // to loop over the test positions in cell order (first, the ordering
1155 // assumption here breaks, and second, it then needs to be tested
1156 // separately for simple and grid searches).
1157 int currentIndex
= 0;
1158 while (pairSearch
.findNextPair(&pair
))
1160 while (currentIndex
< pair
.testIndex())
1164 EXPECT_EQ(currentIndex
, pair
.testIndex());
1165 NeighborhoodSearchTestData::RefPair
searchPair(pair
.refIndex(), std::sqrt(pair
.distance2()));
1166 EXPECT_TRUE(data
.containsPair(currentIndex
, searchPair
));
1167 pairSearch
.skipRemainingPairsForTestPosition();
1172 TEST_F(NeighborhoodSearchTest
, SimpleSearchExclusions
)
1174 const NeighborhoodSearchTestData
&data
= RandomBoxFullPBCData::get();
1176 ExclusionsHelper
helper(data
.refPosCount_
, data
.testPositions_
.size());
1177 helper
.generateExclusions();
1179 nb_
.setCutoff(data
.cutoff_
);
1180 nb_
.setTopologyExclusions(helper
.exclusions());
1181 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Simple
);
1182 gmx::AnalysisNeighborhoodSearch search
=
1183 nb_
.initSearch(&data
.pbc_
,
1184 data
.refPositions().exclusionIds(helper
.refPosIds()));
1185 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Simple
, search
.mode());
1187 testPairSearchFull(&search
, data
,
1188 data
.testPositions().exclusionIds(helper
.testPosIds()),
1189 helper
.exclusions(), gmx::EmptyArrayRef(),
1190 gmx::EmptyArrayRef(), false);
1193 TEST_F(NeighborhoodSearchTest
, GridSearchExclusions
)
1195 const NeighborhoodSearchTestData
&data
= RandomBoxFullPBCData::get();
1197 ExclusionsHelper
helper(data
.refPosCount_
, data
.testPositions_
.size());
1198 helper
.generateExclusions();
1200 nb_
.setCutoff(data
.cutoff_
);
1201 nb_
.setTopologyExclusions(helper
.exclusions());
1202 nb_
.setMode(gmx::AnalysisNeighborhood::eSearchMode_Grid
);
1203 gmx::AnalysisNeighborhoodSearch search
=
1204 nb_
.initSearch(&data
.pbc_
,
1205 data
.refPositions().exclusionIds(helper
.refPosIds()));
1206 ASSERT_EQ(gmx::AnalysisNeighborhood::eSearchMode_Grid
, search
.mode());
1208 testPairSearchFull(&search
, data
,
1209 data
.testPositions().exclusionIds(helper
.testPosIds()),
1210 helper
.exclusions(), gmx::EmptyArrayRef(),
1211 gmx::EmptyArrayRef(), false);