From 15689aedc5e3b3a567723ece6c73c2d31392436e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 19 Jan 2026 21:44:02 -0500 Subject: [PATCH 01/47] recover compiling SCAFACOS package on Ubuntu 22.04LTS --- cmake/CMakeLists.txt | 5 ++++- cmake/Modules/Packages/SCAFACOS.cmake | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index d28aae24256..a9a4bd861f8 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -193,7 +193,10 @@ endif() # do not include the (obsolete) MPI C++ bindings which makes for leaner object files # and avoids namespace conflicts. Put this early to increase its visbility. -set(MPI_CXX_SKIP_MPICXX TRUE CACHE BOOL "Skip MPI C++ Bindings" FORCE) +# do not apply when compiling SCAFACOS since it uses the obsolete bindings +if (NOT PKG_SCAFACOS) + set(MPI_CXX_SKIP_MPICXX TRUE CACHE BOOL "Skip MPI C++ Bindings" FORCE) +endif() ######################################################################## # User input options # diff --git a/cmake/Modules/Packages/SCAFACOS.cmake b/cmake/Modules/Packages/SCAFACOS.cmake index 2905a207b0b..b0455c5f006 100644 --- a/cmake/Modules/Packages/SCAFACOS.cmake +++ b/cmake/Modules/Packages/SCAFACOS.cmake @@ -3,7 +3,7 @@ enable_language(C) find_package(GSL REQUIRED) find_package(PkgConfig QUIET) -find_package(MPI REQUIRED) +find_package(MPI REQUIRED COMPONENTS C MPICXX CXX Fortran) set(DOWNLOAD_SCAFACOS_DEFAULT ON) if(PKG_CONFIG_FOUND) pkg_check_modules(SCAFACOS QUIET scafacos) @@ -19,6 +19,8 @@ if(DOWNLOAD_SCAFACOS) mark_as_advanced(SCAFACOS_URL) mark_as_advanced(SCAFACOS_MD5) GetFallbackURL(SCAFACOS_URL SCAFACOS_FALLBACK) + set(SCAFACOS_CXX_FLAGS "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}} ${CMAKE_CXX_FLAGS}") + set(SCAFACOS_C_FLAGS "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}} ${CMAKE_C_FLAGS}") include(ExternalProject) ExternalProject_Add(scafacos_build @@ -32,6 +34,8 @@ if(DOWNLOAD_SCAFACOS) CXX=${CMAKE_MPI_CXX_COMPILER} CC=${CMAKE_MPI_C_COMPILER} F77= + CFLAGS=${SCAFACOS_C_FLAGS} + CXXFLAGS=${SCAFACOS_CXX_FLAGS} BUILD_BYPRODUCTS /lib/libfcs.a /lib/libfcs_direct.a @@ -55,7 +59,7 @@ if(DOWNLOAD_SCAFACOS) set_target_properties(LAMMPS::SCAFACOS PROPERTIES IMPORTED_LOCATION "${INSTALL_DIR}/lib/libfcs.a" INTERFACE_INCLUDE_DIRECTORIES "${INSTALL_DIR}/include" - INTERFACE_LINK_LIBRARIES "${INSTALL_DIR}/lib/libfcs.a;${INSTALL_DIR}/lib/libfcs_direct.a;${INSTALL_DIR}/lib/libfcs_ewald.a;${INSTALL_DIR}/lib/libfcs_fmm.a;${INSTALL_DIR}/lib/libfcs_p2nfft.a;${INSTALL_DIR}/lib/libfcs_p3m.a;GSL::gsl;${INSTALL_DIR}/lib/libfcs_near.a;${INSTALL_DIR}/lib/libfcs_gridsort.a;${INSTALL_DIR}/lib/libfcs_resort.a;${INSTALL_DIR}/lib/libfcs_redist.a;${INSTALL_DIR}/lib/libfcs_common.a;${INSTALL_DIR}/lib/libfcs_pnfft.a;${INSTALL_DIR}/lib/libfcs_pfft.a;${INSTALL_DIR}/lib/libfcs_fftw3_mpi.a;${INSTALL_DIR}/lib/libfcs_fftw3.a;MPI::MPI_Fortran;MPI::MPI_C") + INTERFACE_LINK_LIBRARIES "${INSTALL_DIR}/lib/libfcs.a;${INSTALL_DIR}/lib/libfcs_direct.a;${INSTALL_DIR}/lib/libfcs_ewald.a;${INSTALL_DIR}/lib/libfcs_fmm.a;${INSTALL_DIR}/lib/libfcs_p2nfft.a;${INSTALL_DIR}/lib/libfcs_p3m.a;GSL::gsl;${INSTALL_DIR}/lib/libfcs_near.a;${INSTALL_DIR}/lib/libfcs_gridsort.a;${INSTALL_DIR}/lib/libfcs_resort.a;${INSTALL_DIR}/lib/libfcs_redist.a;${INSTALL_DIR}/lib/libfcs_common.a;${INSTALL_DIR}/lib/libfcs_pnfft.a;${INSTALL_DIR}/lib/libfcs_pfft.a;${INSTALL_DIR}/lib/libfcs_fftw3_mpi.a;${INSTALL_DIR}/lib/libfcs_fftw3.a;MPI::MPI_CXX;MPI::MPI_Fortran;MPI::MPI_C;${CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES}") target_link_libraries(lammps PRIVATE LAMMPS::SCAFACOS) add_dependencies(LAMMPS::SCAFACOS scafacos_build) else() From 35dfe3b0998bfc6679b8557e63172ae843208f06 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 19 Jan 2026 23:05:08 -0500 Subject: [PATCH 02/47] modernize error handling in fix brownian/* and fix broken checks --- src/BROWNIAN/fix_brownian.cpp | 7 +- src/BROWNIAN/fix_brownian_asphere.cpp | 15 ++-- src/BROWNIAN/fix_brownian_base.cpp | 110 ++++++++++++++------------ src/BROWNIAN/fix_brownian_sphere.cpp | 12 +-- 4 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/BROWNIAN/fix_brownian.cpp b/src/BROWNIAN/fix_brownian.cpp index 0b73858f29e..df3cc5f4477 100644 --- a/src/BROWNIAN/fix_brownian.cpp +++ b/src/BROWNIAN/fix_brownian.cpp @@ -34,10 +34,9 @@ using namespace FixConst; FixBrownian::FixBrownian(LAMMPS *lmp, int narg, char **arg) : FixBrownianBase(lmp, narg, arg) { if (dipole_flag || gamma_t_eigen_flag || gamma_r_eigen_flag || gamma_r_flag || rot_temp_flag || - planar_rot_flag) { - error->all(FLERR, "Illegal fix brownian command."); - } - if (!gamma_t_flag) { error->all(FLERR, "Illegal fix brownian command."); } + planar_rot_flag) + error->all(FLERR, "Using keyword(s) not compatible with fix {}", style); + if (!gamma_t_flag) error->all(FLERR, "Keyword gamma_t is required for fix {}", style); } /* ---------------------------------------------------------------------- */ diff --git a/src/BROWNIAN/fix_brownian_asphere.cpp b/src/BROWNIAN/fix_brownian_asphere.cpp index 5ce57df7ce8..0dd4da95e7a 100644 --- a/src/BROWNIAN/fix_brownian_asphere.cpp +++ b/src/BROWNIAN/fix_brownian_asphere.cpp @@ -37,17 +37,14 @@ using namespace FixConst; FixBrownianAsphere::FixBrownianAsphere(LAMMPS *lmp, int narg, char **arg) : FixBrownianBase(lmp, narg, arg), avec(nullptr) { - if (!gamma_t_eigen_flag || !gamma_r_eigen_flag) { - error->all(FLERR, "Illegal fix brownian command."); - } - - if (gamma_t_flag || gamma_r_flag) error->all(FLERR, "Illegal fix brownian command."); + if (gamma_t_flag || gamma_r_flag) + error->all(FLERR, "Keywords gamma_t or gamma_r are not compatible with fix {}", style); + if (!gamma_t_eigen_flag || !gamma_r_eigen_flag) + error->all(FLERR, "Keywords gamma_t_eigen and gamma_r_eigen are required for fix {}", style); if (dipole_flag && !atom->mu_flag) - error->all(FLERR, "Fix brownian/asphere dipole requires atom attribute mu"); - - if (!atom->ellipsoid_flag) - error->all(FLERR, "Fix brownian/asphere requires atom style ellipsoid"); + error->all(FLERR, "Fix {} dipole requires atom attribute mu", style); + if (!atom->ellipsoid_flag) error->all(FLERR, "Fix {} requires atom style ellipsoid", style); if (planar_rot_flag && (comm->me == 0)) { error->warning(FLERR, "Ignoring first two entries of gamma_r_eigen since rotation is planar."); diff --git a/src/BROWNIAN/fix_brownian_base.cpp b/src/BROWNIAN/fix_brownian_base.cpp index 6b00ec29854..a7f1ca1f17c 100644 --- a/src/BROWNIAN/fix_brownian_base.cpp +++ b/src/BROWNIAN/fix_brownian_base.cpp @@ -48,18 +48,19 @@ FixBrownianBase::FixBrownianBase(LAMMPS *lmp, int narg, char **arg) : planar_rot_flag = 0; g2 = 0.0; - if (narg < 5) utils::missing_cmd_args(FLERR, "fix brownian", error); + std::string mystyle = fmt::format("fix {}", style); + if (narg < 5) utils::missing_cmd_args(FLERR, mystyle, error); temp = utils::numeric(FLERR, arg[3], false, lmp); - if (temp <= 0) error->all(FLERR, "Fix brownian temp must be > 0.0"); + if (temp <= 0) error->all(FLERR, 3, "Fix {} temp must be > 0.0", style); seed = utils::inumeric(FLERR, arg[4], false, lmp); - if (seed <= 0) error->all(FLERR, "Fix brownian seed must be > 0"); + if (seed <= 0) error->all(FLERR, 4, "Fix {} seed must be > 0", style); int iarg = 5; while (iarg < narg) { if (strcmp(arg[iarg], "rng") == 0) { - if (narg < iarg + 1) utils::missing_cmd_args(FLERR, "fix brownian rng", error); + if (narg < iarg + 2) utils::missing_cmd_args(FLERR, mystyle + " rng", error); if (strcmp(arg[iarg + 1], "uniform") == 0) { noise_flag = 1; } else if (strcmp(arg[iarg + 1], "gaussian") == 0) { @@ -68,11 +69,11 @@ FixBrownianBase::FixBrownianBase(LAMMPS *lmp, int narg, char **arg) : } else if (strcmp(arg[iarg + 1], "none") == 0) { noise_flag = 0; } else { - error->all(FLERR, "Unknown fix brownian rng keyword {}", arg[iarg + 1]); + error->all(FLERR, iarg + 1, "Unknown fix {} rng keyword {}", style, arg[iarg + 1]); } iarg = iarg + 2; } else if (strcmp(arg[iarg], "dipole") == 0) { - if (narg < iarg + 3) utils::missing_cmd_args(FLERR, "fix brownian dipole", error); + if (narg < iarg + 4) utils::missing_cmd_args(FLERR, mystyle + " dipole", error); dipole_flag = 1; delete[] dipole_body; @@ -84,27 +85,31 @@ FixBrownianBase::FixBrownianBase(LAMMPS *lmp, int narg, char **arg) : iarg = iarg + 4; } else if (strcmp(arg[iarg], "gamma_t_eigen") == 0) { - if (narg < iarg + 3) utils::missing_cmd_args(FLERR, "fix brownian gamma_t_eigen", error); + if (narg < iarg + 4) utils::missing_cmd_args(FLERR, mystyle + " gamma_t_eigen", error); + + double gamma_t_tmp[3]; + gamma_t_tmp[0] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + gamma_t_tmp[1] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); + if (domain->dimension == 2) { + if (strcmp(arg[iarg + 3], "inf") != 0) + error->all(FLERR, iarg + 3, "Fix {} gamma_t_eigen third value must be inf for 2D system", + style); + gamma_t_tmp[2] = 1.0; + } else { + gamma_t_tmp[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); + } + if ((gamma_t_tmp[0] <= 0.0) || (gamma_t_tmp[1] <= 0.0) || (gamma_t_tmp[2] < -0)) + error->all(FLERR, iarg, "Fix {} gamma_t_eigen values must be > 0", style); gamma_t_eigen_flag = 1; delete[] gamma_t_inv; delete[] gamma_t_invsqrt; gamma_t_inv = new double[3]; gamma_t_invsqrt = new double[3]; - gamma_t_inv[0] = 1. / utils::numeric(FLERR, arg[iarg + 1], false, lmp); - gamma_t_inv[1] = 1. / utils::numeric(FLERR, arg[iarg + 2], false, lmp); - - if (domain->dimension == 2) { - if (strcmp(arg[iarg + 3], "inf") != 0) { - error->all(FLERR, "Fix brownian gamma_t_eigen third value must be inf for 2D system."); - } - gamma_t_inv[2] = 0; - } else { - gamma_t_inv[2] = 1.0 / utils::numeric(FLERR, arg[iarg + 3], false, lmp); - } - - if (gamma_t_inv[0] < 0 || gamma_t_inv[1] < 0 || gamma_t_inv[2] < 0) - error->all(FLERR, "Fix brownian gamma_t_eigen values must be > 0."); + gamma_t_inv[0] = 1.0 / gamma_t_tmp[0]; + gamma_t_inv[1] = 1.0 / gamma_t_tmp[1]; + gamma_t_inv[2] = 1.0 / gamma_t_tmp[2]; + if (domain->dimension == 2) gamma_t_inv[2] = 0.0; gamma_t_invsqrt[0] = sqrt(gamma_t_inv[0]); gamma_t_invsqrt[1] = sqrt(gamma_t_inv[1]); @@ -112,7 +117,27 @@ FixBrownianBase::FixBrownianBase(LAMMPS *lmp, int narg, char **arg) : iarg = iarg + 4; } else if (strcmp(arg[iarg], "gamma_r_eigen") == 0) { - if (narg == iarg + 3) error->all(FLERR, "Illegal fix brownian command."); + if (narg < iarg + 4) utils::missing_cmd_args(FLERR, mystyle + " gamma_r_eigen", error); + + double gamma_r_tmp[3]; + if (domain->dimension == 2) { + if (strcmp(arg[iarg + 1], "inf") != 0) + error->all(FLERR, iarg + 1, "Fix {} gamma_r_eigen first value must be inf for 2D system", + style); + gamma_r_tmp[0] = 1.0; + + if (strcmp(arg[iarg + 2], "inf") != 0) + error->all(FLERR, iarg + 2, "Fix {} gamma_r_eigen second value must be inf for 2D system", + style); + gamma_r_tmp[1] = 1.0; + } else { + gamma_r_tmp[0] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + gamma_r_tmp[1] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); + } + gamma_r_tmp[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); + + if ((gamma_r_tmp[0] <= 0.0) || (gamma_r_tmp[1] <= 0) || (gamma_r_tmp[2] <= 0)) + error->all(FLERR, iarg, "Fix {} gamma_r_eigen values must be > 0", style); gamma_r_eigen_flag = 1; delete[] gamma_r_inv; @@ -120,65 +145,52 @@ FixBrownianBase::FixBrownianBase(LAMMPS *lmp, int narg, char **arg) : gamma_r_inv = new double[3]; gamma_r_invsqrt = new double[3]; + gamma_r_inv[0] = 1.0 / gamma_r_tmp[0]; + gamma_r_inv[1] = 1.0 / gamma_r_tmp[1]; + gamma_r_inv[2] = 1.0 / gamma_r_tmp[2]; if (domain->dimension == 2) { - if (strcmp(arg[iarg + 1], "inf") != 0) { - error->all(FLERR, "Fix brownian gamma_r_eigen first value must be inf for 2D system."); - } - gamma_r_inv[0] = 0; - - if (strcmp(arg[iarg + 2], "inf") != 0) { - error->all(FLERR, "Fix brownian gamma_r_eigen second value must be inf for 2D system."); - } - gamma_r_inv[1] = 0; - } else { - - gamma_r_inv[0] = 1. / utils::numeric(FLERR, arg[iarg + 1], false, lmp); - gamma_r_inv[1] = 1. / utils::numeric(FLERR, arg[iarg + 2], false, lmp); + gamma_r_inv[0] = 0.0; + gamma_r_inv[1] = 0.0; } - - gamma_r_inv[2] = 1. / utils::numeric(FLERR, arg[iarg + 3], false, lmp); - - if (gamma_r_inv[0] < 0 || gamma_r_inv[1] < 0 || gamma_r_inv[2] < 0) - error->all(FLERR, "Fix brownian gamma_r_eigen values must be > 0."); - gamma_r_invsqrt[0] = sqrt(gamma_r_inv[0]); gamma_r_invsqrt[1] = sqrt(gamma_r_inv[1]); gamma_r_invsqrt[2] = sqrt(gamma_r_inv[2]); iarg = iarg + 4; } else if (strcmp(arg[iarg], "gamma_t") == 0) { - if (narg == iarg + 1) { error->all(FLERR, "Illegal fix brownian command."); } + if (narg < iarg + 2) utils::missing_cmd_args(FLERR, mystyle + " gamma_t", error); gamma_t_flag = 1; gamma_t = utils::numeric(FLERR, arg[iarg + 1], false, lmp); - if (gamma_t <= 0) error->all(FLERR, "Fix brownian gamma_t must be > 0."); + if (gamma_t <= 0.0) error->all(FLERR, iarg + 1, "Fix {} gamma_t value must be > 0", style); iarg = iarg + 2; } else if (strcmp(arg[iarg], "gamma_r") == 0) { - if (narg == iarg + 1) { error->all(FLERR, "Illegal fix brownian command."); } + if (narg < iarg + 2) utils::missing_cmd_args(FLERR, mystyle + " gamma_r", error); gamma_r_flag = 1; gamma_r = utils::numeric(FLERR, arg[iarg + 1], false, lmp); - if (gamma_r <= 0) error->all(FLERR, "Fix brownian gamma_r must be > 0."); + if (gamma_r <= 0) error->all(FLERR, iarg + 1, "Fix {} gamma_r value must be > 0", style); iarg = iarg + 2; } else if (strcmp(arg[iarg], "rotation_temp") == 0) { - if (narg == iarg + 1) { error->all(FLERR, "Illegal fix brownian command."); } + if (narg < iarg + 2) utils::missing_cmd_args(FLERR, mystyle + " rotation_temp", error); rot_temp_flag = 1; rot_temp = utils::numeric(FLERR, arg[iarg + 1], false, lmp); - if (rot_temp <= 0) error->all(FLERR, "Fix brownian rotation_temp must be > 0."); + if (rot_temp <= 0) + error->all(FLERR, iarg + 1, "Fix {} rotation_temp value must be > 0", style); iarg = iarg + 2; } else if (strcmp(arg[iarg], "planar_rotation") == 0) { planar_rot_flag = 1; if (domain->dimension == 2) - error->all(FLERR, "The planar_rotation keyword is not allowed for 2D simulations"); + error->all(FLERR, iarg, "The planar_rotation keyword is not allowed for 2D simulations"); iarg = iarg + 1; } else { - error->all(FLERR, "Illegal fix brownian command."); + error->all(FLERR, iarg, "Unknown fix {} keyword {}", style, arg[iarg]); } } if (!rot_temp_flag) rot_temp = temp; diff --git a/src/BROWNIAN/fix_brownian_sphere.cpp b/src/BROWNIAN/fix_brownian_sphere.cpp index 79e3858f00b..52d89091687 100644 --- a/src/BROWNIAN/fix_brownian_sphere.cpp +++ b/src/BROWNIAN/fix_brownian_sphere.cpp @@ -35,12 +35,12 @@ using namespace FixConst; FixBrownianSphere::FixBrownianSphere(LAMMPS *lmp, int narg, char **arg) : FixBrownianBase(lmp, narg, arg) { - if (gamma_t_eigen_flag || gamma_r_eigen_flag) { - error->all(FLERR, "Illegal fix brownian/sphere command."); - } - - if (!gamma_t_flag || !gamma_r_flag) error->all(FLERR, "Illegal fix brownian/sphere command."); - if (!atom->mu_flag) error->all(FLERR, "Fix brownian/sphere requires atom attribute mu"); + if (gamma_t_eigen_flag || gamma_r_eigen_flag) + error->all(FLERR, "Keywords gamma_t_eigen or gamma_r_eigen are not compatible with fix {}", + style); + if (!gamma_t_flag || !gamma_r_flag) + error->all(FLERR, "Keywords gamma_t and gamma_r are required for fix {}", style); + if (!atom->mu_flag) error->all(FLERR, "Fix {} requires atom attribute mu", style); } /* ---------------------------------------------------------------------- */ From 3199d6b43e143f9009f1de1c624b1ca6ac2a010d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 00:43:33 -0500 Subject: [PATCH 03/47] add VMD workflow example --- doc/src/Howto_viz.rst | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/doc/src/Howto_viz.rst b/doc/src/Howto_viz.rst index ea80da342c9..acdea0e13e7 100644 --- a/doc/src/Howto_viz.rst +++ b/doc/src/Howto_viz.rst @@ -34,6 +34,81 @@ rarely needed these days. ------------------------ +Basic workflow for loading LAMMPS trajectories in VMD +===================================================== + +VMD can read native LAMMPS dump files (in text format not binary) and +several other dump styles. The native LAMMPS format is preferred since +it contains simulation box information. VMD does not support reading +data files directly, but `the TopoTools plugin +`_ does. This +step is usually necessary, since the LAMMPS dump files do not contain +information about the molecular topology and elements and that +information can be obtained from the data file. There also is the +`pbctools plugin +`_ that +"repairing" bonds that are broken because one of its atom passed through +a periodic boundary. Because of using the plugins, the following +commands need to be typed into the VMD console to read a trajectory. +The following commands are based on the ``peptide`` example to which the +command ``dump 1 all atom 10 dump.peptide`` (note the use of the group +"all" to match the number of atoms in the data file. VMD does not +support cases where the number of atoms change or you are trying to read +in a subset of a system unless *all* files contain the same subset. + +.. code-block:: Tcl + + # load data file including its coordinates + topo readlammpsdata data.peptide full + # atom style full is the default ^^^^ and can be omitted + + # try to infer missing atom properties from available information + topo guessatom lammps data + + # now add one of more dump files + mol addfile dump.peptide type lammpstrj waitfor all + # we tell VMD which file type ^^^^^^^^^ if the filename does not end in .lammpstrj + + # jump to the first frame with the data file coordinates + animate goto 0 + # if needed use the following command to try and repair broken bonds + # pbc join fragment -now + + # unwrap the entire trajectory to repair all broken bonds + # this is faster and more accurate than running `pbc join` for all frames + pbc unwrap -all + pbc wrap -all -compound fragment -orthorhombic + # remove for triclinic box ^^^^^^^^^^^^^ + + # delete the coordinate frame from the data file + animate delete beg 0 end 0 top + # write out the topology information without coordinates to a PSF file + animate write psf peptide.psf top + # and the processed trajectory data to a DCD file for future use + animate write dcd peptide.dcd top waitfor all + + ############################################################### + # the steps up to this line only need to be run once + ############################################################### + # set visualization defaults + display perspective orthographic + mol default style Licorice + mol default material Diffuse + display backgroundgradient on + + # delete molecule and load psf and dcd file + mol delete top + mol new peptide.psf + mol addfile peptide.dcd waitfor all + pbc box + +To automate this process, you can also save these commands to a file, +e.g. ``peptide.vmd`` and then load this file from the VMD "File" menu +with "Load Visualization State..." or type in the command console +``play peptide.vmd``. + +------------------------ + Advanced graphics features in the *dump image* command ====================================================== From 9b40817b4c6950642e291003dda6db8021103e56 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 00:43:49 -0500 Subject: [PATCH 04/47] replace non-ASCII characters and rewrap --- doc/src/fix_bond_react.rst | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/doc/src/fix_bond_react.rst b/doc/src/fix_bond_react.rst index 5103722f0d2..d30786fcfac 100644 --- a/doc/src/fix_bond_react.rst +++ b/doc/src/fix_bond_react.rst @@ -396,20 +396,21 @@ Wildcard atoms match to any atom type in the simulation. Wildcard atoms can be used to reduce the number of reaction templates needed to model a set of similar reactions. Wildcard atoms are specified in the Wildcards section of the map file. The atom types of wildcard atoms in the -simulation are not updated. Any bond, angle, dihedral, or improper, that -is defined in the reaction templates and contains a wildcard atom, will be -updated by inferring its type from its constituent atom types. To use -wildcard atoms, a specific :doc:`type label ` format is -necessary to infer the types of higher-order interactions. Bond, angle, -dihedral, and improper type labels must contain their constituent atom -types delimited by hyphens, e.g., ‘c2-c2-c2-n’ for a dihedral that contains -three atoms of type 'c2' and one atom of 'n'. Certain symmetries are -considered to account for equivalent ways of writing higher-order -interactions. Type labels for bonds, angles, and dihedrals are assumed to -be equivalent to those written in reverse order. For example, an angle -with type label 'c1-c2-n' is equivalent to 'n-c2-c1'. Symmetries for -impropers are more complex and are described on the doc page for each -improper style in the 'Symmetry convention' section. +simulation are not updated. Any bond, angle, dihedral, or improper, +that is defined in the reaction templates and contains a wildcard atom, +will be updated by inferring its type from its constituent atom types. +To use wildcard atoms, a specific :doc:`type label ` +format is necessary to infer the types of higher-order interactions. +Bond, angle, dihedral, and improper type labels must contain their +constituent atom types delimited by hyphens, e.g., 'c2-c2-c2-n' for a +dihedral that contains three atoms of type 'c2' and one atom of 'n'. +Certain symmetries are considered to account for equivalent ways of +writing higher-order interactions. Type labels for bonds, angles, and +dihedrals are assumed to be equivalent to those written in reverse +order. For example, an angle with type label 'c1-c2-n' is equivalent to +'n-c2-c1'. Symmetries for impropers are more complex and are described +on the doc page for each improper style in the 'Symmetry convention' +section. The post-reacted molecule template contains a sample of the reaction site and its surrounding topology after the reaction has occurred. It From 4a17805c6583f5a10b4c9542b340d1baa102a7f1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 00:50:00 -0500 Subject: [PATCH 05/47] own a few more files --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c3b3ddbce19..331c9569ff5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -174,6 +174,10 @@ # docs /doc/* @akohlmey +/doc/src/Howto_viz.rst @akohlmey +/doc/src/Howto_spc.rst @akohlmey +/doc/src/Howto_tip*.rst @akohlmey +/examples/GRAPHICS/ @akohlmey /examples/plugin/ @akohlmey /examples/PACKAGES/pace/plugin/ @akohlmey From a8554a98e0181e06054ea811588b3b7b888d7f76 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 01:02:11 -0500 Subject: [PATCH 06/47] apply C++ move semantics to avoid copy of temporary variable --- src/label_map.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/label_map.cpp b/src/label_map.cpp index a4bc67d9a04..95e98a68b6d 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -390,7 +390,7 @@ int LabelMap::infer_bondtype(int type1, int type2) mytypes[1] = typelabel[type2-1]; if (mytypes[0].empty() || mytypes[1].empty()) return -1; - return infer_bondtype(mytypes); + return infer_bondtype(std::move(mytypes)); } /* ---------------------------------------------------------------------- @@ -430,7 +430,7 @@ int LabelMap::infer_angletype(int type1, int type2, int type3) for (size_t i = 0; i < 3; i++) if (mytypes[i].empty()) return -1; - return infer_angletype(mytypes); + return infer_angletype(std::move(mytypes)); } /* ---------------------------------------------------------------------- @@ -473,7 +473,7 @@ int LabelMap::infer_dihedraltype(int type1, int type2, int type3, int type4) for (size_t i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; - return infer_dihedraltype(mytypes); + return infer_dihedraltype(std::move(mytypes)); } /* ---------------------------------------------------------------------- @@ -518,7 +518,7 @@ int LabelMap::infer_impropertype(int type1, int type2, int type3, int type4) for (int i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; - return infer_impropertype(mytypes); + return infer_impropertype(std::move(mytypes)); } /* ---------------------------------------------------------------------- @@ -580,7 +580,7 @@ int LabelMap::parse_typelabel(int ntypes, std::string label, std::vector Date: Tue, 20 Jan 2026 03:52:13 -0500 Subject: [PATCH 07/47] use raw string literals for regex patterns --- src/GRAPHICS/dump_image.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/GRAPHICS/dump_image.cpp b/src/GRAPHICS/dump_image.cpp index 7f1870eb820..0829dbef5ab 100644 --- a/src/GRAPHICS/dump_image.cpp +++ b/src/GRAPHICS/dump_image.cpp @@ -101,23 +101,23 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : // set filetype based on filename suffix - if (utils::strmatch(filename, "\\.jpg$") || utils::strmatch(filename, "\\.JPG$") - || utils::strmatch(filename, "\\.jpeg$") || utils::strmatch(filename, "\\.JPEG$")) + if (utils::strmatch(filename, R"(\.jpg$)") || utils::strmatch(filename, R"(\.JPG$)") || + utils::strmatch(filename, R"(\.jpeg$)") || utils::strmatch(filename, R"(\.JPEG$)")) filetype = JPG; - else if (utils::strmatch(filename, "\\.png$") || utils::strmatch(filename, "\\.PNG$")) + else if (utils::strmatch(filename, R"(\.png$)") || utils::strmatch(filename, R"(\.PNG$)")) filetype = PNG; - else if (utils::strmatch(filename, "\\.tga$") || utils::strmatch(filename, "\\.TGA$")) + else if (utils::strmatch(filename, R"(\.tga$)") || utils::strmatch(filename, R"(\.TGA$)")) filetype = TGA; - else if (compressed && (utils::strmatch(filename, "\\.jpg\\.\\w+$") || - utils::strmatch(filename, "\\.JPG\\.\\w+$") || - utils::strmatch(filename, "\\.jpeg\\.\\w+$") || - utils::strmatch(filename, "\\.JPEG\\.\\w+$"))) + else if (compressed && (utils::strmatch(filename, R"(\.jpg\.\w+$)") || + utils::strmatch(filename, R"(\.JPG\.\w+$)") || + utils::strmatch(filename, R"(\.jpeg\.\w+$)") || + utils::strmatch(filename, R"(\.JPEG\.\w+$)"))) error->all(FLERR, Error::NOLASTLINE, "Cannot use compression with JPEG images"); - else if (compressed && (utils::strmatch(filename, "\\.png\\.\\w+$") || - utils::strmatch(filename, "\\.PNG\\.\\w+$"))) + else if (compressed && (utils::strmatch(filename, R"(\.png\.\w+$)") || + utils::strmatch(filename, R"(\.PNG\.\w+$)"))) error->all(FLERR, Error::NOLASTLINE, "Cannot use compression with PNG images"); - else if (compressed && (utils::strmatch(filename, "\\.tga\\.\\w+$") || - utils::strmatch(filename, "\\.TGA\\.\\w+$"))) + else if (compressed && (utils::strmatch(filename, R"(\.tga\.\w+$)") || + utils::strmatch(filename, R"(\.TGA\.\w+$)"))) error->all(FLERR, Error::NOLASTLINE, "Cannot use compression with TGA images"); else filetype = PPM; From ca3ccb6baa6a9eb3532867e4273d81e1bfe1f6c1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 03:53:26 -0500 Subject: [PATCH 08/47] use nested namespace --- src/graphics.h | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/graphics.h b/src/graphics.h index e5a14e011a4..81e9ebf932b 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -16,28 +16,26 @@ // common definitions and declarations for graphics support in LAMMPS -namespace LAMMPS_NS { -namespace Graphics { - enum { - NONE, - SPHERE, // a single sphere with radius provided - LINE, // a cylinder with diameter given through fflag2 - TRI, // a surface mesh as triangles or cylinder mesh based on fflag1, fflag2 sets diameter - CYLINDER, // a cylinder with diameter given by fix, fflag1 choose caps, fflag2 adjusts diameter - TRIANGLE, // a regular triangle, no settings apply - BOND, // two connected cylinders with bond diameter, colored by atom types, fflag1 sets cap - ARROW, // a cylinder with a conical tip and a flat cap at the bottom - CONE, // a truncated cone with flat caps, fflag1 sets caps - PIXMAP // a pointer to a pixmap buffer at x,y,z location - }; // used by some Body and Fix child classes +namespace LAMMPS_NS::Graphics { +enum { + NONE, + SPHERE, // a single sphere with radius provided + LINE, // a cylinder with diameter given through fflag2 + TRI, // a surface mesh as triangles or cylinder mesh based on fflag1, fflag2 sets diameter + CYLINDER, // a cylinder with diameter given by fix, fflag1 choose caps, fflag2 adjusts diameter + TRIANGLE, // a regular triangle, no settings apply + BOND, // two connected cylinders with bond diameter, colored by atom types, fflag1 sets cap + ARROW, // a cylinder with a conical tip and a flat cap at the bottom + CONE, // a truncated cone with flat caps, fflag1 sets caps + PIXMAP // a pointer to a pixmap buffer at x,y,z location +}; // used by some Body and Fix child classes - // definitions for rendering caps and sides of a truncated cone - enum { - CONE_TOP = 1 << 0, // draw top cap of cone/cylinder - CONE_BOT = 1 << 1, // draw bottom cap of cone/cylinder - CONE_SIDE = 1 << 2, // draw side of cone/cylinder - CONE_ALL = CONE_TOP | CONE_BOT | CONE_SIDE // all of the above - }; -} // namespace Graphics -} // namespace LAMMPS_NS +// definitions for rendering caps and sides of a truncated cone +enum { + CONE_TOP = 1 << 0, // draw top cap of cone/cylinder + CONE_BOT = 1 << 1, // draw bottom cap of cone/cylinder + CONE_SIDE = 1 << 2, // draw side of cone/cylinder + CONE_ALL = CONE_TOP | CONE_BOT | CONE_SIDE // all of the above +}; +} // namespace LAMMPS_NS::Graphics #endif From 28a479858fddc2f2993c3a9006c410e5ba4cdfe2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 04:58:27 -0500 Subject: [PATCH 09/47] use const references where possible to avoid copies --- src/label_map.cpp | 16 ++++++++-------- src/label_map.h | 25 +++++++++++++++---------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/label_map.cpp b/src/label_map.cpp index 95e98a68b6d..c01fb4b7a82 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -390,7 +390,7 @@ int LabelMap::infer_bondtype(int type1, int type2) mytypes[1] = typelabel[type2-1]; if (mytypes[0].empty() || mytypes[1].empty()) return -1; - return infer_bondtype(std::move(mytypes)); + return infer_bondtype(mytypes); } /* ---------------------------------------------------------------------- @@ -398,7 +398,7 @@ int LabelMap::infer_bondtype(int type1, int type2) assumes bond types are of the form "a-b" for atom types 'a' and 'b' ------------------------------------------------------------------------- */ -int LabelMap::infer_bondtype(std::vector mytypes) +int LabelMap::infer_bondtype(const std::vector &mytypes) { // search for matching bond type label with symmetry considerations @@ -430,7 +430,7 @@ int LabelMap::infer_angletype(int type1, int type2, int type3) for (size_t i = 0; i < 3; i++) if (mytypes[i].empty()) return -1; - return infer_angletype(std::move(mytypes)); + return infer_angletype(mytypes); } /* ---------------------------------------------------------------------- @@ -439,7 +439,7 @@ int LabelMap::infer_angletype(int type1, int type2, int type3) assumes angle types of the form "a-b-c" for atom types 'a', 'b', 'c' ------------------------------------------------------------------------- */ -int LabelMap::infer_angletype(std::vector mytypes) +int LabelMap::infer_angletype(const std::vector &mytypes) { // search for matching angle type label, with symmetry considerations @@ -473,7 +473,7 @@ int LabelMap::infer_dihedraltype(int type1, int type2, int type3, int type4) for (size_t i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; - return infer_dihedraltype(std::move(mytypes)); + return infer_dihedraltype(mytypes); } /* ---------------------------------------------------------------------- @@ -482,7 +482,7 @@ int LabelMap::infer_dihedraltype(int type1, int type2, int type3, int type4) assumes dihedral types of the form "a-b-c-d" ------------------------------------------------------------------------- */ -int LabelMap::infer_dihedraltype(std::vector mytypes) +int LabelMap::infer_dihedraltype(const std::vector &mytypes) { // search for matching dihedral type label @@ -518,7 +518,7 @@ int LabelMap::infer_impropertype(int type1, int type2, int type3, int type4) for (int i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; - return infer_impropertype(std::move(mytypes)); + return infer_impropertype(mytypes); } /* ---------------------------------------------------------------------- @@ -528,7 +528,7 @@ int LabelMap::infer_impropertype(int type1, int type2, int type3, int type4) the symmetry of the improper is encoded in improper.symmatoms ------------------------------------------------------------------------- */ -int LabelMap::infer_impropertype(std::vector mytypes) +int LabelMap::infer_impropertype(const std::vector &mytypes) { // search for matching improper type label diff --git a/src/label_map.h b/src/label_map.h index 4b3632fb2d0..4a4ed51f646 100644 --- a/src/label_map.h +++ b/src/label_map.h @@ -40,19 +40,24 @@ class LabelMap : protected Pointers { // infer interaction types from standard hyphen-delimited format - int infer_bondtype(int, int); // infer bond type from two atom types - int infer_bondtype(std::vector); // infer bond type from two atom type labels + // infer bond type from two numeric atom types or type labels + int infer_bondtype(int, int); + int infer_bondtype(const std::vector &); - int infer_angletype(int, int, int); // infer angle type from three atom types - int infer_angletype(std::vector); // infer angle type from three atom type labels + // infer angle type from three numeric atom types or type labels + int infer_angletype(int, int, int); + int infer_angletype(const std::vector &); - int infer_dihedraltype(int, int, int, int); // infer dihedral type from four atom types - int infer_dihedraltype(std::vector); // infer dihedral type from four atom type labels + // infer dihedral type from four numeric atom types or type labels + int infer_dihedraltype(int, int, int, int); + int infer_dihedraltype(const std::vector &); - int infer_impropertype(int, int, int, int); // infer improper type from four atom types - int infer_impropertype(std::vector); // infer improper type from four atom type labels + // infer improper type from four numeric atom types or type labels + int infer_impropertype(int, int, int, int); + int infer_impropertype(const std::vector &); - int parse_typelabel(int, std::string, std::vector &); // get strings within hyphen delimiters + // get strings within hyphen delimiters + int parse_typelabel(int, const std::string &, std::vector &); // input/output for atom class label map @@ -60,7 +65,7 @@ class LabelMap : protected Pointers { void read_restart(FILE *fp); void write_restart(FILE *); -protected: + protected: int natomtypes, nbondtypes, nangletypes, ndihedraltypes, nimpropertypes; std::vector typelabel, btypelabel, atypelabel; std::vector dtypelabel, itypelabel; From 0bc87ee188e1eae5c73bfa934155719b4bcde2be Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 05:03:02 -0500 Subject: [PATCH 10/47] check for out-of-range numerical atom types --- src/label_map.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/label_map.cpp b/src/label_map.cpp index c01fb4b7a82..30aee840a88 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -385,6 +385,9 @@ bool LabelMap::is_complete(int mode) const int LabelMap::infer_bondtype(int type1, int type2) { + // check for out of range input + if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes)) return -1; + std::vector mytypes(2); mytypes[0] = typelabel[type1-1]; mytypes[1] = typelabel[type2-1]; @@ -421,6 +424,10 @@ int LabelMap::infer_bondtype(const std::vector &mytypes) int LabelMap::infer_angletype(int type1, int type2, int type3) { + // check for out of range input + if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || + (type3 > natomtypes)) + return -1; // convert numeric atom types to type label std::vector mytypes(3); @@ -463,6 +470,10 @@ int LabelMap::infer_angletype(const std::vector &mytypes) int LabelMap::infer_dihedraltype(int type1, int type2, int type3, int type4) { + // check for out of range input + if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || + (type3 > natomtypes) || (type4 < 1) || (type4 > natomtypes)) + return -1; // convert numeric atom types to type label std::vector mytypes(4); @@ -508,6 +519,10 @@ int LabelMap::infer_dihedraltype(const std::vector &mytypes) int LabelMap::infer_impropertype(int type1, int type2, int type3, int type4) { + // check for out of range input + if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || + (type3 > natomtypes) || (type4 < 1) || (type4 > natomtypes)) + return -1; // convert numeric atom types to type label std::vector mytypes(4); From fcce8f56dbfe088ec140946e063d3b218b963368 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 05:03:37 -0500 Subject: [PATCH 11/47] apply clang-format --- src/label_map.cpp | 128 ++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 66 deletions(-) diff --git a/src/label_map.cpp b/src/label_map.cpp index 30aee840a88..09b7400968e 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -302,38 +302,33 @@ int LabelMap::find(const std::string &mylabel, int mode) const const std::string &LabelMap::find(int i, int mode) const { switch (mode) { - case Atom::ATOM: - if ((i > 0) && (i <= atom->ntypes)) { - if (is_complete(mode)) - return typelabel[i-1]; - } - break; - case Atom::BOND: - if ((i > 0) && (i <= atom->nbondtypes)) { - if (is_complete(mode)) - return btypelabel[i-1]; - } - break; - case Atom::ANGLE: - if ((i > 0) && (i <= atom->nangletypes)) { - if (is_complete(mode)) - return atypelabel[i-1]; - } - break; - case Atom::DIHEDRAL: - if ((i > 0) && (i <= atom->ndihedraltypes)) { - if (is_complete(mode)) - return dtypelabel[i-1]; - } - break; - case Atom::IMPROPER: - if ((i > 0) && (i <= atom->nimpropertypes)) { - if (is_complete(mode)) - return itypelabel[i-1]; - } - break; - default: - return empty; + case Atom::ATOM: + if ((i > 0) && (i <= atom->ntypes)) { + if (is_complete(mode)) return typelabel[i - 1]; + } + break; + case Atom::BOND: + if ((i > 0) && (i <= atom->nbondtypes)) { + if (is_complete(mode)) return btypelabel[i - 1]; + } + break; + case Atom::ANGLE: + if ((i > 0) && (i <= atom->nangletypes)) { + if (is_complete(mode)) return atypelabel[i - 1]; + } + break; + case Atom::DIHEDRAL: + if ((i > 0) && (i <= atom->ndihedraltypes)) { + if (is_complete(mode)) return dtypelabel[i - 1]; + } + break; + case Atom::IMPROPER: + if ((i > 0) && (i <= atom->nimpropertypes)) { + if (is_complete(mode)) return itypelabel[i - 1]; + } + break; + default: + return empty; } return empty; } @@ -388,9 +383,10 @@ int LabelMap::infer_bondtype(int type1, int type2) // check for out of range input if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes)) return -1; + // convert numeric atom types to type label std::vector mytypes(2); - mytypes[0] = typelabel[type1-1]; - mytypes[1] = typelabel[type2-1]; + mytypes[0] = typelabel[type1 - 1]; + mytypes[1] = typelabel[type2 - 1]; if (mytypes[0].empty() || mytypes[1].empty()) return -1; return infer_bondtype(mytypes); @@ -404,18 +400,17 @@ int LabelMap::infer_bondtype(int type1, int type2) int LabelMap::infer_bondtype(const std::vector &mytypes) { // search for matching bond type label with symmetry considerations - std::vector btypes(2); for (int i = 0; i < nbondtypes; i++) { int status = parse_typelabel(2, btypelabel[i], btypes); - if (status != -1) + if ((status != -1) && (btypes.size() == 2)) if ((mytypes[0] == btypes[0] && mytypes[1] == btypes[1]) || - (mytypes[0] == btypes[1] && mytypes[1] == btypes[0])) return i+1; + (mytypes[0] == btypes[1] && mytypes[1] == btypes[0])) + return i + 1; } return -1; } - /* ---------------------------------------------------------------------- infer angle type from three atom types input/output is numeric types, uses type labels internally @@ -428,12 +423,12 @@ int LabelMap::infer_angletype(int type1, int type2, int type3) if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || (type3 > natomtypes)) return -1; - // convert numeric atom types to type label + // convert numeric atom types to type label std::vector mytypes(3); - mytypes[0] = typelabel[type1-1]; - mytypes[1] = typelabel[type2-1]; - mytypes[2] = typelabel[type3-1]; + mytypes[0] = typelabel[type1 - 1]; + mytypes[1] = typelabel[type2 - 1]; + mytypes[2] = typelabel[type3 - 1]; for (size_t i = 0; i < 3; i++) if (mytypes[i].empty()) return -1; @@ -456,12 +451,12 @@ int LabelMap::infer_angletype(const std::vector &mytypes) status = parse_typelabel(3, atypelabel[i], atypes); if (status != -1 && mytypes[1] == atypes[1]) if ((mytypes[0] == atypes[0] && mytypes[2] == atypes[2]) || - (mytypes[0] == atypes[2] && mytypes[2] == atypes[0])) return i+1; + (mytypes[0] == atypes[2] && mytypes[2] == atypes[0])) + return i + 1; } return -1; } - /* ---------------------------------------------------------------------- infer dihedral type from four atom types input/output is numeric types, uses type labels internally @@ -474,13 +469,13 @@ int LabelMap::infer_dihedraltype(int type1, int type2, int type3, int type4) if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || (type3 > natomtypes) || (type4 < 1) || (type4 > natomtypes)) return -1; - // convert numeric atom types to type label + // convert numeric atom types to type label std::vector mytypes(4); - mytypes[0] = typelabel[type1-1]; - mytypes[1] = typelabel[type2-1]; - mytypes[2] = typelabel[type3-1]; - mytypes[3] = typelabel[type4-1]; + mytypes[0] = typelabel[type1 - 1]; + mytypes[1] = typelabel[type2 - 1]; + mytypes[2] = typelabel[type3 - 1]; + mytypes[3] = typelabel[type4 - 1]; for (size_t i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; @@ -502,10 +497,11 @@ int LabelMap::infer_dihedraltype(const std::vector &mytypes) for (int i = 0; i < ndihedraltypes; i++) { status = parse_typelabel(4, dtypelabel[i], dtypes); if (status != -1) - if ((mytypes[0] == dtypes[0] && mytypes[1] == dtypes[1] && - mytypes[2] == dtypes[2] && mytypes[3] == dtypes[3]) || - (mytypes[3] == dtypes[0] && mytypes[2] == dtypes[1] && - mytypes[1] == dtypes[2] && mytypes[0] == dtypes[3])) return i+1; + if ((mytypes[0] == dtypes[0] && mytypes[1] == dtypes[1] && mytypes[2] == dtypes[2] && + mytypes[3] == dtypes[3]) || + (mytypes[3] == dtypes[0] && mytypes[2] == dtypes[1] && mytypes[1] == dtypes[2] && + mytypes[0] == dtypes[3])) + return i + 1; } return -1; } @@ -523,13 +519,13 @@ int LabelMap::infer_impropertype(int type1, int type2, int type3, int type4) if ((type1 < 1) || (type1 > natomtypes) || (type2 < 1) || (type2 > natomtypes) || (type3 < 1) || (type3 > natomtypes) || (type4 < 1) || (type4 > natomtypes)) return -1; - // convert numeric atom types to type label + // convert numeric atom types to type label std::vector mytypes(4); - mytypes[0] = typelabel[type1-1]; - mytypes[1] = typelabel[type2-1]; - mytypes[2] = typelabel[type3-1]; - mytypes[3] = typelabel[type4-1]; + mytypes[0] = typelabel[type1 - 1]; + mytypes[1] = typelabel[type2 - 1]; + mytypes[2] = typelabel[type3 - 1]; + mytypes[3] = typelabel[type4 - 1]; for (int i = 0; i < 4; i++) if (mytypes[i].empty()) return -1; @@ -556,7 +552,7 @@ int LabelMap::infer_impropertype(const std::vector &mytypes) status = parse_typelabel(4, itypelabel[i], itypes); if (status != -1) { for (int j = 0; j < 4; j++) { - if (force->improper->symmatoms[j] == 1) { + if (force->improper && (force->improper->symmatoms[j] == 1)) { if (mytypes[j] != itypes[j]) { status = -1; break; @@ -567,14 +563,14 @@ int LabelMap::infer_impropertype(const std::vector &mytypes) } } if (status == -1) continue; - std::sort(list1.begin(),list1.end()); - std::sort(list2.begin(),list2.end()); + std::sort(list1.begin(), list1.end()); + std::sort(list2.begin(), list2.end()); for (int j = 0; j < nlist; j++) if (list1[j] != list2[j]) { status = -1; break; } - if (status != -1) return i+1; + if (status != -1) return i + 1; } } return -1; @@ -584,15 +580,15 @@ int LabelMap::infer_impropertype(const std::vector &mytypes) return -1 if number of parsed strings is not equal to ntypes input ------------------------------------------------------------------------- */ -int LabelMap::parse_typelabel(int ntypes, std::string label, std::vector &types) +int LabelMap::parse_typelabel(int ntypes, const std::string &label, std::vector &types) { std::vector out; size_t start = label.find_first_not_of('-'); while (start != std::string::npos) { - size_t end = label.find('-', start); - out.emplace_back(label.substr(start, end - start)); - start = label.find_first_not_of('-', end); + size_t end = label.find('-', start); + out.emplace_back(label.substr(start, end - start)); + start = label.find_first_not_of('-', end); } if (out.size() != ntypes) return -1; types = std::move(out); From 842dbbde0c9f575663784f3eed2f10f65d1e7546 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 05:04:09 -0500 Subject: [PATCH 12/47] add unit tests for typelabel inference --- unittest/commands/test_labelmap.cpp | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/unittest/commands/test_labelmap.cpp b/unittest/commands/test_labelmap.cpp index 1025b46e84f..2175a68ae89 100644 --- a/unittest/commands/test_labelmap.cpp +++ b/unittest/commands/test_labelmap.cpp @@ -291,6 +291,57 @@ TEST_F(LabelMapTest, Topology) utils::expand_type(FLERR, "XX", Atom::DIHEDRAL, lmp);); TEST_FAILURE(".*ERROR: Improper type string XX not found in labelmap.*", utils::expand_type(FLERR, "XX", Atom::IMPROPER, lmp);); + + // check for inference + + BEGIN_HIDE_OUTPUT(); + command(R"(labelmap atom 2 "N2")"); + command(R"(labelmap bond 2 "C1-C1")"); + command(R"(labelmap bond 3 "N2-N2")"); + command(R"(labelmap angle 2 "N2-C1-N2")"); + END_HIDE_OUTPUT(); + + EXPECT_EQ(atom->lmap->infer_bondtype(1, 1), 2); + EXPECT_EQ(atom->lmap->infer_bondtype(1, 2), 1); + EXPECT_EQ(atom->lmap->infer_bondtype(2, 2), 3); + EXPECT_EQ(atom->lmap->infer_bondtype(2, 3), -1); + EXPECT_EQ(atom->lmap->infer_bondtype({"N2", "N2"}), 3); + EXPECT_EQ(atom->lmap->infer_bondtype({"C1", "N2"}), 1); + EXPECT_EQ(atom->lmap->infer_bondtype({"C1", "C1"}), 2); + EXPECT_EQ(atom->lmap->infer_bondtype({"N2", "C1"}), 1); + EXPECT_EQ(atom->lmap->infer_bondtype({"N3", "C1"}), -1); + EXPECT_EQ(atom->lmap->infer_angletype(1, 2, 1), 1); + EXPECT_EQ(atom->lmap->infer_angletype(2, 1, 2), 2); + EXPECT_EQ(atom->lmap->infer_angletype(1, 1, 1), -1); + EXPECT_EQ(atom->lmap->infer_angletype(3, 1, 1), -1); + EXPECT_EQ(atom->lmap->infer_angletype(1, 3, 1), -1); + EXPECT_EQ(atom->lmap->infer_angletype(1, 1, 3), -1); + EXPECT_EQ(atom->lmap->infer_angletype({"C1", "N2", "C1"}), 1); + EXPECT_EQ(atom->lmap->infer_angletype({"N2", "C1", "N2"}), 2); + EXPECT_EQ(atom->lmap->infer_angletype({"C1", "N2", "N2"}), -1); + EXPECT_EQ(atom->lmap->infer_angletype({"C1", "N3", "C1"}), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(1, 2, 1, 2), 1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(2, 1, 2, 2), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(2, 1, 2, 1), 1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(3, 1, 2, 1), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(2, 3, 2, 1), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(2, 1, 3, 1), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype(2, 1, 2, 3), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype({"C1", "N2", "C1", "N2"}), 1); + EXPECT_EQ(atom->lmap->infer_dihedraltype({"N2", "C1", "N2", "C1"}), 1); + EXPECT_EQ(atom->lmap->infer_dihedraltype({"C1", "N2", "N2", "C1"}), -1); + EXPECT_EQ(atom->lmap->infer_dihedraltype({"N3", "C1", "N2", "C1"}), -1); + EXPECT_EQ(atom->lmap->infer_impropertype(1, 2, 1, 2), 1); + EXPECT_EQ(atom->lmap->infer_impropertype(2, 1, 2, 2), -1); + EXPECT_EQ(atom->lmap->infer_impropertype(2, 1, 2, 1), 1); + EXPECT_EQ(atom->lmap->infer_impropertype(3, 1, 2, 1), -1); + EXPECT_EQ(atom->lmap->infer_impropertype(2, 3, 2, 1), -1); + EXPECT_EQ(atom->lmap->infer_impropertype(2, 1, 3, 1), -1); + EXPECT_EQ(atom->lmap->infer_impropertype(2, 1, 2, 3), -1); + EXPECT_EQ(atom->lmap->infer_impropertype({"C1", "N2", "C1", "N2"}), 1); + EXPECT_EQ(atom->lmap->infer_impropertype({"N2", "C1", "N2", "C1"}), 1); + EXPECT_EQ(atom->lmap->infer_impropertype({"C1", "N2", "N2", "C1"}), 1); + EXPECT_EQ(atom->lmap->infer_impropertype({"N3", "C1", "N2", "C1"}), -1); } } // namespace LAMMPS_NS From 497acb0a42400c51c969418209c07239b01589bf Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 05:09:03 -0500 Subject: [PATCH 13/47] simplify by using Tokenizer class --- src/label_map.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/label_map.cpp b/src/label_map.cpp index 09b7400968e..9d9d431e502 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "force.h" #include "improper.h" +#include "tokenizer.h" #include #include @@ -582,14 +583,7 @@ int LabelMap::infer_impropertype(const std::vector &mytypes) int LabelMap::parse_typelabel(int ntypes, const std::string &label, std::vector &types) { - std::vector out; - size_t start = label.find_first_not_of('-'); - - while (start != std::string::npos) { - size_t end = label.find('-', start); - out.emplace_back(label.substr(start, end - start)); - start = label.find_first_not_of('-', end); - } + auto out = Tokenizer(label,"-").as_vector(); if (out.size() != ntypes) return -1; types = std::move(out); return 1; From 97ace6b38a39d919933b2f037208838dfcd4b9fc Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 05:17:31 -0500 Subject: [PATCH 14/47] silence warning --- src/label_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/label_map.cpp b/src/label_map.cpp index 9d9d431e502..19b3746ef6d 100644 --- a/src/label_map.cpp +++ b/src/label_map.cpp @@ -584,7 +584,7 @@ int LabelMap::infer_impropertype(const std::vector &mytypes) int LabelMap::parse_typelabel(int ntypes, const std::string &label, std::vector &types) { auto out = Tokenizer(label,"-").as_vector(); - if (out.size() != ntypes) return -1; + if ((int)out.size() != ntypes) return -1; types = std::move(out); return 1; } From 2f6e7625346d6a79435038fa1add8a056985cab3 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 16:09:13 -0500 Subject: [PATCH 15/47] add links to YouTube videos of the GRAPHICS examples simulations --- examples/GRAPHICS/README | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/GRAPHICS/README b/examples/GRAPHICS/README index 138701e5e95..2bd67d1a270 100644 --- a/examples/GRAPHICS/README +++ b/examples/GRAPHICS/README @@ -11,5 +11,10 @@ automatically as they are created. Here is a list of the inputs and which graphics features they demonstrate - in.cubes-and-pyramids: visualization of body particles, uses progress bar + https://www.youtube.com/shorts/OYn_VVodnIg + - in.breakable: using autobond, color-by-property, adding objects and text + https://www.youtube.com/watch?v=f4hfPs7aCmI + - in.water-arrows: box of water molecules, uses many kinds of arrows + https://www.youtube.com/shorts/4Cm5p0SfgNU From ba162aea728dcf7eb45dc29bdd0dea1e5d8ab897 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 20 Jan 2026 16:46:44 -0500 Subject: [PATCH 16/47] update release steps docs. mention special settings for online manual version --- .github/release_steps.md | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/.github/release_steps.md b/.github/release_steps.md index 6a3bda821cf..76863f74e87 100644 --- a/.github/release_steps.md +++ b/.github/release_steps.md @@ -212,6 +212,26 @@ gh release upload patch_4Feb2025 LAMMPS-Win10-64bit-GUI-4Feb2025.exe The symbolic link is used to have a consistent naming scheme for the packages attached to the GitHub release page. +#### LAMMPS Online Manual + +Creating the online docs requires setting some environment variables to have +the extra box at the bottom of the navigation bar included that allows to +switch between release, stable, and develop branch versions of the manual. +Also the search box should use Google search instead of the javascript search. + +``` sh +cd lammps-release +make -C doc clean +make -C doc upgrade +env LAMMPS_WEBSITE_BUILD=1 LAMMPS_WEBSITE_BUILD_VERSION=release LAMMPS_WEBSITE_BASEURL="https://docs.lammps.org/" \ + make -C doc html WEB_SEARCH=YES +make -C doc pdf +mv doc/Manual.pdf doc/html +rsync -arpv doc/html/ www.lammps.org:/var/www/lammps/docs/release-new + +Then log into www.lammps.org and move the current folder away and +the new folder in its place and update permissions. + #### Clean up: ``` sh @@ -243,6 +263,7 @@ and installed (e.g. to `$HOME/.local`) as static libraries only: - jpeg - zlib - png +- voro++ (for VORONOI package) - Qt (for LAMMPS-GUI) When configuring LAMMPS the `cmake/presets/clang.cmake` should be used @@ -298,8 +319,8 @@ Check out the LAMMPS website repo https://github.com/lammps/lammps-website.git and edit the file `src/download.txt` for the new release. Test translation with `make html` and review `html/download.html` Then add and commit to git and -push the changes to GitHub. The Temple Jenkis cluster will -automatically update https://www.lammps.org/download.html accordingly. +push the changes to GitHub. A cron job will automatically update +https://www.lammps.org/ accordingly if there are changes. Also notify Steve of the release so he can update `src/bug.txt` on the website from the available release notes. @@ -373,11 +394,10 @@ git tag -s -m 'Update 2 for Stable LAMMPS version 29 August 2024' stable_29Aug20 git push git@github.com:lammps/lammps.git --tags maintenance stable ``` -Associate draft release notes with new tag and publish as "latest release". - -On https://ci.lammps.org/ go to "dev", "stable" and manually execute -the "update\_release" task. This will update https://docs.lammps.org/stable -and prepare a stable tarball. +Associate draft release notes with new tag and prepare and upload +various source and binary packages. Publish as "latest release" +only when all uploads are complete as they cannot be changed after +they are published since we are creating "immutable" releases now. ### Build and upload binary packages and source tarball to GitHub From 222e4aceaec8808ecc5559e4df2e37adb68bd5be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 21:58:33 +0000 Subject: [PATCH 17/47] Initial draft of LabelMap docs by GitHub Copilot --- doc/doxygen/Doxyfile.in | 2 + doc/src/Developer_utils.rst | 187 ++++++++++++++++++++++++++ src/label_map.h | 256 +++++++++++++++++++++++++++++++----- 3 files changed, 410 insertions(+), 35 deletions(-) diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index 1439f5b54f6..48f456f6d6a 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -431,6 +431,8 @@ INPUT = @LAMMPS_SOURCE_DIR@/utils.cpp \ @LAMMPS_SOURCE_DIR@/text_file_reader.h \ @LAMMPS_SOURCE_DIR@/potential_file_reader.cpp \ @LAMMPS_SOURCE_DIR@/potential_file_reader.h \ + @LAMMPS_SOURCE_DIR@/label_map.cpp \ + @LAMMPS_SOURCE_DIR@/label_map.h \ @LAMMPS_SOURCE_DIR@/my_page.cpp \ @LAMMPS_SOURCE_DIR@/my_page.h \ @LAMMPS_SOURCE_DIR@/my_pool_chunk.cpp \ diff --git a/doc/src/Developer_utils.rst b/doc/src/Developer_utils.rst index 866945fc881..9611316a943 100644 --- a/doc/src/Developer_utils.rst +++ b/doc/src/Developer_utils.rst @@ -558,6 +558,193 @@ A file that would be parsed by the reader code fragment looks like this: ---------- +Type label mapping +------------------ + +The :cpp:class:`LabelMap ` class provides a powerful +abstraction for mapping between symbolic type labels and numeric type indices +in LAMMPS simulations. Instead of referring to atom types, bond types, angle +types, dihedral types, and improper types by numeric indices (1, 2, 3, ...), +users can assign meaningful string labels (e.g., "C", "H", "N", "C-H", "H-C-H") +that improve readability and maintainability of input scripts. + +This feature is particularly useful for: + +* Complex molecular systems with many atom types +* Making input scripts self-documenting +* Avoiding errors from renumbering types +* Reusing force field definitions across different systems + +Type labels are set using the ``labelmap`` command in LAMMPS input scripts: + +.. code-block:: LAMMPS + + # Assign atom type labels + labelmap atom 1 C 2 H 3 N 4 O + + # Assign bond type labels (hyphen-delimited format) + labelmap bond 1 C-H 2 C-C 3 C-N + + # Assign angle type labels + labelmap angle 1 H-C-H 2 C-C-H 3 C-N-C + +Interaction types (bonds, angles, dihedrals, impropers) use a **hyphen-delimited +format** that combines constituent atom type labels. For example, a bond between +carbon and hydrogen atoms can be labeled "C-H", and an angle formed by +H-C-H atoms can be labeled "H-C-H". + +The LabelMap class supports **bidirectional lookup**: + +* Forward lookup: Convert a type label string to its numeric type index +* Reverse lookup: Convert a numeric type index to its label string + +It also provides **automatic type inference** for interactions based on their +constituent atom types. For instance, given atom type labels, the class can +infer the corresponding bond, angle, dihedral, or improper type from the +hyphen-delimited format. This handles symmetric matching automatically (e.g., +"C-H" matches "H-C" for bonds). + +Integration with utils namespace +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Several utility functions in the ``utils`` namespace work with type labels and +interact with the LabelMap class: + +* :cpp:func:`utils::is_type() ` - Validates whether + a string is a valid type label. Returns 1 for valid labels, 0 for numeric + types, -1 for invalid strings. Type labels cannot start with digits, '*', or + '#', and cannot contain whitespace or UTF-8 characters. + +* :cpp:func:`utils::expand_type() ` - Converts + a type label string to its numeric equivalent using the LabelMap. Returns + ``nullptr`` if no label map exists or if the string is not a valid type label. + +* :cpp:func:`utils::bounds_typelabel() ` - + Extended version of ``utils::bounds()`` that accepts type labels in addition + to numeric ranges. Uses ``expand_type()`` internally to convert labels to + numeric bounds before processing. + +These functions enable seamless integration of type labels throughout LAMMPS, +allowing commands that accept type specifications to work with both numeric +indices and symbolic labels. + +Usage examples +^^^^^^^^^^^^^^ + +**Example 1: Basic type label lookup** + +.. code-block:: c++ + :caption: Finding types from labels and vice versa + + #include "label_map.h" + #include "atom.h" + + // Access the label map (assumes atom->lmap exists and is initialized) + LabelMap *lmap = atom->lmap; + + // Forward lookup: Get numeric type from label + int ctype = lmap->find("C", Atom::ATOM); // Returns atom type for "C" + int htype = lmap->find("H", Atom::ATOM); // Returns atom type for "H" + int missing = lmap->find("X", Atom::ATOM); // Returns -1 (not found) + + // Reverse lookup: Get label from numeric type + const std::string &label1 = lmap->find(1, Atom::ATOM); // Returns label for type 1 + const std::string &label2 = lmap->find(2, Atom::BOND); // Returns bond label for type 2 + + // Check if all types have labels + bool complete = lmap->is_complete(Atom::ATOM); // Returns true if all atom types labeled + +**Example 2: Inferring bond types from atom types** + +.. code-block:: c++ + :caption: Using infer_bondtype() with numeric and label-based lookups + + #include "label_map.h" + #include "atom.h" + + LabelMap *lmap = atom->lmap; + + // Assume we have: labelmap atom 1 C 2 H 3 N + // And: labelmap bond 1 C-H 2 C-C 3 C-N + + // Infer bond type from numeric atom types + int bt1 = lmap->infer_bondtype(1, 2); // Returns 1 (C-H bond) + int bt2 = lmap->infer_bondtype(1, 1); // Returns 2 (C-C bond) + int bt3 = lmap->infer_bondtype(1, 3); // Returns 3 (C-N bond) + + // Infer bond type from atom type labels (handles symmetry automatically) + int bt4 = lmap->infer_bondtype({"C", "H"}); // Returns 1 (C-H) + int bt5 = lmap->infer_bondtype({"H", "C"}); // Returns 1 (symmetric match) + int bt6 = lmap->infer_bondtype({"C", "N"}); // Returns 3 (C-N) + +**Example 3: Inferring angle and dihedral types** + +.. code-block:: c++ + :caption: Using infer_angletype() and infer_dihedraltype() + + #include "label_map.h" + #include "atom.h" + + LabelMap *lmap = atom->lmap; + + // Assume: labelmap angle 1 H-C-H 2 C-C-H 3 C-N-C + // And: labelmap dihedral 1 H-C-C-H 2 C-C-N-H + + // Infer angle type from numeric atom types + int at1 = lmap->infer_angletype(2, 1, 2); // Returns 1 (H-C-H) + int at2 = lmap->infer_angletype(1, 1, 2); // Returns 2 (C-C-H) + + // Infer angle type from labels (supports bidirectional matching) + int at3 = lmap->infer_angletype({"H", "C", "H"}); // Returns 1 + int at4 = lmap->infer_angletype({"H", "C", "H"}); // Returns 1 (same angle reversed) + + // Infer dihedral type from labels + int dt1 = lmap->infer_dihedraltype({"H", "C", "C", "H"}); // Returns 1 + int dt2 = lmap->infer_dihedraltype({"H", "C", "C", "H"}); // Returns 1 (bidirectional) + int dt3 = lmap->infer_dihedraltype({"C", "C", "N", "H"}); // Returns 2 + +**Example 4: Using utils functions with type labels** + +.. code-block:: c++ + :caption: Validating and expanding type labels with utils functions + + #include "utils.h" + #include "lammps.h" + + using namespace LAMMPS_NS; + + LAMMPS *lmp = /* ... */; + + // Validate type label strings + int result1 = utils::is_type("C"); // Returns 1 (valid label) + int result2 = utils::is_type("123"); // Returns 0 (numeric type) + int result3 = utils::is_type("*"); // Returns -1 (invalid - starts with *) + int result4 = utils::is_type("C H"); // Returns -1 (invalid - contains whitespace) + + // Convert type label to numeric string + char *numstr = utils::expand_type(__FILE__, __LINE__, "C", Atom::ATOM, lmp); + if (numstr) { + // Use the numeric type string + delete[] numstr; // Must delete after use + } + + // Convert type label to integer + int type = utils::expand_type_int(__FILE__, __LINE__, "C", Atom::ATOM, lmp, true); + // The 'true' argument enables range verification + + // Use bounds_typelabel for ranges with label support + int lo, hi; + utils::bounds_typelabel(__FILE__, __LINE__, "C:H", 1, 10, lo, hi, lmp, Atom::ATOM); + // Expands "C:H" to numeric range, e.g., "1:2" -> lo=1, hi=2 + +---------- + +.. doxygenclass:: LAMMPS_NS::LabelMap + :project: progguide + :members: + +---------- + Memory pool classes ------------------- diff --git a/src/label_map.h b/src/label_map.h index 4a4ed51f646..8e8dfd02b95 100644 --- a/src/label_map.h +++ b/src/label_map.h @@ -11,6 +11,8 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ +/*! \file label_map.h */ + #ifndef LMP_LABEL_MAP_H #define LMP_LABEL_MAP_H @@ -20,6 +22,29 @@ namespace LAMMPS_NS { +/*! \class LabelMap + * \brief Manage type labels for atoms and interactions (bonds, angles, dihedrals, impropers) + * + * The LabelMap class provides functionality to map between string labels and + * numeric type indices for atoms, bonds, angles, dihedrals, and impropers in LAMMPS. + * This enables users to reference types by symbolic names (e.g., "C", "H", "C-H") + * instead of numeric indices, improving readability and maintainability of input scripts. + * + * Type labels use a hyphen-delimited format for interaction types: + * - Bond types: "atom1-atom2" (e.g., "C-H", "N-O") + * - Angle types: "atom1-atom2-atom3" (e.g., "H-C-H", "C-N-C") + * - Dihedral types: "atom1-atom2-atom3-atom4" (e.g., "C-C-N-H") + * - Improper types: "atom1-atom2-atom3-atom4" (e.g., "C-N-C-C") + * + * The class supports bidirectional lookup (label ↔ type) and can infer + * interaction types from constituent atom types using the hyphen-delimited format. + * + * Related utilities in the utils namespace: + * - utils::is_type() - Validate type label strings + * - utils::expand_type() - Convert type labels to numeric types + * - utils::bounds_typelabel() - Process bounds with type label support + */ + class LabelMap : protected Pointers { friend class AtomVec; friend class DumpCustom; @@ -28,76 +53,237 @@ class LabelMap : protected Pointers { friend class ReadData; public: + /*! Construct a LabelMap instance + * + * \param lmp Pointer to LAMMPS instance + * \param natomtypes Number of atom types + * \param nbondtypes Number of bond types + * \param nangletypes Number of angle types + * \param ndihedraltypes Number of dihedral types + * \param nimpropertypes Number of improper types */ LabelMap(LAMMPS *lmp, int, int, int, int, int); ~LabelMap() override; - void modify_lmap(int, char **); // labelmap command in the input script - void merge_lmap(LabelMap *, int); // copy another lmap into this one - void create_lmap2lmap(LabelMap *, int); // index mapping between two lmaps - int find(const std::string &, int) const; // find numeric type of type label - const std::string &find(int, int) const; // find type label for numeric type - bool is_complete(int) const; // check if all types are assigned + /*! Process labelmap command from input script + * + * Parse and store type label mappings from LAMMPS input commands. + * Supported syntax: labelmap