From 180e8168862f0d50cd5a6cbed8e5f1a6c8553708 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 23 Mar 2021 19:55:08 -0400 Subject: [PATCH 01/21] Simplify PythonPackage tests --- unittest/python/test_python_package.cpp | 44 +++++++++++++------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index cba77ee2b0..5974592997 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -40,6 +40,8 @@ protected: LAMMPS *lmp; Info *info; + void command(const std::string &line) { lmp->input->one(line.c_str()); } + void SetUp() override { const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"}; @@ -51,16 +53,16 @@ protected: ASSERT_NE(lmp, nullptr); info = new Info(lmp); if (!verbose) ::testing::internal::CaptureStdout(); - lmp->input->one("units real"); - lmp->input->one("dimension 3"); - lmp->input->one("region box block -4 4 -4 4 -4 4"); - lmp->input->one("create_box 1 box"); - lmp->input->one("create_atoms 1 single 0.0 0.0 0.0 units box"); - lmp->input->one("create_atoms 1 single 1.9 -1.9 1.9999 units box"); - lmp->input->one("pair_style zero 2.0"); - lmp->input->one("pair_coeff * *"); - lmp->input->one("mass * 1.0"); - lmp->input->one("variable input_dir index " + INPUT_FOLDER); + command("units real"); + command("dimension 3"); + command("region box block -4 4 -4 4 -4 4"); + command("create_box 1 box"); + command("create_atoms 1 single 0.0 0.0 0.0 units box"); + command("create_atoms 1 single 1.9 -1.9 1.9999 units box"); + command("pair_style zero 2.0"); + command("pair_coeff * *"); + command("mass * 1.0"); + command("variable input_dir index " + INPUT_FOLDER); if (!verbose) ::testing::internal::GetCapturedStdout(); } @@ -78,31 +80,31 @@ TEST_F(PythonPackageTest, python_invoke) if (!info->has_style("command", "python")) GTEST_SKIP(); // execute python function from file if (!verbose) ::testing::internal::CaptureStdout(); - lmp->input->one("python printnum file ${input_dir}/func.py"); + command("python printnum file ${input_dir}/func.py"); if (!verbose) ::testing::internal::GetCapturedStdout(); ::testing::internal::CaptureStdout(); - lmp->input->one("python printnum invoke"); + command("python printnum invoke"); std::string output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; ASSERT_THAT(output, MatchesRegex("python.*2.25.*")); // execute another python function from same file if (!verbose) ::testing::internal::CaptureStdout(); - lmp->input->one("python printtxt exists"); + command("python printtxt exists"); if (!verbose) ::testing::internal::GetCapturedStdout(); ::testing::internal::CaptureStdout(); - lmp->input->one("python printtxt invoke"); + command("python printtxt invoke"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; ASSERT_THAT(output, MatchesRegex("python.*sometext.*")); // execute python function that uses the LAMMPS python module if (!verbose) ::testing::internal::CaptureStdout(); - lmp->input->one("variable idx equal 2.25"); - lmp->input->one("python getidxvar input 1 SELF format p exists"); + command("variable idx equal 2.25"); + command("python getidxvar input 1 SELF format p exists"); if (!verbose) ::testing::internal::GetCapturedStdout(); ::testing::internal::CaptureStdout(); - lmp->input->one("python getidxvar invoke"); + command("python getidxvar invoke"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; ASSERT_THAT(output, MatchesRegex("python.*2.25.*")); @@ -112,12 +114,12 @@ TEST_F(PythonPackageTest, python_variable) { if (!info->has_style("command", "python")) GTEST_SKIP(); if (!verbose) ::testing::internal::CaptureStdout(); - lmp->input->one("variable sq python square"); - lmp->input->one("variable val index 1.5"); - lmp->input->one("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py"); + command("variable sq python square"); + command("variable val index 1.5"); + command("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py"); if (!verbose) ::testing::internal::GetCapturedStdout(); ::testing::internal::CaptureStdout(); - lmp->input->one("print \"${sq}\""); + command("print \"${sq}\""); std::string output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; ASSERT_THAT(output, MatchesRegex("print.*2.25.*")); From 6b24006d43f71f5368d3feedd66e7f48e29de9f1 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 23 Mar 2021 19:56:18 -0400 Subject: [PATCH 02/21] Use Info::has_package to check for PYTHON support --- unittest/python/test_python_package.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index 5974592997..110ea24dbc 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -77,7 +77,7 @@ protected: TEST_F(PythonPackageTest, python_invoke) { - if (!info->has_style("command", "python")) GTEST_SKIP(); + if (!info->has_package("PYTHON")) GTEST_SKIP(); // execute python function from file if (!verbose) ::testing::internal::CaptureStdout(); command("python printnum file ${input_dir}/func.py"); @@ -112,7 +112,7 @@ TEST_F(PythonPackageTest, python_invoke) TEST_F(PythonPackageTest, python_variable) { - if (!info->has_style("command", "python")) GTEST_SKIP(); + if (!info->has_package("PYTHON")) GTEST_SKIP(); if (!verbose) ::testing::internal::CaptureStdout(); command("variable sq python square"); command("variable val index 1.5"); From 359a369573121f9101c0651384a9efd835ffba39 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 23 Mar 2021 19:57:45 -0400 Subject: [PATCH 03/21] Ensure that global Py_UnbufferedStdioFlag is set when PYTHONUNBUFFERED=1 --- src/PYTHON/python_impl.cpp | 10 ++++++++++ unittest/python/CMakeLists.txt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index d1f602a1ea..1f45ca6635 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -55,6 +55,16 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) nfunc = 0; pfuncs = nullptr; + + // check for PYTHONUNBUFFERED environment variable + const char * PYTHONUNBUFFERED = getenv("PYTHONUNBUFFERED"); + + if (PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0) { + // Python Global configuration variable + // Force the stdout and stderr streams to be unbuffered. + Py_UnbufferedStdioFlag = 1; + } + // one-time initialization of Python interpreter // pyMain stores pointer to main module external_interpreter = Py_IsInitialized(); diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index d508602c93..d1db17c941 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(test_python_package test_python_package.cpp) target_link_libraries(test_python_package PRIVATE lammps GTest::GMock GTest::GTest) target_compile_definitions(test_python_package PRIVATE -DTEST_INPUT_FOLDER=${TEST_INPUT_FOLDER}) add_test(NAME PythonPackage COMMAND test_python_package WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}") +set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONUNBUFFERED=1") # we must have shared libraries enabled for testing the python module if(NOT BUILD_SHARED_LIBS) From 23c8d8ccfb9ec206e7acf6035c3adcd0919929b6 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 23 Mar 2021 20:13:39 -0400 Subject: [PATCH 04/21] Use HasSubstr since output order is dependent on buffering --- unittest/python/test_python_package.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index 110ea24dbc..723271c589 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -34,6 +34,7 @@ using LAMMPS_NS::utils::split_words; namespace LAMMPS_NS { using ::testing::MatchesRegex; using ::testing::StrEq; +using ::testing::HasSubstr; class PythonPackageTest : public ::testing::Test { protected: @@ -86,7 +87,7 @@ TEST_F(PythonPackageTest, python_invoke) command("python printnum invoke"); std::string output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; - ASSERT_THAT(output, MatchesRegex("python.*2.25.*")); + ASSERT_THAT(output, HasSubstr("2.25\n")); // execute another python function from same file if (!verbose) ::testing::internal::CaptureStdout(); @@ -96,7 +97,7 @@ TEST_F(PythonPackageTest, python_invoke) command("python printtxt invoke"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; - ASSERT_THAT(output, MatchesRegex("python.*sometext.*")); + ASSERT_THAT(output, HasSubstr("sometext\n")); // execute python function that uses the LAMMPS python module if (!verbose) ::testing::internal::CaptureStdout(); @@ -107,7 +108,7 @@ TEST_F(PythonPackageTest, python_invoke) command("python getidxvar invoke"); output = ::testing::internal::GetCapturedStdout(); if (verbose) std::cout << output; - ASSERT_THAT(output, MatchesRegex("python.*2.25.*")); + ASSERT_THAT(output, HasSubstr("2.25\n")); } TEST_F(PythonPackageTest, python_variable) From 8790ecc141cfe52f01d3ec77bcf687bb3808c9da Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 11:18:21 -0400 Subject: [PATCH 05/21] Refactor existing tests --- unittest/python/test_python_package.cpp | 130 ++++++++++++++---------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index 723271c589..137278a9d2 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -20,6 +20,7 @@ #include #include #include +#include // location of '*.py' files required by tests #define STRINGIFY(val) XSTR(val) @@ -43,86 +44,107 @@ protected: void command(const std::string &line) { lmp->input->one(line.c_str()); } + void HIDE_OUTPUT(std::function f) { + if (!verbose) ::testing::internal::CaptureStdout(); + f(); + if (!verbose) ::testing::internal::GetCapturedStdout(); + } + + std::string CAPTURE_OUTPUT(std::function f) { + ::testing::internal::CaptureStdout(); + f(); + auto output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + return output; + } + void SetUp() override { const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"}; char **argv = (char **)args; int argc = sizeof(args) / sizeof(char *); - if (!verbose) ::testing::internal::CaptureStdout(); - lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); - if (!verbose) ::testing::internal::GetCapturedStdout(); + HIDE_OUTPUT([&] { + lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); + }); ASSERT_NE(lmp, nullptr); info = new Info(lmp); - if (!verbose) ::testing::internal::CaptureStdout(); - command("units real"); - command("dimension 3"); - command("region box block -4 4 -4 4 -4 4"); - command("create_box 1 box"); - command("create_atoms 1 single 0.0 0.0 0.0 units box"); - command("create_atoms 1 single 1.9 -1.9 1.9999 units box"); - command("pair_style zero 2.0"); - command("pair_coeff * *"); - command("mass * 1.0"); - command("variable input_dir index " + INPUT_FOLDER); - if (!verbose) ::testing::internal::GetCapturedStdout(); + if (!info->has_package("PYTHON")) GTEST_SKIP(); + HIDE_OUTPUT([&] { + command("units real"); + command("dimension 3"); + command("region box block -4 4 -4 4 -4 4"); + command("create_box 1 box"); + command("create_atoms 1 single 0.0 0.0 0.0 units box"); + command("create_atoms 1 single 1.9 -1.9 1.9999 units box"); + command("pair_style zero 2.0"); + command("pair_coeff * *"); + command("mass * 1.0"); + command("variable input_dir index " + INPUT_FOLDER); + }); } void TearDown() override { - if (!verbose) ::testing::internal::CaptureStdout(); - delete info; - delete lmp; - if (!verbose) ::testing::internal::GetCapturedStdout(); + HIDE_OUTPUT([&] { + delete info; + delete lmp; + info = nullptr; + lmp = nullptr; + }); } }; -TEST_F(PythonPackageTest, python_invoke) +TEST_F(PythonPackageTest, InvokeFunctionFromFile) { - if (!info->has_package("PYTHON")) GTEST_SKIP(); // execute python function from file - if (!verbose) ::testing::internal::CaptureStdout(); - command("python printnum file ${input_dir}/func.py"); - if (!verbose) ::testing::internal::GetCapturedStdout(); - ::testing::internal::CaptureStdout(); - command("python printnum invoke"); - std::string output = ::testing::internal::GetCapturedStdout(); - if (verbose) std::cout << output; + HIDE_OUTPUT([&] { + command("python printnum file ${input_dir}/func.py"); + }); + + auto output = CAPTURE_OUTPUT([&]() { + command("python printnum invoke"); + }); ASSERT_THAT(output, HasSubstr("2.25\n")); +} +TEST_F(PythonPackageTest, InvokeOtherFunctionFromFile) +{ // execute another python function from same file - if (!verbose) ::testing::internal::CaptureStdout(); - command("python printtxt exists"); - if (!verbose) ::testing::internal::GetCapturedStdout(); - ::testing::internal::CaptureStdout(); - command("python printtxt invoke"); - output = ::testing::internal::GetCapturedStdout(); - if (verbose) std::cout << output; - ASSERT_THAT(output, HasSubstr("sometext\n")); + HIDE_OUTPUT([&] { + command("python printnum file ${input_dir}/func.py"); + command("python printtxt exists"); + }); + auto output = CAPTURE_OUTPUT([&] { + command("python printtxt invoke"); + }); + ASSERT_THAT(output, HasSubstr("sometext\n")); +} + +TEST_F(PythonPackageTest, InvokeFunctionThatUsesLAMMPSModule) +{ // execute python function that uses the LAMMPS python module - if (!verbose) ::testing::internal::CaptureStdout(); - command("variable idx equal 2.25"); - command("python getidxvar input 1 SELF format p exists"); - if (!verbose) ::testing::internal::GetCapturedStdout(); - ::testing::internal::CaptureStdout(); - command("python getidxvar invoke"); - output = ::testing::internal::GetCapturedStdout(); - if (verbose) std::cout << output; + HIDE_OUTPUT([&] { + command("python printnum file ${input_dir}/func.py"); + command("variable idx equal 2.25"); + command("python getidxvar input 1 SELF format p exists"); + }); + auto output = CAPTURE_OUTPUT([&] { + command("python getidxvar invoke"); + }); ASSERT_THAT(output, HasSubstr("2.25\n")); } TEST_F(PythonPackageTest, python_variable) { - if (!info->has_package("PYTHON")) GTEST_SKIP(); - if (!verbose) ::testing::internal::CaptureStdout(); - command("variable sq python square"); - command("variable val index 1.5"); - command("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py"); - if (!verbose) ::testing::internal::GetCapturedStdout(); - ::testing::internal::CaptureStdout(); - command("print \"${sq}\""); - std::string output = ::testing::internal::GetCapturedStdout(); - if (verbose) std::cout << output; + HIDE_OUTPUT([&] { + command("variable sq python square"); + command("variable val index 1.5"); + command("python square input 1 v_val return v_sq format ff file ${input_dir}/func.py"); + }); + std::string output = CAPTURE_OUTPUT([&] { + command("print \"${sq}\""); + }); ASSERT_THAT(output, MatchesRegex("print.*2.25.*")); } From 9d3e37b1028b16ba14c3fed101e10036440333fd Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 12:43:47 -0400 Subject: [PATCH 06/21] Add more python variable tests --- unittest/python/func.py | 10 ++-- unittest/python/test_python_package.cpp | 65 ++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/unittest/python/func.py b/unittest/python/func.py index 27704660a6..cc2269c435 100644 --- a/unittest/python/func.py +++ b/unittest/python/func.py @@ -4,6 +4,11 @@ from __future__ import print_function def square(val): return val*val +def bool_to_val(txt): + if txt.upper() in ["TRUE", "YES"]: + return 1.0 + return 0.0 + def printnum(): print("2.25") @@ -11,8 +16,7 @@ def printtxt(): print("sometext") def getidxvar(lmpptr): - from lammps import lammps, LMP_VAR_EQUAL + from lammps import lammps lmp = lammps(ptr=lmpptr) - - val = lmp.extract_variable("idx",None,LMP_VAR_EQUAL) + val = lmp.extract_variable("idx") print(val) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index 137278a9d2..a7cf52c74e 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -14,6 +14,7 @@ #include "atom.h" #include "info.h" #include "input.h" +#include "variable.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -58,6 +59,13 @@ protected: return output; } + double get_variable_value(const std::string & name) { + char * str = utils::strdup(fmt::format("v_{}", name)); + double value = lmp->input->variable->compute_equal(str); + delete [] str; + return value; + } + void SetUp() override { const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"}; @@ -100,13 +108,67 @@ TEST_F(PythonPackageTest, InvokeFunctionFromFile) HIDE_OUTPUT([&] { command("python printnum file ${input_dir}/func.py"); }); - + auto output = CAPTURE_OUTPUT([&]() { command("python printnum invoke"); }); ASSERT_THAT(output, HasSubstr("2.25\n")); } +TEST_F(PythonPackageTest, InvokeFunctionPassInt) +{ + // execute python function, passing integer as argument + HIDE_OUTPUT([&] { + command("variable sq python square"); + command("python square input 1 2 format ii return v_sq file ${input_dir}/func.py"); + }); + + ASSERT_EQ(get_variable_value("sq"), 4.0); +} + +TEST_F(PythonPackageTest, InvokeFunctionPassFloat) +{ + // execute python function, passing float as argument + HIDE_OUTPUT([&] { + command("variable sq python square"); + command("python square input 1 2.5 format ff return v_sq file ${input_dir}/func.py"); + }); + + ASSERT_EQ(get_variable_value("sq"), 6.25); +} + +TEST_F(PythonPackageTest, InvokeFunctionPassString) +{ + // execute python function, passing string as argument + HIDE_OUTPUT([&] { + command("variable val python bool_to_val"); + command("python bool_to_val input 1 \"true\" format sf return v_val file ${input_dir}/func.py"); + }); + + ASSERT_EQ(get_variable_value("val"), 1.0); +} + +TEST_F(PythonPackageTest, InvokeFunctionPassStringVariable) +{ + // execute python function, passing string variable as argument + HIDE_OUTPUT([&] { + command("variable val python bool_to_val"); + command("python bool_to_val input 1 v_str format sf return v_val file ${input_dir}/func.py"); + }); + + HIDE_OUTPUT([&] { + command("variable str string \"true\""); + }); + + ASSERT_EQ(get_variable_value("val"), 1.0); + + HIDE_OUTPUT([&] { + command("variable str string \"false\""); + }); + + ASSERT_EQ(get_variable_value("val"), 0.0); +} + TEST_F(PythonPackageTest, InvokeOtherFunctionFromFile) { // execute another python function from same file @@ -137,6 +199,7 @@ TEST_F(PythonPackageTest, InvokeFunctionThatUsesLAMMPSModule) TEST_F(PythonPackageTest, python_variable) { + // define variable that evaluates a python function HIDE_OUTPUT([&] { command("variable sq python square"); command("variable val index 1.5"); From 1c9c46d2c1811531a18b05b10ec0f33f8104ecee Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 15:42:38 -0400 Subject: [PATCH 07/21] Add tests to cover python command --- src/PYTHON/python_impl.cpp | 1 + unittest/python/func.py | 8 ++ unittest/python/run.py | 2 + unittest/python/test_python_package.cpp | 100 +++++++++++++++++++++++- 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 unittest/python/run.py diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index 1f45ca6635..4c43ca3744 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -506,6 +506,7 @@ int PythonImpl::create_entry(char *name) "cannot be used unless output is a string"); pfuncs[ifunc].length_longstr = length_longstr; pfuncs[ifunc].longstr = new char[length_longstr+1]; + pfuncs[ifunc].longstr[length_longstr] = '\0'; } if (strstr(ostr,"v_") != ostr) error->all(FLERR,"Invalid python command"); diff --git a/unittest/python/func.py b/unittest/python/func.py index cc2269c435..cf8db41670 100644 --- a/unittest/python/func.py +++ b/unittest/python/func.py @@ -9,6 +9,11 @@ def bool_to_val(txt): return 1.0 return 0.0 +def val_to_bool(val): + if val != 0: + return "True" + return "False" + def printnum(): print("2.25") @@ -20,3 +25,6 @@ def getidxvar(lmpptr): lmp = lammps(ptr=lmpptr) val = lmp.extract_variable("idx") print(val) + +def longstr(): + return "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus." diff --git a/unittest/python/run.py b/unittest/python/run.py new file mode 100644 index 0000000000..7cdb205f50 --- /dev/null +++ b/unittest/python/run.py @@ -0,0 +1,2 @@ +from __future__ import print_function +print("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus.") diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index a7cf52c74e..f7a184b6b3 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -15,6 +15,7 @@ #include "info.h" #include "input.h" #include "variable.h" +#include "library.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -31,11 +32,14 @@ std::string INPUT_FOLDER = STRINGIFY(TEST_INPUT_FOLDER); // whether to print verbose output (i.e. not capturing LAMMPS screen output). bool verbose = false; +const char * LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus."; + using LAMMPS_NS::utils::split_words; namespace LAMMPS_NS { using ::testing::MatchesRegex; using ::testing::StrEq; +using ::testing::Eq; using ::testing::HasSubstr; class PythonPackageTest : public ::testing::Test { @@ -45,6 +49,8 @@ protected: void command(const std::string &line) { lmp->input->one(line.c_str()); } + void command_string(const std::string &lines) { lammps_commands_string(lmp, lines.c_str()); } + void HIDE_OUTPUT(std::function f) { if (!verbose) ::testing::internal::CaptureStdout(); f(); @@ -66,6 +72,10 @@ protected: return value; } + std::string get_variable_string(const std::string & name) { + return lmp->input->variable->retrieve(name.c_str()); + } + void SetUp() override { const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"}; @@ -121,6 +131,7 @@ TEST_F(PythonPackageTest, InvokeFunctionPassInt) HIDE_OUTPUT([&] { command("variable sq python square"); command("python square input 1 2 format ii return v_sq file ${input_dir}/func.py"); + command("python square invoke"); }); ASSERT_EQ(get_variable_value("sq"), 4.0); @@ -169,6 +180,38 @@ TEST_F(PythonPackageTest, InvokeFunctionPassStringVariable) ASSERT_EQ(get_variable_value("val"), 0.0); } +TEST_F(PythonPackageTest, InvokeStringFunction) +{ + // execute python function, passing string variable as argument + HIDE_OUTPUT([&] { + command("variable str python val_to_bool"); + command("python val_to_bool input 1 v_val format is return v_str file ${input_dir}/func.py"); + }); + + HIDE_OUTPUT([&] { + command("variable val equal 0"); + }); + + ASSERT_THAT(get_variable_string("str"), StrEq("False")); + + HIDE_OUTPUT([&] { + command("variable val equal 1"); + }); + + ASSERT_THAT(get_variable_string("str"), StrEq("True")); +} + +TEST_F(PythonPackageTest, InvokeLongStringFunction) +{ + // execute python function, passing string variable as argument + HIDE_OUTPUT([&] { + command("variable str python longstr"); + command("python longstr format s length 72 return v_str file ${input_dir}/func.py"); + }); + + ASSERT_THAT(get_variable_string("str"), StrEq(LOREM_IPSUM)); +} + TEST_F(PythonPackageTest, InvokeOtherFunctionFromFile) { // execute another python function from same file @@ -211,6 +254,61 @@ TEST_F(PythonPackageTest, python_variable) ASSERT_THAT(output, MatchesRegex("print.*2.25.*")); } +TEST_F(PythonPackageTest, InlineFunction) +{ + // define variable that evaluates a python function + HIDE_OUTPUT([&] { + command("variable fact python factorial"); + command("python factorial input 1 v_n return v_fact format ii here \"\"\"\n" + "def factorial(n):\n" + " if n == 0 or n == 1: return 1\n" + " return n*factorial(n-1)\n" + "\"\"\""); + }); + + HIDE_OUTPUT([&] { + command("variable n equal 1"); + }); + + ASSERT_EQ(get_variable_value("fact"), 1.0); + + HIDE_OUTPUT([&] { + command("variable n equal 2"); + }); + + ASSERT_EQ(get_variable_value("fact"), 2.0); + + HIDE_OUTPUT([&] { + command("variable n equal 3"); + }); + + ASSERT_EQ(get_variable_value("fact"), 6.0); +} + +TEST_F(PythonPackageTest, RunSource) +{ + // execute python script from file + auto output = CAPTURE_OUTPUT([&] { + command("python xyz source ${input_dir}/run.py"); + }); + + ASSERT_THAT(output, HasSubstr(LOREM_IPSUM)); +} + +TEST_F(PythonPackageTest, RunSourceInline) +{ + // execute inline python script + auto output = CAPTURE_OUTPUT([&] { + command("python xyz source \"\"\"\n" + "from __future__ import print_function\n" + "print(2+2)\n" + "\"\"\"" + ); + }); + + ASSERT_THAT(output, HasSubstr("4")); +} + } // namespace LAMMPS_NS int main(int argc, char **argv) @@ -220,7 +318,7 @@ int main(int argc, char **argv) // handle arguments passed via environment variable if (const char *var = getenv("TEST_ARGS")) { - std::vector env = split_words(var); + auto env = split_words(var); for (auto arg : env) { if (arg == "-v") { verbose = true; From b15502ddc861526a72f4041896fbabc5e0b64337 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 15:53:00 -0400 Subject: [PATCH 08/21] Add utils::split_lines --- src/utils.cpp | 8 ++++++++ src/utils.h | 6 ++++++ unittest/utils/test_utils.cpp | 17 +++++++++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/utils.cpp b/src/utils.cpp index b5576b9f27..e733d7eaae 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -851,6 +851,14 @@ std::vector utils::split_words(const std::string &text) return list; } +/* ---------------------------------------------------------------------- + Convert multi-line string into lines +------------------------------------------------------------------------- */ +std::vector utils::split_lines(const std::string &text) +{ + return Tokenizer(text, "\n").as_vector(); +} + /* ---------------------------------------------------------------------- Return whether string is a valid integer number ------------------------------------------------------------------------- */ diff --git a/src/utils.h b/src/utils.h index eab81f1343..ec4dd6ae85 100644 --- a/src/utils.h +++ b/src/utils.h @@ -321,6 +321,12 @@ namespace LAMMPS_NS { std::vector split_words(const std::string &text); + /** Take multi-line text and split into lines + * + * \param text string that should be split + * \return STL vector with the lines */ + std::vector split_lines(const std::string &text); + /** Check if string can be converted to valid integer * * \param str string that should be checked diff --git a/unittest/utils/test_utils.cpp b/unittest/utils/test_utils.cpp index 2fa17a5e5a..2a87f1c347 100644 --- a/unittest/utils/test_utils.cpp +++ b/unittest/utils/test_utils.cpp @@ -113,7 +113,7 @@ TEST(Utils, count_words_with_extra_spaces) TEST(Utils, split_words_simple) { - std::vector list = utils::split_words("one two three"); + auto list = utils::split_words("one two three"); ASSERT_EQ(list.size(), 3); ASSERT_THAT(list[0], StrEq("one")); ASSERT_THAT(list[1], StrEq("two")); @@ -122,7 +122,7 @@ TEST(Utils, split_words_simple) TEST(Utils, split_words_quoted) { - std::vector list = utils::split_words("one 'two' \"three\""); + auto list = utils::split_words("one 'two' \"three\""); ASSERT_EQ(list.size(), 3); ASSERT_THAT(list[0], StrEq("one")); ASSERT_THAT(list[1], StrEq("two")); @@ -131,7 +131,7 @@ TEST(Utils, split_words_quoted) TEST(Utils, split_words_escaped) { - std::vector list = utils::split_words("1\\' '\"two\"' 3\\\""); + auto list = utils::split_words("1\\' '\"two\"' 3\\\""); ASSERT_EQ(list.size(), 3); ASSERT_THAT(list[0], StrEq("1\\'")); ASSERT_THAT(list[1], StrEq("\"two\"")); @@ -140,13 +140,22 @@ TEST(Utils, split_words_escaped) TEST(Utils, split_words_quote_in_quoted) { - std::vector list = utils::split_words("one 't\\'wo' \"th\\\"ree\""); + auto list = utils::split_words("one 't\\'wo' \"th\\\"ree\""); ASSERT_EQ(list.size(), 3); ASSERT_THAT(list[0], StrEq("one")); ASSERT_THAT(list[1], StrEq("t\\'wo")); ASSERT_THAT(list[2], StrEq("th\\\"ree")); } +TEST(Utils, split_lines) +{ + auto list = utils::split_lines(" line 1\nline 2 \n line 3 \n"); + ASSERT_EQ(list.size(), 3); + ASSERT_THAT(list[0], StrEq(" line 1")); + ASSERT_THAT(list[1], StrEq("line 2 ")); + ASSERT_THAT(list[2], StrEq(" line 3 ")); +} + TEST(Utils, valid_integer1) { ASSERT_TRUE(utils::is_integer("10")); From 45191e9f7c3c21a8f445c3c9699037838bb382cf Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 16:33:39 -0400 Subject: [PATCH 09/21] Refactor and add fix python/invoke tests --- unittest/python/test_python_package.cpp | 115 +++++++++++++----------- unittest/testing/core.h | 46 ++++++++-- unittest/testing/systems/melt.h | 28 +++--- 3 files changed, 117 insertions(+), 72 deletions(-) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index f7a184b6b3..5a36112781 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -24,14 +24,14 @@ #include #include +#include "../testing/core.h" +#include "../testing/systems/melt.h" + // location of '*.py' files required by tests #define STRINGIFY(val) XSTR(val) #define XSTR(val) #val std::string INPUT_FOLDER = STRINGIFY(TEST_INPUT_FOLDER); -// whether to print verbose output (i.e. not capturing LAMMPS screen output). -bool verbose = false; - const char * LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus."; using LAMMPS_NS::utils::split_words; @@ -42,51 +42,12 @@ using ::testing::StrEq; using ::testing::Eq; using ::testing::HasSubstr; -class PythonPackageTest : public ::testing::Test { +class PythonPackageTest : public LAMMPSTest { protected: - LAMMPS *lmp; - Info *info; - - void command(const std::string &line) { lmp->input->one(line.c_str()); } - - void command_string(const std::string &lines) { lammps_commands_string(lmp, lines.c_str()); } - - void HIDE_OUTPUT(std::function f) { - if (!verbose) ::testing::internal::CaptureStdout(); - f(); - if (!verbose) ::testing::internal::GetCapturedStdout(); - } - - std::string CAPTURE_OUTPUT(std::function f) { - ::testing::internal::CaptureStdout(); - f(); - auto output = ::testing::internal::GetCapturedStdout(); - if (verbose) std::cout << output; - return output; - } - - double get_variable_value(const std::string & name) { - char * str = utils::strdup(fmt::format("v_{}", name)); - double value = lmp->input->variable->compute_equal(str); - delete [] str; - return value; - } - - std::string get_variable_string(const std::string & name) { - return lmp->input->variable->retrieve(name.c_str()); - } - - void SetUp() override + void InitSystem() override { - const char *args[] = {"PythonPackageTest", "-log", "none", "-echo", "screen", "-nocite"}; - char **argv = (char **)args; - int argc = sizeof(args) / sizeof(char *); - HIDE_OUTPUT([&] { - lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); - }); - ASSERT_NE(lmp, nullptr); - info = new Info(lmp); if (!info->has_package("PYTHON")) GTEST_SKIP(); + HIDE_OUTPUT([&] { command("units real"); command("dimension 3"); @@ -100,15 +61,15 @@ protected: command("variable input_dir index " + INPUT_FOLDER); }); } +}; - void TearDown() override +class FixPythonInvokeTest : public MeltTest { +protected: + void InitSystem() override { - HIDE_OUTPUT([&] { - delete info; - delete lmp; - info = nullptr; - lmp = nullptr; - }); + if (!info->has_package("PYTHON")) GTEST_SKIP(); + + MeltTest::InitSystem(); } }; @@ -309,6 +270,56 @@ TEST_F(PythonPackageTest, RunSourceInline) ASSERT_THAT(output, HasSubstr("4")); } +TEST_F(FixPythonInvokeTest, end_of_step) +{ + HIDE_OUTPUT([&] { + command("python end_of_step_callback here \"\"\"\n" + "from __future__ import print_function\n" + "def end_of_step_callback(ptr):\n" + " print(\"PYTHON_END_OF_STEP\")\n" + "\"\"\""); + command("fix eos all python/invoke 10 end_of_step end_of_step_callback"); + }); + + auto output = CAPTURE_OUTPUT([&] { + command("run 50"); + }); + + auto lines = utils::split_lines(output); + int count = 0; + + for(auto & line : lines) { + if (line == "PYTHON_END_OF_STEP") ++count; + } + + ASSERT_EQ(count, 5); +} + +TEST_F(FixPythonInvokeTest, post_force) +{ + HIDE_OUTPUT([&] { + command("python post_force_callback here \"\"\"\n" + "from __future__ import print_function\n" + "def post_force_callback(ptr, vflag):\n" + " print(\"PYTHON_POST_FORCE\")\n" + "\"\"\""); + command("fix pf all python/invoke 10 post_force post_force_callback"); + }); + + auto output = CAPTURE_OUTPUT([&] { + command("run 50"); + }); + + auto lines = utils::split_lines(output); + int count = 0; + + for(auto & line : lines) { + if (line == "PYTHON_POST_FORCE") ++count; + } + + ASSERT_EQ(count, 5); +} + } // namespace LAMMPS_NS int main(int argc, char **argv) diff --git a/unittest/testing/core.h b/unittest/testing/core.h index 5852f7fd06..4dc15a5327 100644 --- a/unittest/testing/core.h +++ b/unittest/testing/core.h @@ -16,9 +16,12 @@ #include "info.h" #include "input.h" #include "lammps.h" +#include "variable.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include + using namespace LAMMPS_NS; using ::testing::MatchesRegex; @@ -45,29 +48,58 @@ class LAMMPSTest : public ::testing::Test { public: void command(const std::string &line) { lmp->input->one(line.c_str()); } + void HIDE_OUTPUT(std::function f) { + if (!verbose) ::testing::internal::CaptureStdout(); + f(); + if (!verbose) ::testing::internal::GetCapturedStdout(); + } + + std::string CAPTURE_OUTPUT(std::function f) { + ::testing::internal::CaptureStdout(); + f(); + auto output = ::testing::internal::GetCapturedStdout(); + if (verbose) std::cout << output; + return output; + } + + double get_variable_value(const std::string & name) { + char * str = utils::strdup(fmt::format("v_{}", name)); + double value = lmp->input->variable->compute_equal(str); + delete [] str; + return value; + } + + std::string get_variable_string(const std::string & name) { + return lmp->input->variable->retrieve(name.c_str()); + } + protected: const char *testbinary = "LAMMPSTest"; LAMMPS *lmp; + Info *info; void SetUp() override { const char *args[] = {testbinary, "-log", "none", "-echo", "screen", "-nocite"}; char **argv = (char **)args; int argc = sizeof(args) / sizeof(char *); - if (!verbose) ::testing::internal::CaptureStdout(); - lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); + HIDE_OUTPUT([&] { + lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD); + info = new Info(lmp); + }); InitSystem(); - if (!verbose) ::testing::internal::GetCapturedStdout(); } virtual void InitSystem() {} void TearDown() override { - if (!verbose) ::testing::internal::CaptureStdout(); - delete lmp; - lmp = nullptr; - if (!verbose) ::testing::internal::GetCapturedStdout(); + HIDE_OUTPUT([&] { + delete info; + delete lmp; + info = nullptr; + lmp = nullptr; + }); } }; diff --git a/unittest/testing/systems/melt.h b/unittest/testing/systems/melt.h index 4189a6b771..5ac92f562b 100644 --- a/unittest/testing/systems/melt.h +++ b/unittest/testing/systems/melt.h @@ -19,23 +19,25 @@ class MeltTest : public LAMMPSTest { protected: virtual void InitSystem() override { - command("units lj"); - command("atom_style atomic"); - command("atom_modify map yes"); + HIDE_OUTPUT([&] { + command("units lj"); + command("atom_style atomic"); + command("atom_modify map yes"); - command("lattice fcc 0.8442"); - command("region box block 0 2 0 2 0 2"); - command("create_box 1 box"); - command("create_atoms 1 box"); - command("mass 1 1.0"); + command("lattice fcc 0.8442"); + command("region box block 0 2 0 2 0 2"); + command("create_box 1 box"); + command("create_atoms 1 box"); + command("mass 1 1.0"); - command("velocity all create 3.0 87287"); + command("velocity all create 3.0 87287"); - command("pair_style lj/cut 2.5"); - command("pair_coeff 1 1 1.0 1.0 2.5"); + command("pair_style lj/cut 2.5"); + command("pair_coeff 1 1 1.0 1.0 2.5"); - command("neighbor 0.3 bin"); - command("neigh_modify every 20 delay 0 check no"); + command("neighbor 0.3 bin"); + command("neigh_modify every 20 delay 0 check no"); + }); } }; From 81e8676c7e8fe1566817decacc2f65cf4e5989fd Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 17:10:15 -0400 Subject: [PATCH 10/21] Prepare python/move unittest --- unittest/force-styles/tests/py_nve.py | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 unittest/force-styles/tests/py_nve.py diff --git a/unittest/force-styles/tests/py_nve.py b/unittest/force-styles/tests/py_nve.py new file mode 100644 index 0000000000..4740c85597 --- /dev/null +++ b/unittest/force-styles/tests/py_nve.py @@ -0,0 +1,75 @@ +from __future__ import print_function +from lammps import lammps, LAMMPS_INT, LAMMPS_DOUBLE + +class LAMMPSFix(object): + def __init__(self, ptr, group_name="all"): + self.lmp = lammps(ptr=ptr) + self.group_name = group_name + +class LAMMPSFixMove(LAMMPSFix): + def __init__(self, ptr, group_name="all"): + super(LAMMPSFixMove, self).__init__(ptr, group_name) + + def init(self): + pass + + def initial_integrate(self, vflag): + pass + + def final_integrate(self): + pass + + def initial_integrate_respa(self, vflag, ilevel, iloop): + pass + + def final_integrate_respa(self, ilevel, iloop): + pass + + def reset_dt(self): + pass + + +class NVE(LAMMPSFixMove): + """ Python implementation of fix/nve """ + def __init__(self, ptr, group_name="all"): + super(NVE, self).__init__(ptr) + assert(self.group_name == "all") + + def init(self): + dt = self.lmp.extract_global("dt") + ftm2v = self.lmp.extract_global("ftm2v") + self.ntypes = self.lmp.extract_global("ntypes") + self.dtv = dt + self.dtf = 0.5 * dt * ftm2v + + def initial_integrate(self, vflag): + nlocal = self.lmp.extract_global("nlocal") + mass = self.lmp.extract_atom("mass") + atype = self.lmp.extract_atom("type") + x = self.lmp.extract_atom("x") + v = self.lmp.extract_atom("v") + f = self.lmp.extract_atom("f") + + for i in range(nlocal): + dtfm = self.dtf / mass[int(atype[i])] + v[i][0] += dtfm * f[i][0] + v[i][1] += dtfm * f[i][1] + v[i][2] += dtfm * f[i][2] + x[i][0] += self.dtv * v[i][0] + x[i][1] += self.dtv * v[i][1] + x[i][2] += self.dtv * v[i][2] + + def final_integrate(self): + nlocal = self.lmp.extract_global("nlocal") + mass = self.lmp.extract_atom("mass") + atype = self.lmp.extract_atom("type") + v = self.lmp.extract_atom("v") + f = self.lmp.extract_atom("f") + + for i in range(nlocal): + dtfm = self.dtf / mass[int(atype[i])] + v[i][0] += dtfm * f[i][0] + v[i][1] += dtfm * f[i][1] + v[i][2] += dtfm * f[i][2] + + From b0bc0b9a2f0e39bf936a7b89b10eae3f218bf674 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 17:54:10 -0400 Subject: [PATCH 11/21] Use time.strptime instead of datetime.strptime Embedding the Python interpreter multiple times in the same process can cause this issue due to import caching. https://bugs.python.org/issue27400 This seems to be avoidable by using the time module instead. --- python/lammps/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/lammps/__init__.py b/python/lammps/__init__.py index 48839273c5..e6ffd779a9 100644 --- a/python/lammps/__init__.py +++ b/python/lammps/__init__.py @@ -15,7 +15,7 @@ from .pylammps import * # convert module string version to numeric version def get_version_number(): - from datetime import datetime + import time from sys import version_info vstring = None if version_info.major == 3 and version_info.minor >= 8: @@ -32,7 +32,7 @@ def get_version_number(): if not vstring: return 0 - d = datetime.strptime(vstring, "%d%b%Y") - return d.year*10000 + d.month*100 + d.day + t = time.strptime(vstring, "%d%b%Y") + return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday __version__ = get_version_number() From 4fa5ce2dbccd5d6bdcffcce5cce9c2a3bc636585 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 18:11:31 -0400 Subject: [PATCH 12/21] Remove unnecessary import --- unittest/force-styles/tests/py_nve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/py_nve.py b/unittest/force-styles/tests/py_nve.py index 4740c85597..1f05be04a8 100644 --- a/unittest/force-styles/tests/py_nve.py +++ b/unittest/force-styles/tests/py_nve.py @@ -1,5 +1,5 @@ from __future__ import print_function -from lammps import lammps, LAMMPS_INT, LAMMPS_DOUBLE +from lammps import lammps class LAMMPSFix(object): def __init__(self, ptr, group_name="all"): From 3c41c12dbc0818b2e93165d436aed45430285188 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 24 Mar 2021 18:58:46 -0400 Subject: [PATCH 13/21] Add testcase for python/move --- unittest/force-styles/CMakeLists.txt | 2 +- unittest/force-styles/test_fix_timestep.cpp | 5 +- .../tests/fix-timestep-python_move_nve.yaml | 73 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 unittest/force-styles/tests/fix-timestep-python_move_nve.yaml diff --git a/unittest/force-styles/CMakeLists.txt b/unittest/force-styles/CMakeLists.txt index 02300b5ea2..60e83e7e16 100644 --- a/unittest/force-styles/CMakeLists.txt +++ b/unittest/force-styles/CMakeLists.txt @@ -124,7 +124,7 @@ file(GLOB FIX_TIMESTEP_TESTS LIST_DIRECTORIES false ${TEST_INPUT_FOLDER}/fix-tim 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}") + set_tests_properties(${TNAME} PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${TEST_INPUT_FOLDER}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH}") endforeach() # dihedral style tester diff --git a/unittest/force-styles/test_fix_timestep.cpp b/unittest/force-styles/test_fix_timestep.cpp index e4b0fa6e02..aa07f36fb9 100644 --- a/unittest/force-styles/test_fix_timestep.cpp +++ b/unittest/force-styles/test_fix_timestep.cpp @@ -518,9 +518,12 @@ TEST(FixTimestep, plain) // rigid fixes need work to test properly with r-RESPA. // fix nve/limit cannot work with r-RESPA + // fix python/move implementation is missing library interface access to Repsa::step ifix = lmp->modify->find_fix("test"); if (!utils::strmatch(lmp->modify->fix[ifix]->style, "^rigid") && - !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit")) { + !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit") && + !utils::strmatch(lmp->modify->fix[ifix]->style, "^python/move") + ) { if (!verbose) ::testing::internal::CaptureStdout(); cleanup_lammps(lmp, test_config); diff --git a/unittest/force-styles/tests/fix-timestep-python_move_nve.yaml b/unittest/force-styles/tests/fix-timestep-python_move_nve.yaml new file mode 100644 index 0000000000..9c78dbe416 --- /dev/null +++ b/unittest/force-styles/tests/fix-timestep-python_move_nve.yaml @@ -0,0 +1,73 @@ +--- +lammps_version: 10 Mar 2021 +date_generated: Wed Mar 24 18:57:26 202 +epsilon: 9e-12 +prerequisites: ! | + atom full + fix python/move +pre_commands: ! "" +post_commands: ! | + fix test all python/move py_nve.NVE +input_file: in.fourmol +natoms: 29 +run_pos: ! |2 + 1 -2.7045559775384037e-01 2.4912159905679729e+00 -1.6695851791541885e-01 + 2 3.1004029573899528e-01 2.9612354631094391e+00 -8.5466363037021464e-01 + 3 -7.0398551400789477e-01 1.2305509955830618e+00 -6.2777526944456274e-01 + 4 -1.5818159336499285e+00 1.4837407818929933e+00 -1.2538710836062004e+00 + 5 -9.0719763672789266e-01 9.2652103885675297e-01 3.9954210488374786e-01 + 6 2.4831720524855985e-01 2.8313021497871271e-01 -1.2314233331711453e+00 + 7 3.4143527641386412e-01 -2.2646551041391422e-02 -2.5292291414903052e+00 + 8 1.1743552229100009e+00 -4.8863228565853950e-01 -6.3783432910825522e-01 + 9 1.3800524229500313e+00 -2.5274721030406683e-01 2.8353985887095157e-01 + 10 2.0510765220543883e+00 -1.4604063740302866e+00 -9.8323745081712954e-01 + 11 1.7878031944442556e+00 -1.9921863272948861e+00 -1.8890602447625777e+00 + 12 3.0063007039340053e+00 -4.9013350496963298e-01 -1.6231898107386229e+00 + 13 4.0515402959192999e+00 -8.9202011606653986e-01 -1.6400005529924957e+00 + 14 2.6066963345543819e+00 -4.1789253965514150e-01 -2.6634003608794394e+00 + 15 2.9695287185712913e+00 5.5422613165234036e-01 -1.2342022021790127e+00 + 16 2.6747029695228521e+00 -2.4124119054564295e+00 -2.3435746150616148e-02 + 17 2.2153577785283796e+00 -2.0897985186907717e+00 1.1963150794479436e+00 + 18 2.1369701704115704e+00 3.0158507413630606e+00 -3.5179348337215015e+00 + 19 1.5355837136087378e+00 2.6255292355375675e+00 -4.2353987779879052e+00 + 20 2.7727573005678776e+00 3.6923910449610169e+00 -3.9330842459133493e+00 + 21 4.9040128073204299e+00 -4.0752348172957946e+00 -3.6210314709891711e+00 + 22 4.3582355554440841e+00 -4.2126119427287048e+00 -4.4612844196314052e+00 + 23 5.7439382849307599e+00 -3.5821957939275029e+00 -3.8766361295935821e+00 + 24 2.0689243582422630e+00 3.1513346907271012e+00 3.1550389754828800e+00 + 25 1.3045351331492134e+00 3.2665125705842848e+00 2.5111855257433504e+00 + 26 2.5809237402711274e+00 4.0117602605482832e+00 3.2212060529089896e+00 + 27 -1.9611343130357228e+00 -4.3563411931359752e+00 2.1098293115523705e+00 + 28 -2.7473562684513411e+00 -4.0200819932379330e+00 1.5830052163433954e+00 + 29 -1.3126000191359855e+00 -3.5962518039482929e+00 2.2746342468737835e+00 +run_vel: ! |2 + 1 8.1705744183262364e-03 1.6516406176274288e-02 4.7902264318912978e-03 + 2 5.4501493445687759e-03 5.1791699408496334e-03 -1.4372931530376651e-03 + 3 -8.2298292722385643e-03 -1.2926551614621381e-02 -4.0984181178163881e-03 + 4 -3.7699042590093588e-03 -6.5722892098813799e-03 -1.1184640360133230e-03 + 5 -1.1021961004346586e-02 -9.8906780939335987e-03 -2.8410737829284395e-03 + 6 -3.9676663166400034e-02 4.6817061464710256e-02 3.7148491979476124e-02 + 7 9.1033953013898580e-04 -1.0128524411938794e-02 -5.1568251805019748e-02 + 8 7.9064712058855707e-03 -3.3507254552631767e-03 3.4557098492564629e-02 + 9 1.5644176117320923e-03 3.7365546102722164e-03 1.5047408822037646e-02 + 10 2.9201446820573174e-02 -2.9249578745486147e-02 -1.5018077424322538e-02 + 11 -4.7835961513517560e-03 -3.7481385134185206e-03 -2.3464104142290089e-03 + 12 2.2696451841920521e-03 -3.4774154398129479e-04 -3.0640770327796806e-03 + 13 2.7531740451953168e-03 5.8171061612840667e-03 -7.9467454022160518e-04 + 14 3.5246182371994252e-03 -5.7939995585585468e-03 -3.9478431172751344e-03 + 15 -1.8547943640122894e-03 -5.8554729942777743e-03 6.2938485140538649e-03 + 16 1.8681499973445245e-02 -1.3262466204585335e-02 -4.5638651457003243e-02 + 17 -1.2896269981100382e-02 9.7527665265956451e-03 3.7296535360836762e-02 + 18 -8.0065794848261610e-04 -8.6270473212554395e-04 -1.4483040697508738e-03 + 19 1.2452390836182623e-03 -2.5061097118772701e-03 7.2998631009712975e-03 + 20 3.5930060229597042e-03 3.6938860309252966e-03 3.2322732687893028e-03 + 21 -1.4689220370766550e-03 -2.7352129761527741e-04 7.0581624215243391e-04 + 22 -7.0694199254630339e-03 -4.2577148924878554e-03 2.8079117614251796e-04 + 23 6.0446963117374913e-03 -1.4000131614795382e-03 2.5819754847014255e-03 + 24 3.1926367902287940e-04 -9.9445664749276438e-04 1.4999996959365452e-04 + 25 1.3789754514814662e-04 -4.4335894884532569e-03 -8.1808136725080281e-04 + 26 2.0485904035217549e-03 2.7813358633835984e-03 4.3245727149206692e-03 + 27 4.5604120293369857e-04 -1.0305523026921137e-03 2.1188058381358511e-04 + 28 -6.2544520861855116e-03 1.4127711176146942e-03 -1.8429821884794269e-03 + 29 6.4110631534401762e-04 3.1273432719593867e-03 3.7253671105656715e-03 +... From e0fdd2ad892531632ce18b6cd929f73367c33b91 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 25 Mar 2021 10:09:08 -0400 Subject: [PATCH 14/21] correct lammps.extract_global() method for returned arrays which are returned as list --- python/lammps/core.py | 21 +++++++++++++++++---- unittest/python/python-commands.py | 9 +++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index be026d5e10..279b0a64dc 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -746,12 +746,21 @@ class lammps(object): :type name: string :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) :type dtype: int, optional - :return: value of the property or None - :rtype: int, float, or NoneType + :return: value of the property or list of values or None + :rtype: int, float, list, or NoneType """ + if dtype == LAMMPS_AUTODETECT: dtype = self.extract_global_datatype(name) + # set length of vector for items that are not a scalar + vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3, + 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } + if name in vec_dict: + veclen = vec_dict[name] + else: + veclen = 1 + if name: name = name.encode() else: return None @@ -770,10 +779,14 @@ class lammps(object): ptr = self.lib.lammps_extract_global(self.lmp, name) if ptr: - return target_type(ptr[0]) + if veclen > 1: + result = [] + for i in range(0,veclen): + result.append(target_type(ptr[i])) + return result + else: return target_type(ptr[0]) return None - # ------------------------------------------------------------------------- # extract per-atom info datatype diff --git a/unittest/python/python-commands.py b/unittest/python/python-commands.py index c82e2fdb26..c1e4cecef5 100644 --- a/unittest/python/python-commands.py +++ b/unittest/python/python-commands.py @@ -268,6 +268,15 @@ create_atoms 1 single & self.assertEqual(self.lmp.extract_global("boxyhi"), 2.0) self.assertEqual(self.lmp.extract_global("boxzlo"), -3.0) self.assertEqual(self.lmp.extract_global("boxzhi"), 3.0) + self.assertEqual(self.lmp.extract_global("boxlo"), [-1.0, -2.0, -3.0]) + self.assertEqual(self.lmp.extract_global("boxhi"), [1.0, 2.0, 3.0]) + self.assertEqual(self.lmp.extract_global("sublo"), [-1.0, -2.0, -3.0]) + self.assertEqual(self.lmp.extract_global("subhi"), [1.0, 2.0, 3.0]) + self.assertEqual(self.lmp.extract_global("periodicity"), [1,1,1]) + # only valid for triclinic box + self.lmp.command("change_box all triclinic") + self.assertEqual(self.lmp.extract_global("sublo_lambda"), [0.0, 0.0, 0.0]) + self.assertEqual(self.lmp.extract_global("subhi_lambda"), [1.0, 1.0, 1.0]) ############################## if __name__ == "__main__": From b8f02d759abdbbd88407818bef22bcc48abe7e43 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 25 Mar 2021 11:22:22 -0400 Subject: [PATCH 15/21] add support for extracting respa levels and timestep values --- python/lammps/core.py | 2 ++ src/library.cpp | 19 ++++++++++++++++++- unittest/python/python-commands.py | 10 ++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index 279b0a64dc..b3cdceb1a6 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -758,6 +758,8 @@ class lammps(object): 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } if name in vec_dict: veclen = vec_dict[name] + elif name == 'respa_dt': + veclen = self.extract_global('respa_levels',LAMMPS_INT) else: veclen = 1 diff --git a/src/library.cpp b/src/library.cpp index 8de36a299f..6f08cffcc2 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -31,12 +31,14 @@ #include "group.h" #include "info.h" #include "input.h" +#include "integrate.h" #include "memory.h" #include "modify.h" #include "molecule.h" #include "neigh_list.h" #include "neighbor.h" #include "region.h" +#include "respa.h" #include "output.h" #include "thermo.h" #include "timer.h" @@ -984,12 +986,14 @@ to then decide how to cast the (void*) pointer and access the data. * \return integer constant encoding the data type of the property * or -1 if not found. */ -int lammps_extract_global_datatype(void *handle, const char *name) +int lammps_extract_global_datatype(void * /*handle*/, const char *name) { if (strcmp(name,"dt") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"ntimestep") == 0) return LAMMPS_BIGINT; if (strcmp(name,"atime") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"atimestep") == 0) return LAMMPS_BIGINT; + if (strcmp(name,"respa_levels") == 0) return LAMMPS_INT; + if (strcmp(name,"respa_dt") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"boxlo") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"boxhi") == 0) return LAMMPS_DOUBLE; @@ -1116,6 +1120,14 @@ report the "native" data type. The following tables are provided: - bigint - 1 - the number of the timestep when "atime" was last updated. + * - respa_levels + - int + - 1 + - number of r-RESPA levels. See :doc:`run_style`. + * - respa_dt + - double + - number of r-RESPA levels + - length of the time steps with r-RESPA. See :doc:`run_style`. .. _extract_box_settings: @@ -1366,6 +1378,11 @@ void *lammps_extract_global(void *handle, const char *name) if (strcmp(name,"atime") == 0) return (void *) &lmp->update->atime; if (strcmp(name,"atimestep") == 0) return (void *) &lmp->update->atimestep; + if (utils::strmatch(lmp->update->integrate_style,"^respa")) { + Respa *respa = (Respa *)lmp->update->integrate; + if (strcmp(name,"respa_levels") == 0) return (void *) &respa->nlevels; + if (strcmp(name,"respa_dt") == 0) return (void *) respa->step; + } if (strcmp(name,"boxlo") == 0) return (void *) lmp->domain->boxlo; if (strcmp(name,"boxhi") == 0) return (void *) lmp->domain->boxhi; if (strcmp(name,"sublo") == 0) return (void *) lmp->domain->sublo; diff --git a/unittest/python/python-commands.py b/unittest/python/python-commands.py index c1e4cecef5..3bcb785b74 100644 --- a/unittest/python/python-commands.py +++ b/unittest/python/python-commands.py @@ -274,6 +274,16 @@ create_atoms 1 single & self.assertEqual(self.lmp.extract_global("subhi"), [1.0, 2.0, 3.0]) self.assertEqual(self.lmp.extract_global("periodicity"), [1,1,1]) # only valid for triclinic box + self.assertEqual(self.lmp.extract_global("respa_levels"), None) + self.assertEqual(self.lmp.extract_global("respa_dt"), None) + + # set and initialize r-RESPA + self.lmp.command("run_style respa 3 5 2 pair 2 kspace 3") + self.lmp.command("mass * 1.0") + self.lmp.command("run 1 post no") + self.assertEqual(self.lmp.extract_global("ntimestep"), 1) + self.assertEqual(self.lmp.extract_global("respa_levels"), 3) + self.assertEqual(self.lmp.extract_global("respa_dt"), [0.0005, 0.0025, 0.005]) self.lmp.command("change_box all triclinic") self.assertEqual(self.lmp.extract_global("sublo_lambda"), [0.0, 0.0, 0.0]) self.assertEqual(self.lmp.extract_global("subhi_lambda"), [1.0, 1.0, 1.0]) From a193d9d429ccf8129db46731bf3716b5b58b2c8c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 25 Mar 2021 11:23:28 -0400 Subject: [PATCH 16/21] fix several issues when using extract_global() from python exposed by tests --- python/lammps/core.py | 3 ++- src/library.cpp | 9 +++++++-- unittest/python/python-commands.py | 13 +++++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index b3cdceb1a6..e112c4f3f8 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -777,10 +777,11 @@ class lammps(object): target_type = float elif dtype == LAMMPS_STRING: self.lib.lammps_extract_global.restype = c_char_p - target_type = lambda x: str(x, 'ascii') ptr = self.lib.lammps_extract_global(self.lmp, name) if ptr: + if dtype == LAMMPS_STRING: + return ptr.decode('utf-8') if veclen > 1: result = [] for i in range(0,veclen): diff --git a/src/library.cpp b/src/library.cpp index 6f08cffcc2..a950d31e4e 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1387,8 +1387,13 @@ void *lammps_extract_global(void *handle, const char *name) if (strcmp(name,"boxhi") == 0) return (void *) lmp->domain->boxhi; if (strcmp(name,"sublo") == 0) return (void *) lmp->domain->sublo; if (strcmp(name,"subhi") == 0) return (void *) lmp->domain->subhi; - if (strcmp(name,"sublo_lambda") == 0) return (void *) lmp->domain->sublo_lamda; - if (strcmp(name,"subhi_lambda") == 0) return (void *) lmp->domain->subhi_lamda; + // these are only valid for a triclinic cell + if (lmp->domain->triclinic) { + if (strcmp(name,"sublo_lambda") == 0) + return (void *) lmp->domain->sublo_lamda; + if (strcmp(name,"subhi_lambda") == 0) + return (void *) lmp->domain->subhi_lamda; + } if (strcmp(name,"boxxlo") == 0) return (void *) &lmp->domain->boxlo[0]; if (strcmp(name,"boxxhi") == 0) return (void *) &lmp->domain->boxhi[0]; if (strcmp(name,"boxylo") == 0) return (void *) &lmp->domain->boxlo[1]; diff --git a/unittest/python/python-commands.py b/unittest/python/python-commands.py index 3bcb785b74..3661feb8a0 100644 --- a/unittest/python/python-commands.py +++ b/unittest/python/python-commands.py @@ -259,9 +259,13 @@ create_atoms 1 single & result = self.lmp.get_thermo(key) self.assertEqual(value, result, key) - def test_extract_global_double(self): + def test_extract_global(self): self.lmp.command("region box block -1 1 -2 2 -3 3") self.lmp.command("create_box 1 box") + self.assertEqual(self.lmp.extract_global("units"), "lj") + self.assertEqual(self.lmp.extract_global("ntimestep"), 0) + self.assertEqual(self.lmp.extract_global("dt"), 0.005) + self.assertEqual(self.lmp.extract_global("boxxlo"), -1.0) self.assertEqual(self.lmp.extract_global("boxxhi"), 1.0) self.assertEqual(self.lmp.extract_global("boxylo"), -2.0) @@ -273,7 +277,9 @@ create_atoms 1 single & self.assertEqual(self.lmp.extract_global("sublo"), [-1.0, -2.0, -3.0]) self.assertEqual(self.lmp.extract_global("subhi"), [1.0, 2.0, 3.0]) self.assertEqual(self.lmp.extract_global("periodicity"), [1,1,1]) - # only valid for triclinic box + self.assertEqual(self.lmp.extract_global("triclinic"), 0) + self.assertEqual(self.lmp.extract_global("sublo_lambda"), None) + self.assertEqual(self.lmp.extract_global("subhi_lambda"), None) self.assertEqual(self.lmp.extract_global("respa_levels"), None) self.assertEqual(self.lmp.extract_global("respa_dt"), None) @@ -284,7 +290,10 @@ create_atoms 1 single & self.assertEqual(self.lmp.extract_global("ntimestep"), 1) self.assertEqual(self.lmp.extract_global("respa_levels"), 3) self.assertEqual(self.lmp.extract_global("respa_dt"), [0.0005, 0.0025, 0.005]) + + # checks only for triclinic boxes self.lmp.command("change_box all triclinic") + self.assertEqual(self.lmp.extract_global("triclinic"), 1) self.assertEqual(self.lmp.extract_global("sublo_lambda"), [0.0, 0.0, 0.0]) self.assertEqual(self.lmp.extract_global("subhi_lambda"), [1.0, 1.0, 1.0]) From cb25e4aa3957b44af87823c2ec9e2f67a28c2845 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Thu, 25 Mar 2021 20:08:32 -0400 Subject: [PATCH 17/21] Add nve respa testcase for python/move --- unittest/force-styles/test_fix_timestep.cpp | 3 +-- unittest/force-styles/tests/py_nve.py | 29 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/unittest/force-styles/test_fix_timestep.cpp b/unittest/force-styles/test_fix_timestep.cpp index aa07f36fb9..f72295716e 100644 --- a/unittest/force-styles/test_fix_timestep.cpp +++ b/unittest/force-styles/test_fix_timestep.cpp @@ -521,8 +521,7 @@ TEST(FixTimestep, plain) // fix python/move implementation is missing library interface access to Repsa::step ifix = lmp->modify->find_fix("test"); if (!utils::strmatch(lmp->modify->fix[ifix]->style, "^rigid") && - !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit") && - !utils::strmatch(lmp->modify->fix[ifix]->style, "^python/move") + !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit") ) { if (!verbose) ::testing::internal::CaptureStdout(); diff --git a/unittest/force-styles/tests/py_nve.py b/unittest/force-styles/tests/py_nve.py index 1f05be04a8..57592ea074 100644 --- a/unittest/force-styles/tests/py_nve.py +++ b/unittest/force-styles/tests/py_nve.py @@ -34,6 +34,7 @@ class NVE(LAMMPSFixMove): def __init__(self, ptr, group_name="all"): super(NVE, self).__init__(ptr) assert(self.group_name == "all") + self._step_respa = None def init(self): dt = self.lmp.extract_global("dt") @@ -42,6 +43,12 @@ class NVE(LAMMPSFixMove): self.dtv = dt self.dtf = 0.5 * dt * ftm2v + @property + def step_respa(self): + if not self._step_respa: + self._step_respa = self.lmp.extract_global("respa_dt") + return self._step_respa + def initial_integrate(self, vflag): nlocal = self.lmp.extract_global("nlocal") mass = self.lmp.extract_atom("mass") @@ -72,4 +79,26 @@ class NVE(LAMMPSFixMove): v[i][1] += dtfm * f[i][1] v[i][2] += dtfm * f[i][2] + def initial_integrate_respa(self, vflag, ilevel, iloop): + ftm2v = self.lmp.extract_global("ftm2v") + self.dtv = self.step_respa[ilevel] + self.dtf = 0.5 * self.step_respa[ilevel] * ftm2v + # innermost level - NVE update of v and x + # all other levels - NVE update of v + + if ilevel == 0: + self.initial_integrate(vflag) + else: + self.final_integrate() + + def final_integrate_respa(self, ilevel, iloop): + ftm2v = self.lmp.extract_global("ftm2v") + self.dtf = 0.5 * self.step_respa[ilevel] * ftm2v + self.final_integrate() + + def reset_dt(self): + dt = self.lmp.extract_global("dt") + ftm2v = self.lmp.extract_global("ftm2v") + self.dtv = dt; + self.dtf = 0.5 * dt * ftm2v; From 029db1413e5248e344df1b1a161b4f90e62932eb Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Thu, 25 Mar 2021 21:01:32 -0400 Subject: [PATCH 18/21] Add missing verbose after merge --- unittest/python/test_python_package.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/unittest/python/test_python_package.cpp b/unittest/python/test_python_package.cpp index 5a36112781..c240d23875 100644 --- a/unittest/python/test_python_package.cpp +++ b/unittest/python/test_python_package.cpp @@ -33,6 +33,7 @@ std::string INPUT_FOLDER = STRINGIFY(TEST_INPUT_FOLDER); const char * LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent metus."; +bool verbose = false; using LAMMPS_NS::utils::split_words; From c9652f3aa695f95d62914cc94000cdcd51f4fd98 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 1 Apr 2021 09:31:54 -0400 Subject: [PATCH 19/21] update documentation for the extract_global() method of the lammps.lammps class --- python/lammps/core.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/lammps/core.py b/python/lammps/core.py index e112c4f3f8..3118cb3d99 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -729,12 +729,11 @@ class lammps(object): def extract_global(self, name, dtype=LAMMPS_AUTODETECT): """Query LAMMPS about global settings of different types. - This is a wrapper around the :cpp:func:`lammps_extract_global` - function of the C-library interface. Unlike the C function - this method returns the value and not a pointer and thus can - only return the first value for keywords representing a list - of values. The :cpp:func:`lammps_extract_global` documentation - includes a list of the supported keywords and their data types. + This is a wrapper around the :cpp:func:`lammps_extract_global` function + of the C-library interface. Since there are no pointers in Python, this + method will - unlike the C function - return the value or a list of + values. The :cpp:func:`lammps_extract_global` documentation includes a + list of the supported keywords and their data types. Since Python needs to know the data type to be able to interpret the result, by default, this function will try to auto-detect the data type by asking the library. You can also force a specific data type. For that From f867e69290beea676eb86b0cabae44768349df9f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 1 Apr 2021 09:35:52 -0400 Subject: [PATCH 20/21] include new split_lines() function in Developer docs --- doc/src/Developer_utils.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/src/Developer_utils.rst b/doc/src/Developer_utils.rst index 992df6ba63..17b4715dc7 100644 --- a/doc/src/Developer_utils.rst +++ b/doc/src/Developer_utils.rst @@ -101,6 +101,9 @@ and parsing files or arguments. .. doxygenfunction:: split_words :project: progguide +.. doxygenfunction:: split_lines + :project: progguide + .. doxygenfunction:: strmatch :project: progguide From e0aec1b5d9ba2c3d26d16beb11d5a100853b195f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 1 Apr 2021 09:39:17 -0400 Subject: [PATCH 21/21] remove obsoleted comment --- unittest/force-styles/test_fix_timestep.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/unittest/force-styles/test_fix_timestep.cpp b/unittest/force-styles/test_fix_timestep.cpp index f72295716e..e4b0fa6e02 100644 --- a/unittest/force-styles/test_fix_timestep.cpp +++ b/unittest/force-styles/test_fix_timestep.cpp @@ -518,11 +518,9 @@ TEST(FixTimestep, plain) // rigid fixes need work to test properly with r-RESPA. // fix nve/limit cannot work with r-RESPA - // fix python/move implementation is missing library interface access to Repsa::step ifix = lmp->modify->find_fix("test"); if (!utils::strmatch(lmp->modify->fix[ifix]->style, "^rigid") && - !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit") - ) { + !utils::strmatch(lmp->modify->fix[ifix]->style, "^nve/limit")) { if (!verbose) ::testing::internal::CaptureStdout(); cleanup_lammps(lmp, test_config);