From faac18ffd25e565aae796e03bfceb33fff4c72f7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 8 Aug 2020 22:54:17 -0400 Subject: [PATCH] add tester tool for timestep related fixes: integrators, thermostats, force manipulations, constraints --- unittest/force-styles/CMakeLists.txt | 13 + unittest/force-styles/test_config.h | 8 + unittest/force-styles/test_config_reader.cpp | 32 + unittest/force-styles/test_config_reader.h | 3 + unittest/force-styles/test_fix_timestep.cpp | 628 +++++++++++++++++++ 5 files changed, 684 insertions(+) create mode 100644 unittest/force-styles/test_fix_timestep.cpp diff --git a/unittest/force-styles/CMakeLists.txt b/unittest/force-styles/CMakeLists.txt index a1e586eef2..128dc62cff 100644 --- a/unittest/force-styles/CMakeLists.txt +++ b/unittest/force-styles/CMakeLists.txt @@ -114,3 +114,16 @@ foreach(TEST ${KSPACE_TESTS}) add_test(NAME ${TNAME} COMMAND test_pair_style ${TEST} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:$ENV{PYTHONPATH}") endforeach() + +# tester for timestepping fixes +add_executable(test_fix_timestep test_fix_timestep.cpp) +target_link_libraries(test_fix_timestep PRIVATE lammps style_tests) + +# tests for timestep related fixes (time integration, thermostat, force manipulation, constraints/restraints) +file(GLOB FIX_TIMESTEP_TESTS LIST_DIRECTORIES false ${TEST_INPUT_FOLDER}/fix-timestep-*.yaml) +foreach(TEST ${FIX_TIMESTEP_TESTS}) + string(REGEX REPLACE "^.*fix-timestep-(.*)\.yaml" "FixTimestep:\\1" TNAME ${TEST}) + add_test(NAME ${TNAME} COMMAND test_fix_timestep ${TEST} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:$ENV{PYTHONPATH}") +endforeach() + diff --git a/unittest/force-styles/test_config.h b/unittest/force-styles/test_config.h index 3ac838f635..1fae7197be 100644 --- a/unittest/force-styles/test_config.h +++ b/unittest/force-styles/test_config.h @@ -60,6 +60,10 @@ public: stress_t run_stress; std::vector init_forces; std::vector run_forces; + std::vector run_pos; + std::vector restart_pos; + std::vector run_vel; + std::vector restart_vel; TestConfig() : lammps_version(""), date_generated(""), basename(""), epsilon(1.0e-14), input_file(""), @@ -79,6 +83,10 @@ public: extract.clear(); init_forces.clear(); run_forces.clear(); + run_pos.clear(); + restart_pos.clear(); + run_vel.clear(); + restart_vel.clear(); } virtual ~TestConfig(){}; diff --git a/unittest/force-styles/test_config_reader.cpp b/unittest/force-styles/test_config_reader.cpp index 24b08f93e9..ad5054796f 100644 --- a/unittest/force-styles/test_config_reader.cpp +++ b/unittest/force-styles/test_config_reader.cpp @@ -40,6 +40,8 @@ TestConfigReader::TestConfigReader(TestConfig &config) : YamlReader(), config(co consumers["run_stress"] = &TestConfigReader::run_stress; consumers["init_forces"] = &TestConfigReader::init_forces; consumers["run_forces"] = &TestConfigReader::run_forces; + consumers["run_pos"] = &TestConfigReader::run_pos; + consumers["run_vel"] = &TestConfigReader::run_vel; consumers["pair_style"] = &TestConfigReader::pair_style; consumers["pair_coeff"] = &TestConfigReader::pair_coeff; @@ -176,6 +178,36 @@ void TestConfigReader::run_forces(const yaml_event_t &event) } } +void TestConfigReader::run_pos(const yaml_event_t &event) +{ + config.run_pos.clear(); + config.run_pos.resize(config.natoms + 1); + std::stringstream data((char *)event.data.scalar.value); + std::string line; + + while (std::getline(data, line, '\n')) { + int tag; + coord_t xyz; + sscanf(line.c_str(), "%d %lg %lg %lg", &tag, &xyz.x, &xyz.y, &xyz.z); + config.run_pos[tag] = xyz; + } +} + +void TestConfigReader::run_vel(const yaml_event_t &event) +{ + config.run_vel.clear(); + config.run_vel.resize(config.natoms + 1); + std::stringstream data((char *)event.data.scalar.value); + std::string line; + + while (std::getline(data, line, '\n')) { + int tag; + coord_t xyz; + sscanf(line.c_str(), "%d %lg %lg %lg", &tag, &xyz.x, &xyz.y, &xyz.z); + config.run_vel[tag] = xyz; + } +} + void TestConfigReader::pair_style(const yaml_event_t &event) { config.pair_style = (char *)event.data.scalar.value; diff --git a/unittest/force-styles/test_config_reader.h b/unittest/force-styles/test_config_reader.h index f39706db90..bea43000e0 100644 --- a/unittest/force-styles/test_config_reader.h +++ b/unittest/force-styles/test_config_reader.h @@ -36,6 +36,9 @@ public: void run_stress(const yaml_event_t &event); void init_forces(const yaml_event_t &event); void run_forces(const yaml_event_t &event); + void run_pos(const yaml_event_t &event); + void run_vel(const yaml_event_t &event); + void fix_style(const yaml_event_t &event); void pair_style(const yaml_event_t &event); void pair_coeff(const yaml_event_t &event); void bond_style(const yaml_event_t &event); diff --git a/unittest/force-styles/test_fix_timestep.cpp b/unittest/force-styles/test_fix_timestep.cpp new file mode 100644 index 0000000000..bdd7fc17c7 --- /dev/null +++ b/unittest/force-styles/test_fix_timestep.cpp @@ -0,0 +1,628 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +// unit tests for pair styles intended for molecular systems + +#include "error_stats.h" +#include "test_config.h" +#include "test_config_reader.h" +#include "test_main.h" +#include "yaml_reader.h" +#include "yaml_writer.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "atom.h" +#include "compute.h" +#include "fix.h" +#include "fmt/format.h" +#include "force.h" +#include "info.h" +#include "input.h" +#include "kspace.h" +#include "lammps.h" +#include "modify.h" +#include "pair.h" +#include "universe.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using ::testing::HasSubstr; +using ::testing::StartsWith; + +using namespace LAMMPS_NS; + +static void delete_file(const std::string &filename) +{ + remove(filename.c_str()); +}; + +void cleanup_lammps(LAMMPS *lmp, const TestConfig &cfg) +{ + delete_file(cfg.basename + ".restart"); + delete lmp; + lmp = nullptr; +} + +LAMMPS *init_lammps(int argc, char **argv, const TestConfig &cfg, const bool use_respa = false) +{ + LAMMPS *lmp; + + lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); + + // check if prerequisite styles are available + Info *info = new Info(lmp); + int nfail = 0; + for (auto &prerequisite : cfg.prerequisites) { + std::string style = prerequisite.second; + + // this is a test for fix styles, so if the suffixed + // version is not available, there is no reason to test. + if (prerequisite.first == "fix") { + if (lmp->suffix_enable) { + style += "/"; + style += lmp->suffix; + } + } + + if (!info->has_style(prerequisite.first, style)) ++nfail; + } + delete info; + if (nfail > 0) { + cleanup_lammps(lmp, cfg); + return nullptr; + } + + // utility lambda to improve readability + auto command = [&](const std::string &line) { + lmp->input->one(line.c_str()); + }; + + command("variable input_dir index " + INPUT_FOLDER); + for (auto &pre_command : cfg.pre_commands) + command(pre_command); + + std::string input_file = INPUT_FOLDER + PATH_SEP + cfg.input_file; + lmp->input->file(input_file.c_str()); + + if (use_respa) command("run_style respa 2 1 bond 1 pair 2"); + + // set up molecular system force field + + command("pair_style lj/cut 8.0"); + command("pair_coeff 1 1 0.02 2.5"); + command("pair_coeff 2 2 0.005 1.0"); + command("pair_coeff 2 4 0.005 0.5"); + command("pair_coeff 3 3 0.02 3.2"); + command("pair_coeff 4 4 0.015 3.1"); + command("pair_coeff 5 5 0.015 3.1"); + command("bond_style harmonic"); + command("bond_coeff 1 250.0 1.5"); + command("bond_coeff 2 300.0 1.1"); + command("bond_coeff 3 350.0 1.3"); + command("bond_coeff 4 650.0 1.2"); + command("bond_coeff 5 450.0 1.0"); + command("angle_style harmonic"); + command("angle_coeff 1 75.0 110.1"); + command("angle_coeff 2 45.0 111.0"); + command("angle_coeff 3 50.0 120.0"); + command("angle_coeff 4 100.0 108.5"); + command("group solute molecule 1:2"); + command("group solvent molecule 3:5"); + + for (auto &post_command : cfg.post_commands) + command(post_command); + + command("timestep 0.25"); + command("run 0 post no"); + command("thermo 2"); + command("run 4 post no"); + command("write_restart " + cfg.basename + ".restart"); + command("run 4 post no"); + return lmp; +} + +void restart_lammps(LAMMPS *lmp, const TestConfig &cfg, bool use_rmass, bool use_respa) +{ + // utility lambda to improve readability + auto command = [&](const std::string &line) { + lmp->input->one(line.c_str()); + }; + + command("clear"); + command("read_restart " + cfg.basename + ".restart"); + + if (use_rmass) { + command("fix rmass all property/atom rmass ghost yes"); + for (int i = 0; i < lmp->atom->ntypes; ++i) + command(fmt::format("set type {} mass {}", i + 1, lmp->atom->mass[i + 1])); + } + + if (use_respa) command("run_style respa 2 1 bond 1 pair 2"); + + for (auto &post_command : cfg.post_commands) + command(post_command); + + command("thermo 2"); + command("run 4 post no"); +} + +// re-generate yaml file with current settings. + +void generate_yaml_file(const char *outfile, const TestConfig &config) +{ + // initialize system geometry + const char *args[] = {"FixIntegrate", "-log", "none", "-echo", "screen", "-nocite"}; + + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + LAMMPS *lmp = init_lammps(argc, argv, config); + if (!lmp) { + std::cerr << "One or more prerequisite styles are not available " + "in this LAMMPS configuration:\n"; + for (auto prerequisite : config.prerequisites) { + std::cerr << prerequisite.first << "_style " << prerequisite.second << "\n"; + } + return; + } + + const int natoms = lmp->atom->natoms; + std::string block(""); + YamlWriter writer(outfile); + + // lammps_version + writer.emit("lammps_version", lmp->universe->version); + + // date_generated + std::time_t now = time(NULL); + block = ctime(&now); + block = block.substr(0, block.find("\n") - 1); + writer.emit("date_generated", block); + + // epsilon + writer.emit("epsilon", config.epsilon); + + // prerequisites + block.clear(); + for (auto &prerequisite : config.prerequisites) { + block += prerequisite.first + " " + prerequisite.second + "\n"; + } + writer.emit_block("prerequisites", block); + + // pre_commands + block.clear(); + for (auto &command : config.pre_commands) { + block += command + "\n"; + } + writer.emit_block("pre_commands", block); + + // post_commands + block.clear(); + for (auto &command : config.post_commands) { + block += command + "\n"; + } + writer.emit_block("post_commands", block); + + // input_file + writer.emit("input_file", config.input_file); + + // natoms + writer.emit("natoms", natoms); + + // run_pos + block.clear(); + auto x = lmp->atom->x; + auto tag = lmp->atom->tag; + for (int i = 1; i <= natoms; ++i) { + const int j = lmp->atom->map(i); + block += fmt::format("{:3} {:23.16e} {:23.16e} {:23.16e}\n", i, x[j][0], x[j][1], x[j][2]); + } + writer.emit_block("run_pos", block); + + // run_vel + block.clear(); + auto v = lmp->atom->v; + for (int i = 1; i <= natoms; ++i) { + const int j = lmp->atom->map(i); + block += fmt::format("{:3} {:23.16e} {:23.16e} {:23.16e}\n", i, v[j][0], v[j][1], v[j][2]); + } + writer.emit_block("run_vel", block); + cleanup_lammps(lmp, config); + return; +} + +TEST(FixTimestep, plain) +{ + if (!LAMMPS::is_installed_pkg("MOLECULE")) GTEST_SKIP(); + const char *args[] = {"FixTimestep", "-log", "none", "-echo", "screen", "-nocite"}; + + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + LAMMPS *lmp = init_lammps(argc, argv, test_config, false); + + std::string output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + if (!lmp) { + std::cerr << "One or more prerequisite styles 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); + + double epsilon = test_config.epsilon; + + auto x = lmp->atom->x; + auto tag = lmp->atom->tag; + ErrorStats stats; + stats.reset(); + const std::vector &x_ref = test_config.run_pos; + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, normal_run, verlet: " << stats << std::endl; + + auto v = lmp->atom->v; + const std::vector &v_ref = test_config.run_vel; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, normal_run, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, false, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, restart, verlet: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, restart, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, true, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, rmass, verlet: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, rmass, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + cleanup_lammps(lmp, test_config); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + ::testing::internal::CaptureStdout(); + lmp = init_lammps(argc, argv, test_config, false); + + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, normal_run, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, normal_run, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, false, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, restart, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, restart, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, true, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, rmass, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, rmass, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + cleanup_lammps(lmp, test_config); + if (!verbose) ::testing::internal::GetCapturedStdout(); +}; + +TEST(FixTimestep, omp) +{ + if (!LAMMPS::is_installed_pkg("USER-OMP")) GTEST_SKIP(); + if (!LAMMPS::is_installed_pkg("MOLECULE")) GTEST_SKIP(); + const char *args[] = {"FixTimestep", "-log", "none", "-echo", "screen", "-nocite", + "-pk", "omp", "4", "-sf", "omp"}; + + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + LAMMPS *lmp = init_lammps(argc, argv, test_config, false); + + std::string output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + if (!lmp) { + std::cerr << "One or more prerequisite styles 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); + + double epsilon = test_config.epsilon; + + auto x = lmp->atom->x; + auto tag = lmp->atom->tag; + ErrorStats stats; + stats.reset(); + const std::vector &x_ref = test_config.run_pos; + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, normal_run, verlet: " << stats << std::endl; + + auto v = lmp->atom->v; + const std::vector &v_ref = test_config.run_vel; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, normal_run, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, false, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, restart, verlet: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, restart, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, true, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, rmass, verlet: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, rmass, verlet: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + cleanup_lammps(lmp, test_config); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + ::testing::internal::CaptureStdout(); + lmp = init_lammps(argc, argv, test_config, false); + + output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, normal_run, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, normal_run, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, false, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, restart, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, restart, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + restart_lammps(lmp, test_config, true, false); + if (!verbose) ::testing::internal::GetCapturedStdout(); + + x = lmp->atom->x; + tag = lmp->atom->tag; + stats.reset(); + ASSERT_EQ(nlocal + 1, x_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(x[i][0], x_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][1], x_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(x[i][2], x_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_pos, rmass, respa: " << stats << std::endl; + + v = lmp->atom->v; + ASSERT_EQ(nlocal + 1, v_ref.size()); + for (int i = 0; i < nlocal; ++i) { + EXPECT_FP_LE_WITH_EPS(v[i][0], v_ref[tag[i]].x, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][1], v_ref[tag[i]].y, epsilon); + EXPECT_FP_LE_WITH_EPS(v[i][2], v_ref[tag[i]].z, epsilon); + } + if (print_stats) std::cerr << "run_vel, rmass, respa: " << stats << std::endl; + + if (!verbose) ::testing::internal::CaptureStdout(); + cleanup_lammps(lmp, test_config); + if (!verbose) ::testing::internal::GetCapturedStdout(); +};