18#include "gtest/gtest.h"
21template <
unsigned Dim>
22using Mesh_t = ippl::UniformCartesian<double, Dim>;
24template <
unsigned Dim>
27template <
typename T,
unsigned Dim,
class... ViewArgs>
30template <
typename T,
unsigned Dim>
33template <
typename T,
unsigned Dim,
class... ViewArgs>
37template <
typename T,
unsigned Dim>
61using TestMesh_t = ippl::UniformCartesian<TestT, TestDim>;
62using TestPLayout_t = ippl::ParticleSpatialLayout<TestT, TestDim, TestMesh_t>;
71struct TestBunch :
public ippl::ParticleBase<TestPLayout_t> {
72 using Base = ippl::ParticleBase<TestPLayout_t>;
76 typename Base::particle_position_type
P;
79 ippl::ParticleAttrib<bin_index_type>
Bin;
82 this->addAttribute(
P);
83 this->addAttribute(
Bin);
104 char** argv =
nullptr;
105 ippl::initialize(argc, argv);
111 std::unique_ptr<TestMesh_t>
mesh;
112 std::unique_ptr<TestFL_t>
fl;
123 ippl::Vector<TestT, TestDim> hr;
124 ippl::Vector<TestT, TestDim> origin;
125 ippl::NDIndex<TestDim> domain;
126 std::array<bool, TestDim> decomp;
129 for (
unsigned d = 0; d <
TestDim; ++d) {
135 mesh = std::make_unique<TestMesh_t>(domain, hr, origin);
136 fl = std::make_unique<TestFL_t>(MPI_COMM_WORLD, domain, decomp,
true);
139 bunch->setParticleBC(ippl::BC::PERIODIC);
154 std::mt19937_64 eng(42 + ippl::Comm->rank());
155 std::uniform_real_distribution<TestT> unifR(0.05, 0.95);
156 std::uniform_real_distribution<TestT> unifP(0.1, 0.9);
158 auto R_host =
bunch->R.getHostMirror();
159 auto P_host =
bunch->P.getHostMirror();
160 for (
size_t i = 0; i < n; ++i) {
161 ippl::Vector<TestT, TestDim> r, p;
162 for (
unsigned d = 0; d <
TestDim; ++d) {
169 Kokkos::deep_copy(
bunch->R.getView(), R_host);
170 Kokkos::deep_copy(
bunch->P.getView(), P_host);
171 ippl::Comm->barrier();
179 std::mt19937_64 eng(123 + ippl::Comm->rank());
180 std::uniform_real_distribution<TestT> unifR(0.05, 0.95);
182 auto R_host =
bunch->R.getHostMirror();
183 auto P_host =
bunch->P.getHostMirror();
184 for (
size_t i = 0; i < n; ++i) {
185 ippl::Vector<TestT, TestDim> r, p;
186 for (
unsigned d = 0; d <
TestDim; ++d) {
191 p[axis] = pMin + (pMax - pMin) * (
static_cast<TestT>(i) + 0.5) / n;
195 Kokkos::deep_copy(
bunch->R.getView(), R_host);
196 Kokkos::deep_copy(
bunch->P.getView(), P_host);
197 ippl::Comm->barrier();
205 value_type desW = 0.1,
const std::string& binningCmdName =
"TEST_BINNING_CMD") {
207 adaptBins = std::make_shared<AdaptBins_t>(
208 *
bunch, selector, maxBins, alpha, beta, desW, binningCmdName);
255 for (
bin_index_type n = 1; n <= ParticleBinning::maxArrSize<bin_index_type>; ++n) {
256 auto var = ParticleBinning::createReductionObject<size_type, bin_index_type>(n);
261 sizeof(reducer.the_array) /
sizeof(reducer.the_array[0]),
262 static_cast<size_t>(n));
270 bin_index_type tooBig = ParticleBinning::maxArrSize<bin_index_type> + 1;
273 (ParticleBinning::createReductionObject<size_type, bin_index_type>(tooBig)),
280#ifdef OPALX_DEVICE_COMPILATION
281 GTEST_SKIP() <<
"HostArrayReduction is host-only; skipped under OPALX_DEVICE_COMPILATION.";
310 auto identity = Kokkos::reduction_identity<
313 EXPECT_EQ(identity.the_array[i], 0u);
318#ifdef OPALX_DEVICE_COMPILATION
319 GTEST_SKIP() <<
"HostArrayReduction reduction identity is host-only; skipped under "
320 "OPALX_DEVICE_COMPILATION.";
324 auto identity = Kokkos::reduction_identity<
327 EXPECT_EQ(identity.the_array[i], 0u);
338 auto modeSmall = ParticleBinning::determineHistoReductionMode<bin_index_type>(
340 auto modeLarge = ParticleBinning::determineHistoReductionMode<bin_index_type>(
344 std::is_same<Kokkos::DefaultExecutionSpace, Kokkos::DefaultHostExecutionSpace>::value;
359 std::is_same<Kokkos::DefaultExecutionSpace, Kokkos::DefaultHostExecutionSpace>::value;
362#ifdef OPALX_DEVICE_COMPILATION
365 (ParticleBinning::determineHistoReductionMode<bin_index_type>(
371 auto mode = ParticleBinning::determineHistoReductionMode<bin_index_type>(
379 constexpr size_t N = 5;
380 Kokkos::View<size_type*> input(
"input", N);
381 Kokkos::View<size_type*> postsum(
"postsum", N + 1);
384 auto h_in = Kokkos::create_mirror_view(input);
385 for (
size_t i = 0; i < N; ++i)
387 Kokkos::deep_copy(input, h_in);
389 ParticleBinning::computeFixSum<Kokkos::View<size_type*>>(input, postsum);
391 auto h_ps = Kokkos::create_mirror_view(postsum);
392 Kokkos::deep_copy(h_ps, postsum);
395 EXPECT_EQ(h_ps(0), 0u);
396 EXPECT_EQ(h_ps(1), 1u);
397 EXPECT_EQ(h_ps(2), 3u);
398 EXPECT_EQ(h_ps(3), 6u);
399 EXPECT_EQ(h_ps(4), 10u);
400 EXPECT_EQ(h_ps(5), 15u);
404 constexpr size_t N = 5;
405 Kokkos::View<bin_index_type*> view(
"sortedView", N);
406 auto h_view = Kokkos::create_mirror_view(view);
407 for (
size_t i = 0; i < N; ++i)
409 Kokkos::deep_copy(view, h_view);
412 using hash_type = ippl::detail::hash_type<Kokkos::DefaultExecutionSpace::memory_space>;
413 hash_type idx(
"idx", N);
414 auto h_idx = Kokkos::create_mirror_view(idx);
415 for (
size_t i = 0; i < N; ++i)
416 h_idx(i) =
static_cast<int>(i);
417 Kokkos::deep_copy(idx, h_idx);
419 bool sorted = ParticleBinning::viewIsSorted<bin_index_type, size_type>(view, idx, N);
424 constexpr size_t N = 5;
425 Kokkos::View<bin_index_type*> view(
"unsortedView", N);
426 auto h_view = Kokkos::create_mirror_view(view);
432 Kokkos::deep_copy(view, h_view);
434 using hash_type = ippl::detail::hash_type<Kokkos::DefaultExecutionSpace::memory_space>;
435 hash_type idx(
"idx", N);
436 auto h_idx = Kokkos::create_mirror_view(idx);
437 for (
size_t i = 0; i < N; ++i)
438 h_idx(i) =
static_cast<int>(i);
439 Kokkos::deep_copy(idx, h_idx);
441 bool sorted = ParticleBinning::viewIsSorted<bin_index_type, size_type>(view, idx, N);
442 EXPECT_FALSE(sorted);
453 Histo_t histo(
"testHisto", 4, 2.0, 1.0, 1.0, 0.5);
455 EXPECT_EQ(histo.getCurrentBinCount(), 4);
465 Histo_t histo(
"testInit", nBins, totalWidth, 1.0, 1.0, 0.5);
468 auto histView = histo.getHistogram();
477 auto widths = histo.getBinWidths();
479 EXPECT_NEAR(widths(i), totalWidth / nBins, 1e-12);
483 auto ps = histo.getPostSum();
484 EXPECT_EQ(ps(0), 0u);
485 EXPECT_EQ(ps(1), 10u);
486 EXPECT_EQ(ps(2), 30u);
487 EXPECT_EQ(ps(3), 60u);
488 EXPECT_EQ(ps(4), 100u);
494 Histo_t histo(
"testNPart", 3, 1.0, 1.0, 1.0, 0.3);
495 auto histView = histo.getHistogram();
501 EXPECT_EQ(histo.getNPartInBin(0), 5u);
502 EXPECT_EQ(histo.getNPartInBin(1), 15u);
503 EXPECT_EQ(histo.getNPartInBin(2), 25u);
509 Histo_t original(
"origHisto", 3, 1.5, 1.0, 1.0, 0.5);
510 auto histView = original.getHistogram();
516 Histo_t copy(original);
517 EXPECT_EQ(copy.getCurrentBinCount(), 3);
518 EXPECT_EQ(copy.getNPartInBin(0), 10u);
519 EXPECT_EQ(copy.getNPartInBin(1), 20u);
520 EXPECT_EQ(copy.getNPartInBin(2), 30u);
526 Histo_t original(
"assignOrig", 2, 1.0, 1.0, 1.0, 0.5);
527 auto histView = original.getHistogram();
532 Histo_t assigned(
"dummy", 1, 0.5, 1.0, 1.0, 0.5);
534 EXPECT_EQ(assigned.getCurrentBinCount(), 2);
535 EXPECT_EQ(assigned.getNPartInBin(0), 7u);
536 EXPECT_EQ(assigned.getNPartInBin(1), 13u);
541 using view_type =
decltype(std::declval<Histo_t>().getHistogram().view_device());
557 Histo_t histo(
"policyTest", 3, 1.0, 1.0, 1.0, 0.3);
559 auto dView = histo.getHistogram().view_device();
562 histo.modify_device();
567 auto policy0 = histo.getBinIterationPolicy(0);
568 auto policy1 = histo.getBinIterationPolicy(1);
569 auto policy2 = histo.getBinIterationPolicy(2);
572 EXPECT_EQ(policy0.begin(), 0);
573 EXPECT_EQ(policy0.end(), 5);
574 EXPECT_EQ(policy1.begin(), 5);
575 EXPECT_EQ(policy1.end(), 15);
576 EXPECT_EQ(policy2.begin(), 15);
577 EXPECT_EQ(policy2.end(), 30);
586 Histo_t histo(
"mergeTest", nBins, totalWidth, 1.0, 1.0, 0.2);
592 auto histView = histo.getHistogram();
605 auto lookupTable = histo.mergeBins();
609 EXPECT_GT(mergedCount, 0);
610 EXPECT_LE(mergedCount, nBins);
614 auto h_lookup = Kokkos::create_mirror_view(lookupTable);
615 Kokkos::deep_copy(h_lookup, lookupTable);
617 EXPECT_GE(h_lookup(i), h_lookup(i - 1));
622 EXPECT_GE(h_lookup(i), 0);
623 EXPECT_LT(h_lookup(i), mergedCount);
629 totalAfter += histo.getNPartInBin(i);
631 EXPECT_EQ(totalAfter, 406u);
636 using view_type =
decltype(std::declval<Histo_t>().getHistogram().view_device());
648 Histo_t histo(
"dualViewHisto", 4, 2.0, 1.0, 1.0, 0.5);
649 EXPECT_EQ(histo.getCurrentBinCount(), 4);
652 auto dView = histo.getHistogram().view_device();
655 histo.modify_device();
659 EXPECT_EQ(histo.getNPartInBin(0), 10u);
660 EXPECT_EQ(histo.getNPartInBin(1), 20u);
661 EXPECT_EQ(histo.getNPartInBin(2), 30u);
662 EXPECT_EQ(histo.getNPartInBin(3), 40u);
673 createParticles(nPart);
678 auto P_host = bunch->P.getHostMirror();
679 Kokkos::deep_copy(P_host, bunch->P.getView());
681 size_t localN = bunch->getLocalNum();
682 for (
size_t i = 0; i < localN; ++i) {
684 value_type expected = p_z / std::sqrt(1.0 + p_z * p_z);
688 EXPECT_GE(expected, 0.0);
689 EXPECT_LT(expected, 1.0);
701 value_type binWidthInv = numBins / (xMax - xMin);
705 value_type midpoint = xMin + (b + 0.5) / numBins * (xMax - xMin);
707 EXPECT_EQ(result, b);
714 value_type binWidthInv = numBins / (xMax - xMin);
718 EXPECT_EQ(resultLow, 0);
722 EXPECT_EQ(resultHigh, numBins - 1);
728 value_type binWidthInv = numBins / (xMax - xMin);
744 EXPECT_EQ(adaptBins->getMaxBinCount(), 5);
745 EXPECT_EQ(adaptBins->getCurrentBinCount(), 5);
752 adaptBins->setCurrentBinCount(3);
753 EXPECT_EQ(adaptBins->getCurrentBinCount(), 3);
756 adaptBins->setCurrentBinCount(100);
757 EXPECT_EQ(adaptBins->getCurrentBinCount(), 5);
761 createParticles(200);
764 adaptBins->initLimits();
774 createParticles(nPart);
777 adaptBins->doFullRebin(5);
781 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
782 totalInBins += adaptBins->getNPartInBin(b);
784 EXPECT_EQ(totalInBins, bunch->getLocalNum());
790 createParticles(nPart);
793 adaptBins->doFullRebin(5);
796 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
797 totalGlobal += adaptBins->getNPartInBin(b,
true);
799 EXPECT_EQ(totalGlobal, bunch->getTotalNum());
805 createParticles(nPart);
808 adaptBins->doFullRebin(5);
810 auto binHost = bunch->Bin.getHostMirror();
811 Kokkos::deep_copy(binHost, bunch->Bin.getView());
813 for (
size_t i = 0; i < bunch->getLocalNum(); ++i) {
814 EXPECT_GE(binHost(i), 0);
815 EXPECT_LT(binHost(i), nBins);
823 createParticles(nPart);
826 adaptBins->doFullRebin(5);
827 adaptBins->sortContainerByBin();
829 auto hashArr = adaptBins->getHashArray();
830 auto binView = bunch->Bin.getView();
832 size_t localN = bunch->getLocalNum();
833 if (localN <= 1)
return;
836 auto h_hash = Kokkos::create_mirror_view(hashArr);
837 Kokkos::deep_copy(h_hash, hashArr);
838 auto h_bin = bunch->Bin.getHostMirror();
839 Kokkos::deep_copy(h_bin, binView);
841 for (
size_t i = 1; i < localN; ++i) {
842 EXPECT_GE(h_bin(h_hash(i)), h_bin(h_hash(i - 1))) <<
"Sort violated at index " << i;
849 createParticles(nPart);
852 adaptBins->doFullRebin(5);
853 adaptBins->sortContainerByBin();
856 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
857 auto policy = adaptBins->getBinIterationPolicy(b);
858 totalCovered += (policy.end() - policy.begin());
860 EXPECT_EQ(totalCovered, bunch->getLocalNum());
867 createParticles(nPart);
871 buildAdaptBins(maxBins, 1.0, 1.0, 0.3);
873 adaptBins->doFullRebin(maxBins);
876 EXPECT_EQ(binsBefore, maxBins);
878 adaptBins->genAdaptiveHistogram();
881 EXPECT_GT(binsAfter, 0);
882 EXPECT_LE(binsAfter, binsBefore);
887 totalInBins += adaptBins->getNPartInBin(b);
889 EXPECT_EQ(totalInBins, bunch->getLocalNum());
895 createParticles(nPart);
899 adaptBins->setCurrentBinCount(nBins);
900 adaptBins->doFullRebin(nBins);
903 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
904 totalInBins += adaptBins->getNPartInBin(b);
906 EXPECT_EQ(totalInBins, bunch->getLocalNum()) <<
"Mismatch with nBins=" << nBins;
913 createParticles(nPart);
916 for (
int iter = 0; iter < 3; ++iter) {
917 adaptBins->doFullRebin(4);
920 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
921 total += adaptBins->getNPartInBin(b);
923 EXPECT_EQ(total, bunch->getLocalNum()) <<
"Failed at iteration " << iter;
930 createParticles(nPart);
933 adaptBins->doFullRebin(4);
934 adaptBins->sortContainerByBin();
936 auto hashArr = adaptBins->getHashArray();
937 auto h_hash = Kokkos::create_mirror_view(hashArr);
938 Kokkos::deep_copy(h_hash, hashArr);
940 auto h_bin = bunch->Bin.getHostMirror();
941 Kokkos::deep_copy(h_bin, bunch->Bin.getView());
943 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
944 auto policy = adaptBins->getBinIterationPolicy(b);
945 for (
auto idx = policy.begin(); idx < policy.end(); ++idx) {
946 EXPECT_EQ(h_bin(h_hash(idx)), b) <<
"Particle at sorted index " << idx <<
" has bin "
947 << h_bin(h_hash(idx)) <<
" but expected " << b;
956#ifdef OPALX_DEVICE_COMPILATION
958 <<
"HostOnly histogram reduction is host-only; skipped under OPALX_DEVICE_COMPILATION.";
961 createParticles(nPart);
965 buildAdaptBins(maxBins);
970 for (
bin_index_type b = 0; b < adaptBins->getCurrentBinCount(); ++b) {
971 totalHost += adaptBins->getNPartInBin(b);
973 EXPECT_EQ(totalHost, bunch->getLocalNum());
980 createParticlesUniformP(nPart, 2, 0.1, 0.9);
990 adaptBins->doFullRebin(5);
994 if (localN < 5)
return;
997 size_type count = adaptBins->getNPartInBin(b);
999 EXPECT_GT(count, 50) <<
"Bin " << b <<
" is too small for \"uniform\" distribution.";
1000 EXPECT_LT(count, 350) <<
"Bin " << b <<
" is too big for \"uniform\" distribution";
1008 createParticles(nPart);
1011 buildAdaptBins(maxBins, 1.0, 1.0, 0.2);
1014 adaptBins->doFullRebin(maxBins);
1017 adaptBins->genAdaptiveHistogram();
1019 EXPECT_GT(adaptiveBins, 0);
1022 adaptBins->sortContainerByBin();
1025 auto hashArr = adaptBins->getHashArray();
1026 auto h_hash = Kokkos::create_mirror_view(hashArr);
1027 Kokkos::deep_copy(h_hash, hashArr);
1028 auto h_bin = bunch->Bin.getHostMirror();
1029 Kokkos::deep_copy(h_bin, bunch->Bin.getView());
1033 auto policy = adaptBins->getBinIterationPolicy(b);
1034 size_type rangeSize = policy.end() - policy.begin();
1035 totalCovered += rangeSize;
1038 for (
auto idx = policy.begin(); idx < policy.end(); ++idx) {
1039 EXPECT_EQ(h_bin(h_hash(idx)), b);
1042 EXPECT_EQ(totalCovered, bunch->getLocalNum());
Defines a structure to hold particles in energy bins and their associated data.
ippl::Vector< T, Dim > Vector_t
typename Mesh_t< Dim >::DefaultCentering Centering_t
ippl::Field< T, Dim, Mesh_t< Dim >, Centering_t< Dim >, ViewArgs... > Field
ippl::Field< ippl::Vector< T, Dim >, Dim, ViewArgs... > VField_t
ippl::UniformCartesian< double, 3 > Mesh_t
ippl::detail::size_type size_type
ippl::detail::size_type size_type
TEST_F(BinningTest, ArrayReductionDefaultInit)
ippl::UniformCartesian< TestT, TestDim > TestMesh_t
ippl::FieldLayout< TestDim > TestFL_t
ippl::Vector< T, Dim > Vector
ippl::ParticleSpatialLayout< TestT, TestDim, TestMesh_t > TestPLayout_t
Container_t::bin_index_type bin_index_type
constexpr unsigned TestDim
std::shared_ptr< Container_t > bunch
std::unique_ptr< TestMesh_t > mesh
static constexpr TestT domainLen
static constexpr size_t gridPoints
static void SetUpTestSuite()
std::unique_ptr< TestPLayout_t > playout
static void TearDownTestSuite()
void buildAdaptBins(bin_index_type maxBins=5, value_type alpha=1.0, value_type beta=1.0, value_type desW=0.1, const std::string &binningCmdName="TEST_BINNING_CMD")
Build an AdaptBins with given parameters.
void createParticlesUniformP(size_t n, int axis, TestT pMin, TestT pMax)
Creates n particles where P[axis] is uniformly spread in [pMin, pMax].
std::unique_ptr< TestFL_t > fl
void createParticles(size_t n)
Creates n particles with random P values in [0.1, 0.9] and R in mesh interior.
std::shared_ptr< AdaptBins_t > adaptBins
A class that bins particles in energy bins and allows for adaptive runtime rebinning.
static KOKKOS_INLINE_FUNCTION bin_index_type getBin(value_type x, value_type xMin, value_type xMax, value_type binWidthInv, bin_index_type numBins)
Calculates the bin index for a given position value in a uniform histogram.
Template class providing adaptive particle histogram binning with support for Kokkos Views and DualVi...
decltype(std::declval< Histo_t >().getHistogram().view_device()) view_type
KOKKOS_FUNCTION void operator()(const size_t) const
FillPolicyHistogram1(view_type v)
FillPolicyHistogram2(view_type v)
KOKKOS_FUNCTION void operator()(const size_t i) const
decltype(std::declval< Histo_t >().getHistogram().view_device()) view_type
A templated structure for performing array-based reductions in parallel computations.
Example struct used to access the binning variable for each particle.
void updateDataArr(bunch_type &bunch)
Updates the data array view with the latest particle data.
Host-only array reduction structure for dynamic array sizes in Kokkos::parallel_reduce.
SizeType * the_array
Pointer to the dynamically allocated array for reduction operations.
Minimal particle bunch for binning unit tests.
ippl::ParticleAttrib< bin_index_type > Bin
Bin index attribute (used by AdaptBins)
TestBunch(TestPLayout_t &pl)
ippl::ParticleBase< TestPLayout_t > Base
Base::particle_position_type P
Particle momenta (used by CoordinateSelector)