diff --git a/doc/src/Developer_utils.rst b/doc/src/Developer_utils.rst index 46d15cc007..b92d817d87 100644 --- a/doc/src/Developer_utils.rst +++ b/doc/src/Developer_utils.rst @@ -211,6 +211,9 @@ Argument processing .. doxygenfunction:: expand_args :project: progguide +.. doxygenfunction:: expand_type + :project: progguide + Convenience functions ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/utils.cpp b/src/utils.cpp index 5ae367729f..00f2095c35 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -20,6 +20,7 @@ #include "fix.h" #include "fmt/chrono.h" #include "input.h" +#include "label_map.h" #include "memory.h" #include "modify.h" #include "text_file_reader.h" @@ -766,6 +767,26 @@ int utils::expand_args(const char *file, int line, int narg, char **arg, int mod return newarg; } +/* ------------------------------------------------------------------------- + Expand type string to numeric string from labelmap. + Return copy of expanded type or null pointer. +------------------------------------------------------------------------- */ + +char *utils::expand_type(const char *file, int line, const std::string &str, int mode, LAMMPS *lmp) +{ + if (!lmp) return nullptr; + if (is_type(str) == 1) { + if (!lmp->atom->labelmapflag) + lmp->error->all(file, line, "Type string {} cannot be used without a labelmap", str); + + int type = lmp->atom->lmap->find(str, mode); + if (type == -1) lmp->error->all(file, line, "Type string {} not found in labelmap", str); + + return utils::strdup(std::to_string(type)); + } else + return nullptr; +} + /* ---------------------------------------------------------------------- Make copy of string in new storage. Works like the (non-portable) C-style strdup() but also accepts a C++ string as argument. diff --git a/src/utils.h b/src/utils.h index 8e3a3e35b8..28f48d1ab2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -359,6 +359,24 @@ namespace utils { int expand_args(const char *file, int line, int narg, char **arg, int mode, char **&earg, LAMMPS *lmp); + /*! Expand type label string into its equivalent numeric type + * + * This function checks if a given string may be a type label and + * then searches the labelmap type indicated by the *mode* argument + * for the corresponding numeric type. If this is found a copy of + * the numeric type string is made and returned. Otherwise a null + * pointer is returned. + * If a string is returned, the calling code must free it with delete[]. + * + * \param file name of source file for error message + * \param line line number in source file for error message + * \param str type string to be expanded + * \param mode select labelmap using constants from Atom class + * \param lmp pointer to top-level LAMMPS class instance + * \return pointer to expanded string or null pointer */ + + char *expand_type(const char *file, int line, const std::string &str, int mode, LAMMPS *lmp); + /*! Make C-style copy of string in new storage * * This allocates a storage buffer and copies the C-style or diff --git a/unittest/commands/test_labelmap.cpp b/unittest/commands/test_labelmap.cpp index ab62bfb4b0..1e6bf4305b 100644 --- a/unittest/commands/test_labelmap.cpp +++ b/unittest/commands/test_labelmap.cpp @@ -63,6 +63,13 @@ TEST_F(LabelMapTest, Atoms) BEGIN_HIDE_OUTPUT(); command("region box block 0 2 0 2 0 2"); command("create_box 4 box"); + END_HIDE_OUTPUT(); + EXPECT_EQ(domain->box_exist, 1); + EXPECT_EQ(atom->lmap, nullptr); + TEST_FAILURE(".*ERROR: Type string C1 cannot be used without a labelmap.*", + utils::expand_type(FLERR, "C1", Atom::ATOM, lmp);); + + BEGIN_HIDE_OUTPUT(); command("labelmap atom 2 N1"); command("labelmap atom 3 O1 4 H1"); command("mass * 1.0"); @@ -89,6 +96,22 @@ TEST_F(LabelMapTest, Atoms) EXPECT_EQ(atom->lmap->find("X", Atom::ATOM), -1); EXPECT_DOUBLE_EQ(atom->mass[3], 10.0); + EXPECT_EQ(utils::expand_type(FLERR, "1", Atom::ATOM, lmp), nullptr); + EXPECT_EQ(utils::expand_type(FLERR, "*3", Atom::ATOM, lmp), nullptr); + EXPECT_EQ(utils::expand_type(FLERR, "1*2", Atom::ATOM, lmp), nullptr); + EXPECT_EQ(utils::expand_type(FLERR, "*", Atom::ATOM, lmp), nullptr); + EXPECT_EQ(utils::expand_type(FLERR, "**", Atom::ATOM, lmp), nullptr); + EXPECT_EQ(utils::expand_type(FLERR, "1*2*", Atom::ATOM, lmp), nullptr); + + auto expanded = utils::expand_type(FLERR, "C1", Atom::ATOM, lmp); + EXPECT_THAT(expanded, StrEq("1")); + delete[] expanded; + expanded = utils::expand_type(FLERR, "O#", Atom::ATOM, lmp); + EXPECT_THAT(expanded, StrEq("3")); + delete[] expanded; + TEST_FAILURE(".*ERROR: Type string XX not found in labelmap.*", + utils::expand_type(FLERR, "XX", Atom::ATOM, lmp);); + TEST_FAILURE(".*ERROR: Labelmap atom type 0 must be within 1-4.*", command("labelmap atom 0 C1");); TEST_FAILURE(".*ERROR: Labelmap atom type 5 must be within 1-4.*", @@ -238,6 +261,24 @@ TEST_F(LabelMapTest, Topology) EXPECT_EQ(atom->lmap->find("X", Atom::ATOM), -1); EXPECT_EQ(atom->lmap->find("N2'-C1\"-N2'", Atom::BOND), -1); platform::unlink("labelmap_topology.inc"); + + auto expanded = utils::expand_type(FLERR, "N2'", Atom::ATOM, lmp); + EXPECT_THAT(expanded, StrEq("2")); + delete[] expanded; + expanded = utils::expand_type(FLERR, "[C1][C1]", Atom::BOND, lmp); + EXPECT_THAT(expanded, StrEq("2")); + delete[] expanded; + expanded = utils::expand_type(FLERR, "C1-N2-C1", Atom::ANGLE, lmp); + EXPECT_THAT(expanded, StrEq("1")); + delete[] expanded; + expanded = utils::expand_type(FLERR, "C1-N2-C1-N2", Atom::DIHEDRAL, lmp); + EXPECT_THAT(expanded, StrEq("1")); + delete[] expanded; + expanded = utils::expand_type(FLERR, "C1-N2-C1-N2", Atom::IMPROPER, lmp); + EXPECT_THAT(expanded, StrEq("1")); + delete[] expanded; + TEST_FAILURE(".*ERROR: Type string XX not found in labelmap.*", + utils::expand_type(FLERR, "XX", Atom::BOND, lmp);); } } // namespace LAMMPS_NS diff --git a/unittest/commands/test_variables.cpp b/unittest/commands/test_variables.cpp index 848331abe2..e9103831df 100644 --- a/unittest/commands/test_variables.cpp +++ b/unittest/commands/test_variables.cpp @@ -33,10 +33,10 @@ bool verbose = false; namespace LAMMPS_NS { +using MathConst::MY_PI; using ::testing::ContainsRegex; using ::testing::ExitedWithCode; using ::testing::StrEq; -using MathConst::MY_PI; class VariableTest : public LAMMPSTest { protected: @@ -196,8 +196,10 @@ TEST_F(VariableTest, CreateDelete) TEST_FAILURE(".*ERROR: Illegal variable command.*", command("variable");); TEST_FAILURE(".*ERROR: Illegal variable index command.*", command("variable dummy index");); - TEST_FAILURE(".*ERROR: Illegal variable delete command: expected 2 arguments but found 3.*", command("variable dummy delete xxx");); - TEST_FAILURE(".*ERROR: Invalid variable loop argument: -1.*", command("variable dummy loop -1");); + TEST_FAILURE(".*ERROR: Illegal variable delete command: expected 2 arguments but found 3.*", + command("variable dummy delete xxx");); + TEST_FAILURE(".*ERROR: Invalid variable loop argument: -1.*", + command("variable dummy loop -1");); TEST_FAILURE(".*ERROR: Illegal variable loop command.*", command("variable dummy loop 10 1");); TEST_FAILURE(".*ERROR: Unknown variable keyword: xxx.*", command("variable dummy xxxx");); TEST_FAILURE(".*ERROR: Cannot redefine variable as a different style.*", @@ -402,8 +404,9 @@ TEST_F(VariableTest, Functions) command("print \"$(extract_setting()\"");); TEST_FAILURE(".*ERROR: Invalid extract_setting.. function syntax in variable formula.*", command("print \"$(extract_setting(one,two))\"");); - TEST_FAILURE(".*ERROR: Unknown setting nprocs for extract_setting.. function in variable formula.*", - command("print \"$(extract_setting(nprocs))\"");); + TEST_FAILURE( + ".*ERROR: Unknown setting nprocs for extract_setting.. function in variable formula.*", + command("print \"$(extract_setting(nprocs))\"");); } TEST_F(VariableTest, IfCommand)