Merge branch 'alphataubio-kokkos-bio' of https://github.com/alphataubio/lammps-alphataubio into alphataubio-kokkos-bio
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
add_executable(test_library_open test_library_open.cpp test_main.cpp)
|
||||
target_link_libraries(test_library_open PRIVATE lammps GTest::GMock)
|
||||
add_test(NAME LibraryOpen COMMAND test_library_open)
|
||||
set_tests_properties(LibraryOpen PROPERTIES ENVIRONMENT "OMP_NUM_THREADS=4;OMP_PROC_BIND=false")
|
||||
|
||||
add_executable(test_library_commands test_library_commands.cpp test_main.cpp)
|
||||
target_link_libraries(test_library_commands PRIVATE lammps GTest::GMock)
|
||||
@ -16,7 +17,7 @@ add_executable(test_library_properties test_library_properties.cpp test_main.cpp
|
||||
target_link_libraries(test_library_properties PRIVATE lammps GTest::GMock)
|
||||
target_compile_definitions(test_library_properties PRIVATE -DTEST_INPUT_FOLDER=${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_test(NAME LibraryProperties COMMAND test_library_properties)
|
||||
set_tests_properties(LibraryProperties PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
||||
set_tests_properties(LibraryProperties PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};OMP_NUM_THREADS=4;OMP_PROC_BIND=false")
|
||||
|
||||
add_executable(test_library_objects test_library_objects.cpp test_main.cpp)
|
||||
target_link_libraries(test_library_objects PRIVATE lammps GTest::GMock)
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
|
||||
#include "lammps.h"
|
||||
#define LAMMPS_LIB_MPI 1
|
||||
#include "info.h"
|
||||
#include "library.h"
|
||||
#include <cstdio> // for stdin, stdout
|
||||
#include <mpi.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -78,9 +80,38 @@ TEST(lammps_open, with_args)
|
||||
TEST(lammps_open, with_kokkos)
|
||||
{
|
||||
if (!LAMMPS_NS::LAMMPS::is_installed_pkg("KOKKOS")) GTEST_SKIP();
|
||||
const char *args[] = {"liblammps", "-k", "on", "t", "2", "-sf", "kk", "-log", "none", nullptr};
|
||||
char **argv = (char **)args;
|
||||
int argc = (sizeof(args) / sizeof(char *)) - 1;
|
||||
std::vector<char *> args = {(char *)"lammps", (char *)"-log", (char *)"none", (char *)"-echo",
|
||||
(char *)"screen", (char *)"-sf", (char *)"kk"};
|
||||
|
||||
char *one = (char *)"1";
|
||||
char *four = (char *)"4";
|
||||
char *tee = (char *)"t";
|
||||
char *gee = (char *)"g";
|
||||
char *kay = (char *)"-k";
|
||||
char *yes = (char *)"on";
|
||||
|
||||
args.push_back(kay);
|
||||
args.push_back(yes);
|
||||
|
||||
// when GPU support is enabled in KOKKOS, it *must* be used
|
||||
if (lammps_config_accelerator("KOKKOS", "api", "hip") ||
|
||||
lammps_config_accelerator("KOKKOS", "api", "cuda") ||
|
||||
lammps_config_accelerator("KOKKOS", "api", "sycl")) {
|
||||
args.push_back(gee);
|
||||
args.push_back(one);
|
||||
}
|
||||
|
||||
// use threads or serial
|
||||
args.push_back(tee);
|
||||
if (lammps_config_accelerator("KOKKOS", "api", "openmp")) {
|
||||
args.push_back(four);
|
||||
} else if (lammps_config_accelerator("KOKKOS", "api", "pthreads")) {
|
||||
args.push_back(four);
|
||||
} else {
|
||||
args.push_back(one);
|
||||
}
|
||||
int argc = args.size();
|
||||
char **argv = args.data();
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
void *alt_ptr;
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "lmptype.h"
|
||||
#include "platform.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -742,14 +743,45 @@ TEST_F(AtomProperties, position)
|
||||
TEST(SystemSettings, kokkos)
|
||||
{
|
||||
if (!lammps_config_has_package("KOKKOS")) GTEST_SKIP();
|
||||
if (!lammps_config_accelerator("KOKKOS", "api", "openmp")) GTEST_SKIP();
|
||||
std::vector<char *> args = {(char *)"lammps", (char *)"-log", (char *)"none",
|
||||
(char *)"-echo", (char *)"screen", (char *)"-nocite",
|
||||
(char *)"-sf", (char *)"kk"};
|
||||
|
||||
// clang-format off
|
||||
const char *args[] = {"SystemSettings", "-log", "none", "-echo", "screen", "-nocite",
|
||||
"-k", "on", "t", "4", "-sf", "kk", nullptr};
|
||||
// clang-format on
|
||||
char **argv = (char **)args;
|
||||
int argc = (sizeof(args) / sizeof(char *)) - 1;
|
||||
char *one = (char *)"1";
|
||||
char *four = (char *)"4";
|
||||
char *tee = (char *)"t";
|
||||
char *gee = (char *)"g";
|
||||
char *kay = (char *)"-k";
|
||||
char *yes = (char *)"on";
|
||||
|
||||
args.push_back(kay);
|
||||
args.push_back(yes);
|
||||
|
||||
bool has_gpu = false;
|
||||
bool has_threads = false;
|
||||
|
||||
// when GPU support is enabled in KOKKOS, it *must* be used
|
||||
if (lammps_config_accelerator("KOKKOS", "api", "hip") ||
|
||||
lammps_config_accelerator("KOKKOS", "api", "cuda") ||
|
||||
lammps_config_accelerator("KOKKOS", "api", "sycl")) {
|
||||
has_gpu = true;
|
||||
args.push_back(gee);
|
||||
args.push_back(one);
|
||||
}
|
||||
|
||||
// use threads or serial
|
||||
args.push_back(tee);
|
||||
if (lammps_config_accelerator("KOKKOS", "api", "openmp")) {
|
||||
has_threads = true;
|
||||
args.push_back(four);
|
||||
} else if (lammps_config_accelerator("KOKKOS", "api", "pthreads")) {
|
||||
has_threads = true;
|
||||
args.push_back(four);
|
||||
} else {
|
||||
args.push_back(one);
|
||||
}
|
||||
int argc = args.size();
|
||||
char **argv = args.data();
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
void *lmp = lammps_open_no_mpi(argc, argv, nullptr);
|
||||
@ -758,7 +790,13 @@ TEST(SystemSettings, kokkos)
|
||||
EXPECT_THAT(output, StartsWith("LAMMPS ("));
|
||||
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_active"), 1);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_nthreads"), 4);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_ngpus"), 0);
|
||||
if (has_threads)
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_nthreads"), 4);
|
||||
else
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_nthreads"), 1);
|
||||
if (has_gpu)
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_ngpus"), 1);
|
||||
else
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "kokkos_ngpus"), 0);
|
||||
lammps_close(lmp);
|
||||
}
|
||||
|
||||
@ -314,7 +314,7 @@ TEST_F(GroupTest, Dynamic)
|
||||
command("group ramp variable grow"););
|
||||
}
|
||||
|
||||
constexpr double EPSILON = 1.0e-13;
|
||||
static constexpr double EPSILON = 1.0e-13;
|
||||
|
||||
TEST_F(GroupTest, VariableFunctions)
|
||||
{
|
||||
|
||||
@ -287,6 +287,7 @@ TEST_F(VariableTest, AtomicSystem)
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("f_press[1]"), 0.0);
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("c_press"), 0.0);
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("c_press[2]"), 0.0);
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("c_press[1+1]"), 0.0);
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("1.5+3.25"), 4.75);
|
||||
ASSERT_DOUBLE_EQ(variable->compute_equal("-2.5*1.5"), -3.75);
|
||||
|
||||
@ -302,8 +303,18 @@ TEST_F(VariableTest, AtomicSystem)
|
||||
variable->compute_equal("v_self"););
|
||||
TEST_FAILURE(".*ERROR: Variable sum2: Inconsistent lengths in vector-style variable.*",
|
||||
variable->compute_equal("max(v_sum2)"););
|
||||
TEST_FAILURE("ERROR: Mismatched fix in variable formula.*",
|
||||
TEST_FAILURE(".*ERROR: Mismatched fix in variable formula.*",
|
||||
variable->compute_equal("f_press"););
|
||||
TEST_FAILURE(".*ERROR .*Variable formula compute vector is accessed out-of-range.*",
|
||||
variable->compute_equal("c_press[10]"););
|
||||
TEST_FAILURE(".*ERROR: Non digit character between brackets in variable.*",
|
||||
variable->compute_equal("c_press[axy]"););
|
||||
TEST_FAILURE(".*ERROR: Illegal value in brackets: stoll.*",
|
||||
variable->compute_equal("c_press[73786976294838206464]"););
|
||||
TEST_FAILURE(".*ERROR: Index between variable brackets must be positive.*",
|
||||
variable->compute_equal("c_press[-2]"););
|
||||
TEST_FAILURE(".*ERROR: Index between variable brackets must be positive.*",
|
||||
variable->compute_equal("c_press[0]"););
|
||||
}
|
||||
|
||||
TEST_F(VariableTest, Expressions)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
add_executable(test_lammps_class test_lammps_class.cpp)
|
||||
target_link_libraries(test_lammps_class PRIVATE lammps GTest::GMockMain)
|
||||
add_test(NAME LammpsClass COMMAND test_lammps_class)
|
||||
set_tests_properties(LammpsClass PROPERTIES ENVIRONMENT "OMP_NUM_THREADS=1")
|
||||
set_tests_properties(LammpsClass PROPERTIES ENVIRONMENT "OMP_NUM_THREADS=2;OMP_PROC_BIND=false")
|
||||
|
||||
add_executable(test_input_class test_input_class.cpp)
|
||||
target_link_libraries(test_input_class PRIVATE lammps GTest::GTestMain)
|
||||
|
||||
@ -253,6 +253,15 @@ protected:
|
||||
{
|
||||
LAMMPS::argv args = {"LAMMPS_test", "-log", "none", "-echo", "none", "-screen", "none",
|
||||
"-k", "on", "t", "1", "-sf", "kk"};
|
||||
|
||||
// when GPU support is enabled in KOKKOS, it *must* be used
|
||||
if (Info::has_accelerator_feature("KOKKOS", "api", "hip") ||
|
||||
Info::has_accelerator_feature("KOKKOS", "api", "cuda") ||
|
||||
Info::has_accelerator_feature("KOKKOS", "api", "sycl")) {
|
||||
args = {"LAMMPS_test", "-log", "none", "-echo", "none", "-screen", "none", "-k",
|
||||
"on", "t", "1", "g", "1", "-sf", "kk"};
|
||||
}
|
||||
|
||||
if (Info::has_accelerator_feature("KOKKOS", "api", "openmp")) args[10] = "2";
|
||||
|
||||
if (LAMMPS::is_installed_pkg("KOKKOS")) {
|
||||
|
||||
@ -204,11 +204,7 @@ foreach(TEST ${FIX_TIMESTEP_TESTS})
|
||||
continue()
|
||||
endif()
|
||||
add_test(NAME ${TNAME} COMMAND test_fix_timestep ${TEST})
|
||||
if(WIN32)
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}\\\;${LAMMPS_PYTHON_DIR}")
|
||||
else()
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONDONTWRITEBYTECODE=1")
|
||||
endif()
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "${FORCE_TEST_ENVIRONMENT}")
|
||||
set_tests_properties(${TNAME} PROPERTIES LABELS "${TEST_TAGS}")
|
||||
endforeach()
|
||||
|
||||
@ -225,7 +221,7 @@ foreach(TEST ${DIHEDRAL_TESTS})
|
||||
continue()
|
||||
endif()
|
||||
add_test(NAME ${TNAME} COMMAND test_dihedral_style ${TEST})
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:$ENV{PYTHONPATH};PYTHONDONTWRITEBYTECODE=1")
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "${FORCE_TEST_ENVIRONMENT}")
|
||||
set_tests_properties(${TNAME} PROPERTIES LABELS "${TEST_TAGS}")
|
||||
endforeach()
|
||||
|
||||
@ -242,7 +238,7 @@ foreach(TEST ${IMPROPER_TESTS})
|
||||
continue()
|
||||
endif()
|
||||
add_test(NAME ${TNAME} COMMAND test_improper_style ${TEST})
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:$ENV{PYTHONPATH};PYTHONDONTWRITEBYTECODE=1")
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "${FORCE_TEST_ENVIRONMENT}")
|
||||
set_tests_properties(${TNAME} PROPERTIES LABELS "${TEST_TAGS}")
|
||||
endforeach()
|
||||
|
||||
@ -250,7 +246,7 @@ if(MLIAP_ENABLE_PYTHON AND (NOT WIN32))
|
||||
add_executable(test_mliappy_unified test_mliappy_unified.cpp)
|
||||
target_link_libraries(test_mliappy_unified PRIVATE lammps GTest::GMockMain)
|
||||
add_test(NAME TestMliapPyUnified COMMAND test_mliappy_unified)
|
||||
set_tests_properties(TestMliapPyUnified PROPERTIES ENVIRONMENT "PYTHONPATH=${LAMMPS_PYTHON_DIR};PYTHONDONTWRITEBYTECODE=1")
|
||||
set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "${FORCE_TEST_ENVIRONMENT}")
|
||||
endif()
|
||||
|
||||
add_executable(test_pair_list test_pair_list.cpp)
|
||||
|
||||
@ -530,6 +530,126 @@ TEST(AngleStyle, omp)
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(AngleStyle, kokkos_omp)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("KOKKOS")) GTEST_SKIP();
|
||||
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
|
||||
if (!Info::has_accelerator_feature("KOKKOS", "api", "openmp")) GTEST_SKIP();
|
||||
|
||||
LAMMPS::argv args = {"AngleStyle", "-log", "none", "-echo", "screen", "-nocite",
|
||||
"-k", "on", "t", "4", "-sf", "kk"};
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
LAMMPS *lmp = init_lammps(args, test_config, true);
|
||||
|
||||
std::string output = ::testing::internal::GetCapturedStdout();
|
||||
if (verbose) std::cout << output;
|
||||
|
||||
if (!lmp) {
|
||||
std::cerr << "One or more prerequisite styles with /kk suffix\n"
|
||||
"are not available in this LAMMPS configuration:\n";
|
||||
for (auto &prerequisite : test_config.prerequisites) {
|
||||
std::cerr << prerequisite.first << "_style " << prerequisite.second << "\n";
|
||||
}
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
EXPECT_THAT(output, StartsWith("LAMMPS ("));
|
||||
EXPECT_THAT(output, HasSubstr("Loop time"));
|
||||
|
||||
// abort if running in parallel and not all atoms are local
|
||||
const int nlocal = lmp->atom->nlocal;
|
||||
ASSERT_EQ(lmp->atom->natoms, nlocal);
|
||||
|
||||
// relax error a bit for KOKKOS package
|
||||
double epsilon = 5.0 * test_config.epsilon;
|
||||
|
||||
ErrorStats stats;
|
||||
auto angle = lmp->force->angle;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton on)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton on)", angle->virial, test_config.init_stress, epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton on)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton on)", angle->virial, test_config.run_stress, epsilon);
|
||||
|
||||
stats.reset();
|
||||
int id = lmp->modify->find_compute("sum");
|
||||
double energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.run_energy, epsilon);
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, energy, epsilon);
|
||||
if (print_stats) std::cerr << "run_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
lmp = init_lammps(args, test_config, false);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
// skip over these tests if newton bond is forced to be on
|
||||
if (lmp->force->newton_bond == 0) {
|
||||
angle = lmp->force->angle;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton off)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton off)", angle->virial, test_config.init_stress,
|
||||
2 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton off:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton off)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton off)", angle->virial, test_config.run_stress, epsilon);
|
||||
|
||||
stats.reset();
|
||||
id = lmp->modify->find_compute("sum");
|
||||
energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.run_energy, epsilon);
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, energy, epsilon);
|
||||
if (print_stats) std::cerr << "run_energy stats, newton off:" << stats << std::endl;
|
||||
}
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
restart_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
angle = lmp->force->angle;
|
||||
EXPECT_FORCES("restart_forces", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("restart_stress", angle->virial, test_config.init_stress, epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "restart_energy stats:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
data_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
angle = lmp->force->angle;
|
||||
EXPECT_FORCES("data_forces", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("data_stress", angle->virial, test_config.init_stress, epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(angle->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "data_energy stats:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
|
||||
TEST(AngleStyle, numdiff)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("EXTRA-FIX")) GTEST_SKIP();
|
||||
|
||||
@ -532,6 +532,112 @@ TEST(BondStyle, omp)
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(BondStyle, kokkos_omp)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("KOKKOS")) GTEST_SKIP();
|
||||
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
|
||||
if (!Info::has_accelerator_feature("KOKKOS", "api", "openmp")) GTEST_SKIP();
|
||||
|
||||
LAMMPS::argv args = {"BondStyle", "-log", "none", "-echo", "screen", "-nocite",
|
||||
"-k", "on", "t", "4", "-sf", "kk"};
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
LAMMPS *lmp = init_lammps(args, test_config, true);
|
||||
|
||||
std::string output = ::testing::internal::GetCapturedStdout();
|
||||
if (verbose) std::cout << output;
|
||||
|
||||
if (!lmp) {
|
||||
std::cerr << "One or more prerequisite styles with /kk suffix\n"
|
||||
"are not available in this LAMMPS configuration:\n";
|
||||
for (auto &prerequisite : test_config.prerequisites) {
|
||||
std::cerr << prerequisite.first << "_style " << prerequisite.second << "\n";
|
||||
}
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
EXPECT_THAT(output, StartsWith("LAMMPS ("));
|
||||
EXPECT_THAT(output, HasSubstr("Loop time"));
|
||||
|
||||
// abort if running in parallel and not all atoms are local
|
||||
const int nlocal = lmp->atom->nlocal;
|
||||
ASSERT_EQ(lmp->atom->natoms, nlocal);
|
||||
|
||||
// relax error a bit for KOKKOS package
|
||||
double epsilon = 5.0 * test_config.epsilon;
|
||||
|
||||
ErrorStats stats;
|
||||
auto bond = lmp->force->bond;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton on)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton on)", bond->virial, test_config.init_stress, 10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(bond->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton on)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton on)", bond->virial, test_config.run_stress, 10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
int id = lmp->modify->find_compute("sum");
|
||||
double energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(bond->energy, test_config.run_energy, epsilon);
|
||||
|
||||
// FIXME: this is currently broken ??? for KOKKOS with bond style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.bond_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(bond->energy, energy, epsilon);
|
||||
|
||||
if (print_stats) std::cerr << "run_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
lmp = init_lammps(args, test_config, false);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
// skip over these tests if newton bond is forced to be on
|
||||
if (lmp->force->newton_bond == 0) {
|
||||
bond = lmp->force->bond;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton off)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton off)", bond->virial, test_config.init_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(bond->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton off:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton off)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton off)", bond->virial, test_config.run_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
id = lmp->modify->find_compute("sum");
|
||||
energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(bond->energy, test_config.run_energy, epsilon);
|
||||
|
||||
// FIXME: this is currently broken ??? for KOKKOS with bond style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.bond_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(bond->energy, energy, epsilon);
|
||||
|
||||
if (print_stats) std::cerr << "run_energy stats, newton off:" << stats << std::endl;
|
||||
}
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(BondStyle, numdiff)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("EXTRA-FIX")) GTEST_SKIP();
|
||||
|
||||
@ -534,6 +534,112 @@ TEST(DihedralStyle, omp)
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(DihedralStyle, kokkos_omp)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("KOKKOS")) GTEST_SKIP();
|
||||
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
|
||||
if (!Info::has_accelerator_feature("KOKKOS", "api", "openmp")) GTEST_SKIP();
|
||||
|
||||
LAMMPS::argv args = {"DihedralStyle", "-log", "none", "-echo", "screen", "-nocite",
|
||||
"-k", "on", "t", "4", "-sf", "kk"};
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
LAMMPS *lmp = init_lammps(args, test_config, true);
|
||||
|
||||
std::string output = ::testing::internal::GetCapturedStdout();
|
||||
if (verbose) std::cout << output;
|
||||
|
||||
if (!lmp) {
|
||||
std::cerr << "One or more prerequisite styles with /kk suffix\n"
|
||||
"are not available in this LAMMPS configuration:\n";
|
||||
for (auto &prerequisite : test_config.prerequisites) {
|
||||
std::cerr << prerequisite.first << "_style " << prerequisite.second << "\n";
|
||||
}
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
EXPECT_THAT(output, StartsWith("LAMMPS ("));
|
||||
EXPECT_THAT(output, HasSubstr("Loop time"));
|
||||
|
||||
// abort if running in parallel and not all atoms are local
|
||||
const int nlocal = lmp->atom->nlocal;
|
||||
ASSERT_EQ(lmp->atom->natoms, nlocal);
|
||||
|
||||
// relax error a bit for KOKKOS package
|
||||
double epsilon = 5.0 * test_config.epsilon;
|
||||
|
||||
ErrorStats stats;
|
||||
auto dihedral = lmp->force->dihedral;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton on)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton on)", dihedral->virial, test_config.init_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(dihedral->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton on)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton on)", dihedral->virial, test_config.run_stress, 10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
int id = lmp->modify->find_compute("sum");
|
||||
double energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(dihedral->energy, test_config.run_energy, epsilon);
|
||||
|
||||
// FIXME: this is currently broken ??? for KOKKOS with dihedral style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.dihedral_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(dihedral->energy, energy, epsilon);
|
||||
//if (print_stats) std::cerr << "run_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
lmp = init_lammps(args, test_config, false);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
// skip over these tests if newton bond is forced to be on
|
||||
if (lmp->force->newton_bond == 0) {
|
||||
dihedral = lmp->force->dihedral;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton off)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton off)", dihedral->virial, test_config.init_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(dihedral->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton off:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton off)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton off)", dihedral->virial, test_config.run_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
id = lmp->modify->find_compute("sum");
|
||||
energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(dihedral->energy, test_config.run_energy, epsilon);
|
||||
|
||||
// FIXME: this is currently broken ??? for KOKKOS with dihedral style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.dihedral_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(dihedral->energy, energy, epsilon);
|
||||
|
||||
if (print_stats) std::cerr << "run_energy stats, newton off:" << stats << std::endl;
|
||||
}
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(DihedralStyle, numdiff)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("EXTRA-FIX")) GTEST_SKIP();
|
||||
|
||||
@ -527,6 +527,108 @@ TEST(ImproperStyle, omp)
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(ImproperStyle, kokkos_omp)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("KOKKOS")) GTEST_SKIP();
|
||||
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
|
||||
|
||||
LAMMPS::argv args = {"ImproperStyle", "-log", "none", "-echo", "screen", "-nocite",
|
||||
"-k", "on", "t", "4", "-sf", "kk"};
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
LAMMPS *lmp = init_lammps(args, test_config, true);
|
||||
|
||||
std::string output = ::testing::internal::GetCapturedStdout();
|
||||
if (verbose) std::cout << output;
|
||||
|
||||
if (!lmp) {
|
||||
std::cerr << "One or more prerequisite styles with /kk suffix\n"
|
||||
"are not available in this LAMMPS configuration:\n";
|
||||
for (auto &prerequisite : test_config.prerequisites) {
|
||||
std::cerr << prerequisite.first << "_style " << prerequisite.second << "\n";
|
||||
}
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
EXPECT_THAT(output, StartsWith("LAMMPS ("));
|
||||
EXPECT_THAT(output, HasSubstr("Loop time"));
|
||||
|
||||
// abort if running in parallel and not all atoms are local
|
||||
const int nlocal = lmp->atom->nlocal;
|
||||
ASSERT_EQ(lmp->atom->natoms, nlocal);
|
||||
|
||||
// relax error a bit for KOKKOS package
|
||||
double epsilon = 5.0 * test_config.epsilon;
|
||||
|
||||
ErrorStats stats;
|
||||
auto improper = lmp->force->improper;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton on)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton on)", improper->virial, test_config.init_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(improper->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton on)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton on)", improper->virial, test_config.run_stress, 10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
int id = lmp->modify->find_compute("sum");
|
||||
double energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(improper->energy, test_config.run_energy, epsilon);
|
||||
// FIXME: this is currently broken ??? for KOKKOS with improper style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.improper_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(improper->energy, energy, epsilon);
|
||||
if (print_stats) std::cerr << "run_energy stats, newton on: " << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
lmp = init_lammps(args, test_config, false);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
// skip over these tests if newton bond is forced to be on
|
||||
if (lmp->force->newton_bond == 0) {
|
||||
improper = lmp->force->improper;
|
||||
|
||||
EXPECT_FORCES("init_forces (newton off)", lmp->atom, test_config.init_forces, epsilon);
|
||||
EXPECT_STRESS("init_stress (newton off)", improper->virial, test_config.init_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
EXPECT_FP_LE_WITH_EPS(improper->energy, test_config.init_energy, epsilon);
|
||||
if (print_stats) std::cerr << "init_energy stats, newton off:" << stats << std::endl;
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
run_lammps(lmp);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_FORCES("run_forces (newton off)", lmp->atom, test_config.run_forces, 10 * epsilon);
|
||||
EXPECT_STRESS("run_stress (newton off)", improper->virial, test_config.run_stress,
|
||||
10 * epsilon);
|
||||
|
||||
stats.reset();
|
||||
id = lmp->modify->find_compute("sum");
|
||||
energy = lmp->modify->compute[id]->compute_scalar();
|
||||
EXPECT_FP_LE_WITH_EPS(improper->energy, test_config.run_energy, epsilon);
|
||||
// FIXME: this is currently broken ??? for KOKKOS with improper style hybrid
|
||||
// needs to be fixed in the main code somewhere. Not sure where, though.
|
||||
//if (test_config.improper_style.substr(0, 6) != "hybrid")
|
||||
// EXPECT_FP_LE_WITH_EPS(improper->energy, energy, epsilon);
|
||||
if (print_stats) std::cerr << "run_energy stats, newton off:" << stats << std::endl;
|
||||
}
|
||||
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
cleanup_lammps(lmp, test_config);
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(ImproperStyle, numdiff)
|
||||
{
|
||||
if (!LAMMPS::is_installed_pkg("EXTRA-FIX")) GTEST_SKIP();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
lammps_version: 17 Feb 2022
|
||||
date_generated: Fri Mar 18 22:18:02 2022
|
||||
epsilon: 2.5e-13
|
||||
skip_tests:
|
||||
skip_tests: kokkos_omp
|
||||
prerequisites: ! |
|
||||
atom full
|
||||
improper class2
|
||||
|
||||
@ -88,7 +88,7 @@ static void create_molecule_files(const std::string &h2o_filename, const std::st
|
||||
// whether to print verbose output (i.e. not capturing LAMMPS screen output).
|
||||
bool verbose = false;
|
||||
|
||||
const double EPSILON = 5.0e-14;
|
||||
static const double EPSILON = 5.0e-14;
|
||||
|
||||
namespace LAMMPS_NS {
|
||||
using ::testing::Eq;
|
||||
|
||||
@ -32,7 +32,7 @@ using testing::StrEq;
|
||||
|
||||
using utils::split_words;
|
||||
|
||||
const double EPSILON = 5.0e-14;
|
||||
static constexpr double EPSILON = 5.0e-14;
|
||||
|
||||
#define test_name test_info_->name()
|
||||
|
||||
|
||||
@ -11,11 +11,14 @@
|
||||
See the README file in the top-level LAMMPS directory.
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "lmptype.h"
|
||||
#include "tokenizer.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using namespace LAMMPS_NS;
|
||||
using ::testing::Eq;
|
||||
|
||||
@ -334,46 +337,73 @@ TEST(ValueTokenizer, move_assignment)
|
||||
|
||||
TEST(ValueTokenizer, bad_integer)
|
||||
{
|
||||
ValueTokenizer values("f10 f11 f12");
|
||||
ValueTokenizer values("f10 f11 f12 0xff 109951162777 "
|
||||
"36893488147419103232 36893488147419103232");
|
||||
ASSERT_THROW(values.next_int(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_bigint(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_tagint(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_int(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_int(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_tagint(), InvalidIntegerException);
|
||||
ASSERT_THROW(values.next_bigint(), InvalidIntegerException);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, bad_double)
|
||||
{
|
||||
ValueTokenizer values("1a.0");
|
||||
ValueTokenizer values("1a.0 --2.0 2.4d3 -1e20000 1.0e-1.0");
|
||||
ASSERT_THROW(values.next_double(), InvalidFloatException);
|
||||
ASSERT_THROW(values.next_double(), InvalidFloatException);
|
||||
ASSERT_THROW(values.next_double(), InvalidFloatException);
|
||||
ASSERT_THROW(values.next_double(), InvalidFloatException);
|
||||
ASSERT_THROW(values.next_double(), InvalidFloatException);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, valid_int)
|
||||
{
|
||||
ValueTokenizer values("10");
|
||||
ValueTokenizer values(fmt::format("10 {} {}", -MAXSMALLINT - 1, MAXSMALLINT));
|
||||
ASSERT_EQ(values.next_int(), 10);
|
||||
ASSERT_EQ(values.next_int(), -MAXSMALLINT - 1);
|
||||
ASSERT_EQ(values.next_int(), MAXSMALLINT);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, valid_tagint)
|
||||
{
|
||||
ValueTokenizer values("42");
|
||||
ValueTokenizer values(
|
||||
fmt::format("42 {} {} {} {}", -MAXSMALLINT - 1, MAXSMALLINT, -MAXTAGINT - 1, MAXTAGINT));
|
||||
ASSERT_EQ(values.next_tagint(), 42);
|
||||
ASSERT_EQ(values.next_tagint(), -MAXSMALLINT - 1);
|
||||
ASSERT_EQ(values.next_tagint(), MAXSMALLINT);
|
||||
ASSERT_EQ(values.next_tagint(), -MAXTAGINT - 1);
|
||||
ASSERT_EQ(values.next_tagint(), MAXTAGINT);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, valid_bigint)
|
||||
{
|
||||
ValueTokenizer values("42");
|
||||
ValueTokenizer values(
|
||||
fmt::format("42 {} {} {} {}", -MAXSMALLINT - 1, MAXSMALLINT, -MAXBIGINT - 1, MAXBIGINT));
|
||||
ASSERT_EQ(values.next_bigint(), 42);
|
||||
ASSERT_EQ(values.next_bigint(), -MAXSMALLINT - 1);
|
||||
ASSERT_EQ(values.next_bigint(), MAXSMALLINT);
|
||||
ASSERT_EQ(values.next_bigint(), -MAXBIGINT - 1);
|
||||
ASSERT_EQ(values.next_bigint(), MAXBIGINT);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, valid_double)
|
||||
{
|
||||
ValueTokenizer values("3.14");
|
||||
ValueTokenizer values("3.14 -0.00002 .1 0xff " + std::to_string(MAXBIGINT));
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), 3.14);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), -0.00002);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), 0.1);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), 255);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), MAXBIGINT);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, valid_double_with_exponential)
|
||||
{
|
||||
ValueTokenizer values("3.14e22");
|
||||
ValueTokenizer values(fmt::format("3.14e22 {} {}", DBL_MAX, DBL_MIN));
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), 3.14e22);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), DBL_MAX);
|
||||
ASSERT_DOUBLE_EQ(values.next_double(), DBL_MIN);
|
||||
}
|
||||
|
||||
TEST(ValueTokenizer, contains)
|
||||
|
||||
Reference in New Issue
Block a user