// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include #include #include "mfem.hpp" #include "unit_tests.hpp" // must be included after mfem.hpp #include "general/scan.hpp" using namespace mfem; TEST_CASE("Inclusive Scan", "[Scan],[GPU]") { Array a(10); for (int use_dev = 0; use_dev < 2; ++use_dev) { CAPTURE(use_dev); a.HostReadWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = i; } auto dptr = a.ReadWrite(use_dev); InclusiveScan(use_dev, dptr, dptr, a.Size()); a.HostRead(); for (int i = 0; i < a.Size(); ++i) { int expected = (i + 1) * i / 2; CAPTURE(i); REQUIRE(AsConst(a)[i] == expected); } a.HostReadWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = i + 1; } a.ReadWrite(use_dev); InclusiveScan(use_dev, dptr, dptr, a.Size(), std::multiplies<> {}); a.HostRead(); int expected = 1; for (int i = 0; i < a.Size(); ++i) { expected *= i + 1; CAPTURE(i); REQUIRE(AsConst(a)[i] == expected); } } } TEST_CASE("Exclusive Scan", "[Scan],[GPU]") { Array a(10); for (int use_dev = 0; use_dev < 2; ++use_dev) { CAPTURE(use_dev); a.HostReadWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = i; } auto dptr = a.ReadWrite(use_dev); ExclusiveScan(use_dev, dptr, dptr, a.Size(), 5); a.HostRead(); for (int i = 0; i < a.Size(); ++i) { int expected = (i + 1) * i / 2 - i + 5; CAPTURE(i); REQUIRE(AsConst(a)[i] == expected); } a.HostReadWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = i + 1; } a.ReadWrite(use_dev); ExclusiveScan(use_dev, dptr, dptr, a.Size(), 5, std::multiplies<> {}); a.HostRead(); int expected = 5; for (int i = 0; i < a.Size(); ++i) { CAPTURE(i); REQUIRE(AsConst(a)[i] == expected); expected *= i + 1; } } } TEST_CASE("CopyFlagged", "[Scan],[GPU]") { Array a(10); Array flags(a.Size()); Array res(a.Size()); Array num_selected_out(1); for (int use_dev = 0; use_dev < 2; ++use_dev) { CAPTURE(use_dev); a.HostWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = i; } flags.HostWrite(); // keep entries which are a multiple of 3 for (int i = 0; i < flags.Size(); ++i) { if (i % 3) { flags[i] = false; } else { flags[i] = true; } } auto d_in = a.Read(use_dev); auto d_flags = flags.Read(use_dev); auto d_out = res.Write(use_dev); auto d_num_selected_out = num_selected_out.Write(use_dev); CopyFlagged(use_dev, d_in, d_flags, d_out, d_num_selected_out, a.Size()); res.HostRead(); num_selected_out.HostRead(); REQUIRE(AsConst(num_selected_out)[0] == 4); REQUIRE(AsConst(res)[0] == 0); REQUIRE(AsConst(res)[1] == 3); REQUIRE(AsConst(res)[2] == 6); REQUIRE(AsConst(res)[3] == 9); } } TEST_CASE("CopyIf", "[Scan][GPU]") { Array a(10); Array res(a.Size()); Array num_selected_out(1); for (int use_dev = 1; use_dev < 2; ++use_dev) { CAPTURE(use_dev); a.HostWrite(); res.HostWrite(); num_selected_out.HostWrite(); num_selected_out[0] = 0; for (int i = 0; i < a.Size(); ++i) { a[i] = i; res[i] = 0; } auto d_in = a.Read(use_dev); auto d_out = res.Write(use_dev); auto d_num_selected_out = num_selected_out.Write(use_dev); // copy all values not divisible by 3 // 1, 2, 4, 5, 7, 8 CopyIf(use_dev, d_in, d_out, d_num_selected_out, a.Size(), [=] MFEM_HOST_DEVICE(const int &value) { return value % 3; }); res.HostRead(); num_selected_out.HostRead(); REQUIRE(AsConst(num_selected_out)[0] == 6); REQUIRE(AsConst(res)[0] == 1); REQUIRE(AsConst(res)[1] == 2); REQUIRE(AsConst(res)[2] == 4); REQUIRE(AsConst(res)[3] == 5); REQUIRE(AsConst(res)[4] == 7); REQUIRE(AsConst(res)[5] == 8); } } TEST_CASE("CopyUnique", "[Scan][GPU]") { Array a(10); Array res(a.Size()); Array num_selected_out(1); for (int use_dev = 0; use_dev < 2; ++use_dev) { CAPTURE(use_dev); a.HostWrite(); for (int i = 0; i < a.Size(); ++i) { a[i] = 2; } a[4] = 1; a[5] = 1; auto d_in = a.Read(use_dev); auto d_out = res.Write(use_dev); auto d_num_selected_out = num_selected_out.Write(use_dev); CopyUnique(use_dev, d_in, d_out, d_num_selected_out, a.Size()); res.HostRead(); num_selected_out.HostRead(); REQUIRE(AsConst(num_selected_out)[0] == 3); REQUIRE(AsConst(res)[0] == 2); REQUIRE(AsConst(res)[1] == 1); REQUIRE(AsConst(res)[2] == 2); } }