diff --git a/.github/release_steps.md b/.github/release_steps.md index 1ffd3cb291..b08c8e892a 100644 --- a/.github/release_steps.md +++ b/.github/release_steps.md @@ -104,8 +104,8 @@ with a future release) from the `lammps-static` folder. rm -rf release-packages mkdir release-packages cd release-packages -wget https://download.lammps.org/static/fedora41_musl.sif -apptainer shell fedora41_musl.sif +wget https://download.lammps.org/static/fedora41_musl_mingw.sif +apptainer shell fedora41_musl_mingw.sif git clone -b release --depth 10 https://github.com/lammps/lammps.git lammps-release cmake -S lammps-release/cmake -B build-release -G Ninja -D CMAKE_INSTALL_PREFIX=$PWD/lammps-static -D CMAKE_TOOLCHAIN_FILE=/usr/musl/share/cmake/linux-musl.cmake -C lammps-release/cmake/presets/most.cmake -C lammps-release/cmake/presets/kokkos-openmp.cmake -D DOWNLOAD_POTENTIALS=OFF -D BUILD_MPI=OFF -D BUILD_TESTING=OFF -D CMAKE_BUILD_TYPE=Release -D PKG_ATC=ON -D PKG_AWPMD=ON -D PKG_MANIFOLD=ON -D PKG_MESONT=ON -D PKG_MGPT=ON -D PKG_ML-PACE=ON -D PKG_ML-RANN=ON -D PKG_MOLFILE=ON -D PKG_PTM=ON -D PKG_QTB=ON -D PKG_SMTBQ=ON cmake --build build-release --target all @@ -204,7 +204,7 @@ cd .. rm -r release-packages ``` -#### Build Multi-arch App-bundle for macOS +#### Build Multi-arch App-bundle with GUI for macOS Building app-bundles for macOS is not as easily automated and portable as some of the other steps. It requires a machine actually running @@ -251,7 +251,7 @@ attached to the GitHub release page. We are currently building the application images on macOS 12 (aka Monterey). -#### Build Linux x86_64 binary tarball on Ubuntu 20.04LTS +#### Build Linux x86_64 binary tarball with GUI on Ubuntu 20.04LTS While the flatpak Linux version uses portable runtime libraries provided by the flatpak environment, we also build regular Linux executables that diff --git a/.github/workflows/check-cpp23.yml b/.github/workflows/check-cpp23.yml index 2cd53f2208..15b16e71e4 100644 --- a/.github/workflows/check-cpp23.yml +++ b/.github/workflows/check-cpp23.yml @@ -1,4 +1,4 @@ -# GitHub action to build LAMMPS on Linux with gcc and C++23 +# GitHub action to build LAMMPS on Linux with gcc or clang and C++23 name: "Check for C++23 Compatibility" on: @@ -11,11 +11,19 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: name: Build with C++23 support enabled if: ${{ github.repository == 'lammps/lammps' }} runs-on: ubuntu-latest + strategy: + max-parallel: 2 + matrix: + idx: [ gcc, clang ] env: CCACHE_DIR: ${{ github.workspace }}/.ccache @@ -29,8 +37,11 @@ jobs: run: | sudo apt-get update sudo apt-get install -y ccache \ - libeigen3-dev \ + clang \ libcurl4-openssl-dev \ + libeigen3-dev \ + libfftw3-dev \ + libomp-dev \ mold \ mpi-default-bin \ mpi-default-dev \ @@ -58,14 +69,14 @@ jobs: cmake -S cmake -B build \ -C cmake/presets/most.cmake \ -C cmake/presets/kokkos-openmp.cmake \ + -C cmake/presets/${{ matrix.idx }}.cmake \ -D CMAKE_CXX_STANDARD=23 \ - -D CMAKE_CXX_COMPILER=g++ \ - -D CMAKE_C_COMPILER=gcc \ -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \ -D CMAKE_C_COMPILER_LAUNCHER=ccache \ -D CMAKE_BUILD_TYPE=Debug \ -D CMAKE_CXX_FLAGS_DEBUG="-Og -g" \ -D DOWNLOAD_POTENTIALS=off \ + -D FFT=KISS \ -D BUILD_MPI=on \ -D BUILD_SHARED_LIBS=on \ -D BUILD_TOOLS=off \ diff --git a/cmake/Modules/Packages/GPU.cmake b/cmake/Modules/Packages/GPU.cmake index 67d2221ec4..dd4ac31025 100644 --- a/cmake/Modules/Packages/GPU.cmake +++ b/cmake/Modules/Packages/GPU.cmake @@ -189,7 +189,7 @@ if(GPU_API STREQUAL "CUDA") endif() add_executable(nvc_get_devices ${LAMMPS_LIB_SOURCE_DIR}/gpu/geryon/ucl_get_devices.cpp) - target_compile_definitions(nvc_get_devices PRIVATE -DUCL_CUDADR) + target_compile_definitions(nvc_get_devices PRIVATE -DUCL_CUDADR -DLAMMPS_${LAMMPS_SIZES}) target_link_libraries(nvc_get_devices PRIVATE ${CUDA_LIBRARIES} ${CUDA_CUDA_LIBRARY}) target_include_directories(nvc_get_devices PRIVATE ${CUDA_INCLUDE_DIRS}) @@ -254,7 +254,7 @@ elseif(GPU_API STREQUAL "OPENCL") add_library(gpu STATIC ${GPU_LIB_SOURCES}) target_link_libraries(gpu PRIVATE OpenCL::OpenCL) target_include_directories(gpu PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gpu) - target_compile_definitions(gpu PRIVATE -DUSE_OPENCL -D_${GPU_PREC_SETTING} -DLAMMPS_${LAMMPS_SIZES}) + target_compile_definitions(gpu PRIVATE -DUSE_OPENCL -D_${GPU_PREC_SETTING}) if(GPU_DEBUG) target_compile_definitions(gpu PRIVATE -DUCL_DEBUG -DGERYON_KERNEL_DUMP) else() @@ -490,5 +490,6 @@ else() endif() set_target_properties(gpu PROPERTIES OUTPUT_NAME lammps_gpu${LAMMPS_MACHINE}) +target_compile_definitions(gpu PRIVATE -DLAMMPS_${LAMMPS_SIZES}) target_sources(lammps PRIVATE ${GPU_SOURCES}) target_include_directories(lammps PRIVATE ${GPU_SOURCES_DIR}) diff --git a/cmake/Modules/Tools.cmake b/cmake/Modules/Tools.cmake index 94e077d51e..e37e262bfe 100644 --- a/cmake/Modules/Tools.cmake +++ b/cmake/Modules/Tools.cmake @@ -6,6 +6,10 @@ if(BUILD_TOOLS) add_executable(stl_bin2txt ${LAMMPS_TOOLS_DIR}/stl_bin2txt.cpp) install(TARGETS stl_bin2txt DESTINATION ${CMAKE_INSTALL_BINDIR}) + add_executable(reformat-json ${LAMMPS_TOOLS_DIR}/json/reformat-json.cpp) + target_include_directories(reformat-json PRIVATE ${LAMMPS_SOURCE_DIR}) + install(TARGETS reformat-json DESTINATION ${CMAKE_INSTALL_BINDIR}) + include(CheckGeneratorSupport) if(CMAKE_GENERATOR_SUPPORT_FORTRAN) include(CheckLanguage) diff --git a/cmake/presets/kokkos-cuda.cmake b/cmake/presets/kokkos-cuda.cmake index 31942b8fae..bb311839f8 100644 --- a/cmake/presets/kokkos-cuda.cmake +++ b/cmake/presets/kokkos-cuda.cmake @@ -1,9 +1,8 @@ # preset that enables KOKKOS and selects CUDA compilation with OpenMP -# enabled as well. The GPU architecture *must* match your hardware +# enabled as well. The GPU architecture *must* match your hardware (If not manually set, Kokkos will try to autodetect it). set(PKG_KOKKOS ON CACHE BOOL "" FORCE) set(Kokkos_ENABLE_SERIAL ON CACHE BOOL "" FORCE) set(Kokkos_ENABLE_CUDA ON CACHE BOOL "" FORCE) -set(Kokkos_ARCH_PASCAL60 ON CACHE BOOL "" FORCE) set(BUILD_OMP ON CACHE BOOL "" FORCE) get_filename_component(NVCC_WRAPPER_CMD ${CMAKE_CURRENT_SOURCE_DIR}/../lib/kokkos/bin/nvcc_wrapper ABSOLUTE) set(CMAKE_CXX_COMPILER ${NVCC_WRAPPER_CMD} CACHE FILEPATH "" FORCE) diff --git a/doc/src/Commands_fix.rst b/doc/src/Commands_fix.rst index f8c0937e9f..4bdb3c3bc8 100644 --- a/doc/src/Commands_fix.rst +++ b/doc/src/Commands_fix.rst @@ -113,7 +113,6 @@ OPT. * :doc:`mvv/tdpd ` * :doc:`neb ` * :doc:`neb/spin ` - * :doc:`neighbor/swap ` * :doc:`nonaffine/displacement ` * :doc:`nph (ko) ` * :doc:`nph/asphere (o) ` @@ -219,6 +218,7 @@ OPT. * :doc:`rigid/small (o) ` * :doc:`rx (k) ` * :doc:`saed/vtk ` + * :doc:`set ` * :doc:`setforce (k) ` * :doc:`setforce/spin ` * :doc:`sgcmc ` diff --git a/doc/src/Commands_kspace.rst b/doc/src/Commands_kspace.rst index 0d9b34a2cc..c37d9eee48 100644 --- a/doc/src/Commands_kspace.rst +++ b/doc/src/Commands_kspace.rst @@ -31,3 +31,5 @@ OPT. * :doc:`pppm/dielectric ` * :doc:`pppm/electrode (i) ` * :doc:`scafacos ` + * :doc:`zero ` + diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 354350dde7..a26a522a01 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -68,24 +68,25 @@ Members of ``lammpsplugin_t`` * - author - String with the name and email of the author * - creator.v1 - - Pointer to factory function for pair, bond, angle, dihedral, improper, kspace, or command styles + - Pointer to factory function for pair, bond, angle, dihedral, improper, kspace, command, or minimize styles * - creator.v2 - - Pointer to factory function for compute, fix, or region styles + - Pointer to factory function for compute, fix, region, or run styles * - handle - Pointer to the open DSO file handle Only one of the two alternate creator entries can be used at a time and which of those is determined by the style of plugin. The "creator.v1" element is for factory functions of supported styles computing forces -(i.e. pair, bond, angle, dihedral, or improper styles) or command styles -and the function takes as single argument the pointer to the LAMMPS -instance. The factory function is cast to the ``lammpsplugin_factory1`` -type before assignment. The "creator.v2" element is for factory -functions creating an instance of a fix, compute, or region style and -takes three arguments: a pointer to the LAMMPS instance, an integer with -the length of the argument list and a ``char **`` pointer to the list of -arguments. The factory function pointer needs to be cast to the -``lammpsplugin_factory2`` type before assignment. +(i.e. pair, bond, angle, dihedral, or improper styles), command styles, +or minimize styles and the function takes as single argument the pointer +to the LAMMPS instance. The factory function is cast to the +``lammpsplugin_factory1`` type before assignment. The "creator.v2" +element is for factory functions creating an instance of a fix, compute, +region, or run style and takes three arguments: a pointer to the LAMMPS +instance, an integer with the length of the argument list and a ``char +**`` pointer to the list of arguments. The factory function pointer +needs to be cast to the ``lammpsplugin_factory2`` type before +assignment. Pair style example ^^^^^^^^^^^^^^^^^^ @@ -247,8 +248,8 @@ DSO handle. The registration function is called with a pointer to the address of this struct and the pointer of the LAMMPS class. The registration function will then add the factory function of the plugin style to the respective style map under the provided name. It will also make a copy of the struct -in a list of all loaded plugins and update the reference counter for loaded -plugins from this specific DSO file. +in a global list of all loaded plugins and update the reference counter for +loaded plugins from this specific DSO file. The pair style itself (i.e. the PairMorse2 class in this example) can be written just like any other pair style that is included in LAMMPS. For @@ -263,6 +264,21 @@ the plugin will override the existing code. This can be used to modify the behavior of existing styles or to debug new versions of them without having to re-compile or re-install all of LAMMPS. +.. versionchanged:: 12Jun2025 + +When using the :doc:`clear ` command, plugins are not unloaded +but restored to their respective style maps. This also applies when +multiple LAMMPS instances are created and deleted through the library +interface. The :doc:`plugin load ` load command may be issued +again, but for existing plugins they will be skipped. To replace +plugins they must be explicitly unloaded with :doc:`plugin unload +`. When multiple LAMMPS instances are created concurrently, any +loaded plugins will be added to the global list of plugins, but are not +immediately available to any LAMMPS instance that was created before +loading the plugin. To "import" such plugins, the :doc:`plugin restore +` may be used. Plugins are only removed when they are explicitly +unloaded or the LAMMPS interface is "finalized". + Compiling plugins ^^^^^^^^^^^^^^^^^ diff --git a/doc/src/Fortran.rst b/doc/src/Fortran.rst index 0a8434f63d..3d61473068 100644 --- a/doc/src/Fortran.rst +++ b/doc/src/Fortran.rst @@ -69,10 +69,11 @@ statement. Internally, it will call either :cpp:func:`lammps_open_fortran` or :cpp:func:`lammps_open_no_mpi` from the C library API to create the class instance. All arguments are optional and :cpp:func:`lammps_mpi_init` will be called automatically -if it is needed. Similarly, a possible call to -:cpp:func:`lammps_mpi_finalize` is integrated into the :f:func:`close` -function and triggered with the optional logical argument set to -``.TRUE.``. Here is a simple example: +if it is needed. Similarly, optional calls to +:cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, +:cpp:func:`lammps_python_finalize`, and :cpp:func:`lammps_plugin_finalize` +are integrated into the :f:func:`close` function and triggered with the +optional logical argument set to ``.TRUE.``. Here is a simple example: .. code-block:: fortran @@ -521,8 +522,8 @@ Procedures Bound to the :f:type:`lammps` Derived Type This method will close down the LAMMPS instance through calling :cpp:func:`lammps_close`. If the *finalize* argument is present and has a value of ``.TRUE.``, then this subroutine also calls - :cpp:func:`lammps_kokkos_finalize` and - :cpp:func:`lammps_mpi_finalize`. + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_mpi_finalize`, + :cpp:func:`lammps_python_finalize`, and :cpp:func:`lammps_plugin_finalize`. :o finalize: shut down the MPI environment of the LAMMPS library if ``.TRUE.``. @@ -530,6 +531,8 @@ Procedures Bound to the :f:type:`lammps` Derived Type :to: :cpp:func:`lammps_close` :to: :cpp:func:`lammps_mpi_finalize` :to: :cpp:func:`lammps_kokkos_finalize` + :to: :cpp:func:`lammps_python_finalize` + :to: :cpp:func:`lammps_plugin_finalize` -------- diff --git a/doc/src/Install_git.rst b/doc/src/Install_git.rst index 717c7b3a9e..30a96c6e5d 100644 --- a/doc/src/Install_git.rst +++ b/doc/src/Install_git.rst @@ -21,8 +21,8 @@ You can follow the LAMMPS development on 4 different git branches: * **develop** : this branch follows the ongoing development and is updated with every merge commit of a pull request -* **release** : this branch is updated with every "feature release"; - updates are always "fast-forward" merges from *develop* +* **release** : this branch is updated with every "feature release" + and updates are always "fast-forward" merges from *develop* * **maintenance** : this branch collects back-ported bug fixes from the *develop* branch to the *stable* branch. It is used to update the *stable* branch for "stable update releases". diff --git a/doc/src/Library_create.rst b/doc/src/Library_create.rst index 546db9b3be..5566b04e9b 100644 --- a/doc/src/Library_create.rst +++ b/doc/src/Library_create.rst @@ -11,6 +11,7 @@ This section documents the following functions: - :cpp:func:`lammps_mpi_finalize` - :cpp:func:`lammps_kokkos_finalize` - :cpp:func:`lammps_python_finalize` +- :cpp:func:`lammps_plugin_finalize` - :cpp:func:`lammps_error` -------------------- @@ -119,5 +120,10 @@ calling program. ----------------------- +.. doxygenfunction:: lammps_plugin_finalize + :project: progguide + +----------------------- + .. doxygenfunction:: lammps_error :project: progguide diff --git a/doc/src/Python_call.rst b/doc/src/Python_call.rst index 796adfe1c0..338b7f85bb 100644 --- a/doc/src/Python_call.rst +++ b/doc/src/Python_call.rst @@ -5,18 +5,28 @@ LAMMPS has several commands which can be used to invoke Python code directly from an input script: * :doc:`python ` -* :doc:`variable python ` +* :doc:`python-style variables ` +* :doc:`equal-style and atom-style variables with formulas containing Python function wrappers ` * :doc:`fix python/invoke ` * :doc:`pair_style python ` -The :doc:`python ` command which can be used to define and -execute a Python function that you write the code for. The Python -function can also be assigned to a LAMMPS python-style variable via -the :doc:`variable ` command. Each time the variable is +The :doc:`python ` command can be used to define and execute a +Python function that you write the code for. The Python function can +also be assigned to a LAMMPS python-style variable via the +:doc:`variable ` command. Each time the variable is evaluated, either in the LAMMPS input script itself, or by another LAMMPS command that uses the variable, this will trigger the Python function to be invoked. +The Python function can also be referenced in the formula used to +define an :doc:`equal-style or atom-style variable `, using +the syntax for a :doc:`Python function wrapper `. This make +it easy to pass LAMMPS-related arguments to the Python function, as +well as to invoke it whenever the equal- or atom-style variable is +evaluated. For an atom-style variable it means the Python function +can be invoked once per atom, using per-atom properties as arguments +to the function. + The Python code for the function can be included directly in the input script or in an auxiliary file. The function can have arguments which are mapped to LAMMPS variables (also defined in the input script) and diff --git a/doc/src/Speed_compare.rst b/doc/src/Speed_compare.rst index 3f72e5d715..d59c37c34d 100644 --- a/doc/src/Speed_compare.rst +++ b/doc/src/Speed_compare.rst @@ -75,15 +75,34 @@ section below for examples where this has been done. **Differences between the GPU and KOKKOS packages:** * The GPU package accelerates only pair force, neighbor list, and (parts - of) PPPM calculations. The KOKKOS package attempts to run most of the + of) PPPM calculations (and runs the remaining force computations on + the CPU concurrently). The KOKKOS package attempts to run most of the calculation on the GPU, but can transparently support non-accelerated code (with a performance penalty due to having data transfers between host and GPU). +* The list of which styles are accelerated by the GPU or KOKKOS package + differs with some overlap. * The GPU package requires neighbor lists to be built on the CPU when using hybrid pair styles, exclusion lists, or a triclinic simulation box. -* The GPU package can be compiled for CUDA, HIP, or OpenCL and thus supports - NVIDIA, AMD, and Intel GPUs well. On NVIDIA hardware, using CUDA is - typically resulting in equal or better performance over OpenCL. -* OpenCL in the GPU package does theoretically also support Intel CPUs or - Intel Xeon Phi, but the native support for those in KOKKOS (or INTEL) - is superior. +* The GPU package benefits from running multiple MPI processes (2-8) per + GPU to parallelize the non-GPU accelerated styles. The KOKKOS package + usually not, especially when all parts of the calculation have KOKKOS + support. +* The GPU package can be compiled for CUDA, HIP, or OpenCL and thus + supports NVIDIA, AMD, and Intel GPUs well. On NVIDIA or AMD hardware, + using native CUDA or HIP compilation, respectively, with either GPU or + KOKKOS results in equal or better performance over OpenCL. +* OpenCL in the GPU package supports NVIDIA, AMD, and Intel GPUs at the + *same time* and with the *same executable*. KOKKOS currently does not + support OpenCL. +* The GPU package supports single precision floating point, mixed + precision floating point, and double precision floating point math on + the GPU. This must be chosen at compile time. KOKKOS currently only + supports double precision floating point math. Using single or mixed + precision (recommended) results in significantly improved performance + on consumer GPUs for some loss in accuracy (which is rather small with + mixed precision). Single and mixed precision support for KOKKOS is in + development (no ETA yet). +* Some pair styles (for example :doc:`snap `, :doc:`mliap + ` or :doc:`reaxff ` in the KOKKOS package have + seen extensive optimizations and specializations for GPUs and CPUs. diff --git a/doc/src/Speed_measure.rst b/doc/src/Speed_measure.rst index 888e8d9790..2fe838cb22 100644 --- a/doc/src/Speed_measure.rst +++ b/doc/src/Speed_measure.rst @@ -1,16 +1,218 @@ Measuring performance ===================== -Before trying to make your simulation run faster, you should -understand how it currently performs and where the bottlenecks are. +Factors that influence performance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The best way to do this is run the your system (actual number of -atoms) for a modest number of timesteps (say 100 steps) on several -different processor counts, including a single processor if possible. -Do this for an equilibrium version of your system, so that the -100-step timings are representative of a much longer run. There is -typically no need to run for 1000s of timesteps to get accurate -timings; you can simply extrapolate from short runs. +Before trying to make your simulation run faster, you should understand +how it currently performs and where the bottlenecks are. We generally +distinguish between serial performance (how fast can a single process do +the calculations?) and parallel efficiency (how much faster does a +calculation get by using more processes?). There are many factors +affecting either and below are some lists discussing some commonly +known but also some less known factors. + +Factors affecting serial performance (in no specific order): + +* CPU hardware: clock rate, cache sizes, CPU architecture (instructions + per clock, vectorization support, fused multiply-add support and more) +* RAM speed and number of channels that the CPU can use to access RAM +* Cooling: CPUs can change the CPU clock based on thermal load, thus the + degree of cooling can affect the speed of a CPU. Sometimes even the + temperature of neighboring compute nodes in a cluster can make a + difference. +* Compiler optimization: most of LAMMPS is written to be easy to modify + and thus compiler optimization can speed up calculations. However, too + aggressive compiler optimization can produce incorrect results or + crashes (during compilation or at runtime). +* Source code improvements: styles in the OPT, OPENMP, and INTEL package + can be faster than their base implementation due to improved data + access patterns, cache efficiency, or vectorization. Compiler optimization + is required to take full advantage of these. +* Number and kind of fixes, computes, or variables used during a simulation, + especially if they result in collective communication operations +* Pair style cutoffs and system density: calculations get slower the more + neighbors are in the neighbor list and thus for which interactions need + to be computed. Force fields with pair styles that compute interactions + between triples or quadruples of atoms or that use embedding energies or + charge equilibration will need to walk the neighbor lists multiple times. +* Neighbor list settings: tradeoff between neighbor list skin (larger + skin = more neighbors, more distances to compute before applying the + cutoff) and frequency of neighbor list builds (larger skin = fewer + neighbor list builds). +* Proximity of per-atom data in physical memory that for atoms that are + close in space improves cache efficiency (thus LAMMPS will by default + sort atoms in local storage accordingly) +* Using r-RESPA multi-timestepping or a SHAKE or RATTLE fix to constrain + bonds with higher-frequency vibrations may allow a larger (outer) timestep + and thus fewer force evaluations (usually the most time consuming step in + MD) for the same simulated time (with some tradeoff in accuracy). + +Factors affecting parallel efficiency (in no specific order): + +* Bandwidth and latency of communication between processes. This can vary a + lot between processes on the same CPU or physical node and processes + on different physical nodes and there vary between different + communication technologies (like Ethernet or InfiniBand or other + high-speed interconnects) +* Frequency and complexity of communication patterns required +* Number of "work units" (usually correlated with the number of atoms + and choice of force field) per MPI-process required for one time step + (if this number becomes too small, the cost of communication becomes + dominant). +* Choice of parallelization method (MPI-only, OpenMP-only, MPI+OpenMP, + MPI+GPU, MPI+GPU+OpenMP) +* Algorithmic complexity of the chosen force field (pair-wise vs. many-body + potential, Ewald vs. PPPM vs. (compensated or smoothed) cutoff-Coulomb) +* Communication cutoff: a larger cutoff results in more ghost atoms and + thus more data that needs to be communicated +* Frequency of neighbor list builds: during a neighbor list build the + domain decomposition is updated and the list of ghost atoms rebuilt + which requires multiple global communication steps +* FFT-grid settings and number of MPI processes for kspace style PPPM: + PPPM uses parallel 3d FFTs which will drop much faster in parallel + efficiency with respect to the number of MPI processes than other + parts of the force computation. Thus using MPI+OpenMP parallelization + or :doc:`run style verlet/split ` can improve parallel + efficiency by limiting the number of MPI processes used for the FFTs. +* Load (im-)balance: LAMMPS' domain decomposition assumes that atoms are + evenly distributed across the entire simulation box. If there are + areas of vacuum, this may lead to different amounts of work for + different MPI processes. Using the :doc:`processors command + ` to change the spatial decomposition, or MPI+OpenMP + parallelization instead of only-MPI to have larger sub-domains, or the + (fix) balance command (without or with switching to communication style + tiled) to change the sub-domain volumes are all methods that + can help to avoid load imbalances. + +Examples comparing serial performance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before looking at your own input deck(s), you should get some reference +data from a known input so that you know what kind of performance you +should expect from your input. For the following we therefore use the +``in.rhodo.scaled`` input file and ``data.rhodo`` data file from the +``bench`` folder. This is a system of 32000 atoms using the CHARMM force +field and long-range electrostatics running for 100 MD steps. The +performance data is printed at the end of a run and only measures the +performance during propagation and excludes the setup phase. + +Running with a single MPI process on an AMD Ryzen Threadripper PRO +9985WX CPU (64 cores, 128 threads, base clock: 3.2GHz, max. clock +5.4GHz, L1/L2/L3 cache 5MB/64MB/256MB, 8 DDR5-6400 memory channels) one +gets the following performance report: + +.. code-block:: + + Performance: 1.232 ns/day, 19.476 hours/ns, 7.131 timesteps/s, 228.197 katom-step/s + 99.2% CPU use with 1 MPI tasks x 1 OpenMP threads + +The %CPU value should be at 100% or very close. Lower values would +be an indication that there are *other* processes also using the same +CPU core and thus invalidating the performance data. The katom-step/s +value is best suited for comparisons, since it is fairly independent +from the system size. The `in.rhodo.scaled` input can be easily made +larger through replication in the three dimensions by settings variables +"x", "y", "z" to values other than 1 from the command line with the +"-var" flag. Example: + +- 32000 atoms: 228.8 katom-step/s +- 64000 atoms: 231.6 katom-step/s +- 128000 atoms: 231.1 katom-step/s +- 256000 atoms: 226.4 katom-step/s +- 864000 atoms: 229.6 katom-step/s + +Comparing to an AMD Ryzen 7 7840HS CPU (8 cores, 16 threads, base clock +3.8GHz, max. clock 5.1GHz, L1/L2/L3 cache 512kB/8MB/16MB, 2 DDR5-5600 +memory channels), we get similar single core performance (~220 +katom-step/s vs. ~230 katom-step/s) due to the similar clock and +architecture: + +- 32000 atoms: 219.8 katom-step/s +- 64000 atoms: 222.5 katom-step/s +- 128000 atoms: 216.8 katom-step/s +- 256000 atoms: 221.0 katom-step/s +- 864000 atoms: 221.1 katom-step/s + +Switching to an older Intel Xeon E5-2650 v4 CPU (12 cores, 12 threads, +base clock 2.2GHz, max. clock 2.9GHz, L1/L2/L3 cache (64kB/256kB/30MB, 4 +DDR4-2400 memory channels) leads to a lower performance of approximately +109 katom-step/s due to differences in architecture and clock. In all +cases, when looking at multiple runs, the katom-step/s property +fluctuates by approximately 1% around the average. + +From here on we are looking at the performance for the 256000 atom system only +and change several settings incrementally: + +#. No compiler optimization GCC (-Og -g): 183.8 katom-step/s +#. Moderate optimization with debug info GCC (-O2 -g): 231.1 katom-step/s +#. Full compiler optimization GCC (-DNDEBUG -O3): 236.0 katom-step/s +#. Aggressive compiler optimization GCC (-O3 -ffast-math -march=native): 239.9 katom-step/s +#. Source code optimization in OPENMP package (1 thread): 266.7 katom-step/s +#. Use *fix nvt* instead of *fix npt* (compute virial only every 50 steps): 272.9 katom-step/s +#. Increase pair style cutoff by 2 :math:`\AA`: 181.2 katom-step/s +#. Use tight PPPM convergence (1.0e-6 instead of 1.0e-4): 161.9 katom-step/s +#. Use Ewald summation instead of PPPM (at 1.0e-4 convergence): 19.9 katom-step/s + +The numbers show that gains from aggressive compiler optimizations are +rather small in LAMMPS, the data access optimizations in the OPENMP (and +OPT) packages are more prominent. On the other side, using more +accurate force field settings causes, not unexpectedly, a significant +slowdown (to about half the speed). Finally, using regular Ewald +summation causes a massive slowdown due to the bad algorithmic scaling +with system size. + +Examples comparing parallel performance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The parallel performance usually goes on top of the serial performance. +Using twice as many processors should increase the performance metric +by up to a factor of two. With the number of processors *N* and the +serial performance :math:`p_1` and the performance for *N* processors +:math:`p_N` we can define a *parallel efficiency* in percent as follows: + +.. math:: + + P_{eff} = \frac{p_N}{p_1 \cdot N} \cdot 100\% + +For the AMD Ryzen Threadripper PRO 9985WX CPU and the serial +simulation settings of point 6. from above, we get the following +parallel efficiency data for the 256000 atom system: + +- 1 MPI task: 273.6 katom-step/s, :math:`P_{eff} = 100\%` +- 2 MPI tasks: 530.6 katom-step/s, :math:`P_{eff} = 97\%` +- 4 MPI tasks: 1.021 Matom-step/s, :math:`P_{eff} = 93\%` +- 8 MPI tasks: 1.837 Matom-step/s, :math:`P_{eff} = 84\%` +- 16 MPI tasks: 3.574 Matom-step/s, :math:`P_{eff} = 82\%` +- 32 MPI tasks: 6.479 Matom-step/s, :math:`P_{eff} = 74\%` +- 64 MPI tasks: 9.032 Matom-step/s, :math:`P_{eff} = 52\%` +- 128 MPI tasks: 12.03 Matom-step/s, :math:`P_{eff} = 34\%` + +The 128 MPI tasks run uses CPU cores from hyper-threading. + +For a small system with only 32000 atoms the parallel efficiency +drops off earlier when the number of work units is too small relative +to the communication overhead: + +- 1 MPI task: 270.8 katom-step/s, :math:`P_{eff} = 100\%` +- 2 MPI tasks: 529.3 katom-step/s, :math:`P_{eff} = 98\%` +- 4 MPI tasks: 989.8 katom-step/s, :math:`P_{eff} = 91\%` +- 8 MPI tasks: 1.832 Matom-step/s, :math:`P_{eff} = 85\%` +- 16 MPI tasks: 3.463 Matom-step/s, :math:`P_{eff} = 80\%` +- 32 MPI tasks: 5.970 Matom-step/s, :math:`P_{eff} = 69\%` +- 64 MPI tasks: 7.477 Matom-step/s, :math:`P_{eff} = 42\%` +- 128 MPI tasks: 8.069 Matom-step/s, :math:`P_{eff} = 23\%` + +Measuring performance of your input deck +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The best way to do this is run the your system (actual number of atoms) +for a modest number of timesteps (say 100 steps) on several different +processor counts, including a single processor if possible. Do this for +an equilibrium version of your system, so that the 100-step timings are +representative of a much longer run. There is typically no need to run +for 1000s of timesteps to get accurate timings; you can simply +extrapolate from short runs. For the set of runs, look at the timing data printed to the screen and log file at the end of each LAMMPS run. The @@ -28,12 +230,15 @@ breakdown and relative percentages. For example, trying different options for speeding up the long-range solvers will have little impact if they only consume 10% of the run time. If the pairwise time is dominating, you may want to look at GPU or OMP versions of the pair -style, as discussed below. Comparing how the percentages change as -you increase the processor count gives you a sense of how different -operations within the timestep are scaling. Note that if you are -running with a Kspace solver, there is additional output on the -breakdown of the Kspace time. For PPPM, this includes the fraction -spent on FFTs, which can be communication intensive. +style, as discussed below. Comparing how the percentages change as you +increase the processor count gives you a sense of how different +operations within the timestep are scaling. If you are using PPPM as +Kspace solver, you can turn on an additional output with +:doc:`kspace_modify fftbench yes ` which measures the +time spent during PPPM on the 3d FFTs, which can be communication +intensive for larger processor counts. This provides an indication +whether it is worth trying out alternatives to the default FFT settings +for additional performance. Another important detail in the timing info are the histograms of atoms counts and neighbor counts. If these vary widely across diff --git a/doc/src/Tools.rst b/doc/src/Tools.rst index 521cfe5f16..bb59f72fb3 100644 --- a/doc/src/Tools.rst +++ b/doc/src/Tools.rst @@ -92,6 +92,7 @@ Miscellaneous tools * :ref:`LAMMPS coding standards ` * :ref:`emacs ` * :ref:`i-PI ` + * :ref:`JSON support ` * :ref:`kate ` * :ref:`LAMMPS-GUI ` * :ref:`LAMMPS magic patterns for file(1) ` @@ -364,7 +365,7 @@ These tools were provided by Aidan Thompson at Sandia .. _fep: fep tool ------------------- +-------- The tools/fep directory contains Python scripts useful for post-processing results from performing free-energy perturbation @@ -379,7 +380,7 @@ See README file in the tools/fep directory. .. _ipi: i-PI tool -------------------- +--------- .. versionchanged:: 27June2024 @@ -432,6 +433,87 @@ tools/createatoms tool's input file. ---------- +.. _json: + +JSON support files +------------------ + +.. versionadded:: 12June2025 + +The ``tools/json`` directory contains files and tools to support +using `JSON format `_ files in LAMMPS. +Currently only the :doc:`molecule command ` supports +files in JSON format directly, but this is planned to be expanded +in the future. + +JSON file validation +^^^^^^^^^^^^^^^^^^^^ + +The JSON syntax is independent of its content, and thus the data in the +file must follow suitable conventions to be correctly parsed during +input. This can be done in a portable fashion using a `JSON schema file +`_ (which is in JSON format as well) to define +those conventions. A suitable JSON validator software can then validate +JSON files against the requirements. Validating a particular JSON file +against a schema ensures that both, the syntax *and* the conventions +are followed. This is useful when writing or editing JSON files in a +text editor or when writing a pre-processing script or tool to create +JSON files for a specific purpose in LAMMPS. It **cannot** check +whether the file contents are physically meaningful, though. + +One such validator tool is `check-jsonschema +`_ which is written in Python +and can be installed using the `pip Python package manager +`_, best in a virtual environment as shown below (for +a Bourne Shell command line): + +.. code-block:: sh + + python -m venv validate-json + source validate-json/bin/activate + pip install --upgrade pip + pip install check-jsonschema + +To validate a specific JSON file against a provided schema (here for +a :doc:`molecule command file ` you would then run for example: + +.. code-block:: sh + + check-jsonschema --schemafile molecule-schema.json tip3p.json + +The latest schema files are also maintained and available for download +at https://download.lammps.org/json . This enables validation of JSON +files even if the LAMMPS sources are not locally available. Example: + +.. code-block:: sh + + check-jsonschema --schemafile https://download.lammps.org/json/molecule-schema.json tip3p.json + +JSON file format normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are extensions to the strict JSON format that allow for comments +or ignore additional (dangling) commas. The ``reformat-json.cpp`` tool +will read JSON files in relaxed format, but write it out in strict format. +It is also possible to change the level of indentation from -1 (all data +one long line) to any positive integer value. The original file will be +backed up (.bak added to file name) and then overwritten. + +Manual compilation (it will be automatically included in the CMake build +if building tools is requested during CMake configuration): + +.. code-block:: sh + + g++ -I -o reformat-json reformat-json.cpp + +Usage: + +.. parsed-literal:: + + reformat-json [ ...] + +---------- + .. _kate: kate tool diff --git a/doc/src/compute_angle_local.rst b/doc/src/compute_angle_local.rst index d4491c6945..5bd1692355 100644 --- a/doc/src/compute_angle_local.rst +++ b/doc/src/compute_angle_local.rst @@ -53,15 +53,17 @@ The value *eng* is the interaction energy for the angle. The value *v_name* can be used together with the *set* keyword to compute a user-specified function of the angle theta. The *name* -specified for the *v_name* value is the name of an :doc:`equal-style variable ` which should evaluate a formula based on a +specified for the *v_name* value is the name of an :doc:`equal-style +variable ` which should evaluate a formula based on a variable which will store the angle theta. This other variable must -be an :doc:`internal-style variable ` defined in the input -script; its initial numeric value can be anything. It must be an -internal-style variable, because this command resets its value -directly. The *set* keyword is used to identify the name of this -other variable associated with theta. +be an :doc:`internal-style variable ` specified by the *set* +keyword. It is an internal-style variable, because this command +resets its value directly. The internal-style variable does not need +to be defined in the input script (though it can be); if it is not +defined, then the *set* option creates an :doc:`internal-style +variable ` with the specified name. -Note that the value of theta for each angle which stored in the +Note that the value of theta for each angle which is stored in the internal variable is in radians, not degrees. As an example, these commands can be added to the bench/in.rhodo @@ -70,7 +72,6 @@ system and output the statistics in various ways: .. code-block:: LAMMPS - variable t internal 0.0 variable cos equal cos(v_t) variable cossq equal cos(v_t)*cos(v_t) diff --git a/doc/src/compute_bond_local.rst b/doc/src/compute_bond_local.rst index 433102d772..74fe4aa3c6 100644 --- a/doc/src/compute_bond_local.rst +++ b/doc/src/compute_bond_local.rst @@ -130,13 +130,15 @@ moving apart. The value *v_name* can be used together with the *set* keyword to compute a user-specified function of the bond distance. The *name* -specified for the *v_name* value is the name of an :doc:`equal-style variable ` which should evaluate a formula based on a -variable which will store the bond distance. This other variable must -be an :doc:`internal-style variable ` defined in the input -script; its initial numeric value can be anything. It must be an -internal-style variable, because this command resets its value -directly. The *set* keyword is used to identify the name of this -other variable associated with theta. +specified for the *v_name* value is the name of an :doc:`equal-style +variable ` which should evaluate a formula based on a +variable which stores the bond distance. This other variable must be +the :doc:`internal-style variable ` specified by the *set* +keyword. It is an internal-style variable, because this command +resets its value directly. The internal-style variable does not need +to be defined in the input script (though it can be); if it is not +defined, then the *set* option creates an :doc:`internal-style +variable ` with the specified name. As an example, these commands can be added to the bench/in.rhodo script to compute the length\ :math:`^2` of every bond in the system and @@ -144,7 +146,6 @@ output the statistics in various ways: .. code-block:: LAMMPS - variable d internal 0.0 variable dsq equal v_d*v_d compute 1 all property/local batom1 batom2 btype diff --git a/doc/src/compute_dihedral_local.rst b/doc/src/compute_dihedral_local.rst index d809cd39ce..77f467721d 100644 --- a/doc/src/compute_dihedral_local.rst +++ b/doc/src/compute_dihedral_local.rst @@ -45,30 +45,31 @@ interactions. The number of datums generated, aggregated across all processors, equals the number of dihedral angles in the system, modified by the group parameter as explained below. -The value *phi* (:math:`\phi`) is the dihedral angle, as defined in the diagram -on the :doc:`dihedral_style ` doc page. +The value *phi* (:math:`\phi`) is the dihedral angle, as defined in +the diagram on the :doc:`dihedral_style ` doc page. -The value *v_name* can be used together with the *set* keyword to compute a -user-specified function of the dihedral angle :math:`\phi`. The *name* -specified for the *v_name* value is the name of an -:doc:`equal-style variable ` which should evaluate a formula based on -a variable which will store the angle :math:`\phi`. This other variable must -be an :doc:`internal-style variable ` defined in the input -script; its initial numeric value can be anything. It must be an -internal-style variable, because this command resets its value -directly. The *set* keyword is used to identify the name of this -other variable associated with :math:`\phi`. +The value *v_name* can be used together with the *set* keyword to +compute a user-specified function of the dihedral angle :math:`\phi`. +The *name* specified for the *v_name* value is the name of an +:doc:`equal-style variable ` which should evaluate a formula +based on a variable which will store the angle :math:`\phi`. This +other variable must be an :doc:`internal-style variable ` +specified by the *set* keyword. It is an internal-style variable, +because this command resets its value directly. The internal-style +variable does not need to be defined in the input script (though it +can be); if it is not defined, then the *set* option creates an +:doc:`internal-style variable ` with the specified name. -Note that the value of :math:`\phi` for each angle which stored in the internal -variable is in radians, not degrees. +Note that the value of :math:`\phi` for each angle which stored in the +internal variable is in radians, not degrees. As an example, these commands can be added to the bench/in.rhodo -script to compute the :math:`\cos\phi` and :math:`\cos^2\phi` of every dihedral -angle in the system and output the statistics in various ways: +script to compute the :math:`\cos\phi` and :math:`\cos^2\phi` of every +dihedral angle in the system and output the statistics in various +ways: .. code-block:: LAMMPS - variable p internal 0.0 variable cos equal cos(v_p) variable cossq equal cos(v_p)*cos(v_p) @@ -100,10 +101,10 @@ no consistent ordering of the entries within the local vector or array from one timestep to the next. The only consistency that is guaranteed is that the ordering on a particular timestep will be the same for local vectors or arrays generated by other compute commands. -For example, dihedral output from the -:doc:`compute property/local ` command can be combined -with data from this command and output by the :doc:`dump local ` -command in a consistent way. +For example, dihedral output from the :doc:`compute property/local +` command can be combined with data from this +command and output by the :doc:`dump local ` command in a +consistent way. Here is an example of how to do this: diff --git a/doc/src/create_atoms.rst b/doc/src/create_atoms.rst index 4aef11e1ba..a29d8352a3 100644 --- a/doc/src/create_atoms.rst +++ b/doc/src/create_atoms.rst @@ -416,24 +416,23 @@ atom, based on its coordinates. They apply to all styles except *single*. The *name* specified for the *var* keyword is the name of an :doc:`equal-style variable ` that should evaluate to a zero or non-zero value based on one or two or three variables that -will store the *x*, *y*, or *z* coordinates of an atom (one variable per -coordinate). If used, these other variables must be -:doc:`internal-style variables ` defined in the input -script; their initial numeric value can be anything. They must be -internal-style variables, because this command resets their values -directly. The *set* keyword is used to identify the names of these -other variables, one variable for the *x*-coordinate of a created atom, -one for *y*, and one for *z*. +will store the *x*, *y*, or *z* coordinates of an atom (one variable +per coordinate). If used, these other variables must be specified by +the *set* keyword. They are internal-style variable, because this +command resets their values directly. The internal-style variables do +not need to be defined in the input script (though they can be); if +one (or more) is not defined, then the *set* option creates an +:doc:`internal-style variable ` with the specified name. .. figure:: img/sinusoid.jpg :figwidth: 50% :align: right :target: _images/sinusoid.jpg -When an atom is created, its :math:`(x,y,z)` coordinates become the values for -any *set* variable that is defined. The *var* variable is then -evaluated. If the returned value is 0.0, the atom is not created. If -it is non-zero, the atom is created. +When an atom is about to be created, its :math:`(x,y,z)` coordinates +become the values for any *set* variable that is defined. The *var* +variable is then evaluated. If the returned value is 0.0, the atom is +not created. If it is non-zero, the atom is created. As an example, these commands can be used in a 2d simulation, to create a sinusoidal surface. Note that the surface is "rough" due to @@ -456,8 +455,6 @@ converts lattice spacings to distance. region box block 0 $x 0 $y -0.5 0.5 create_box 1 box - variable xx internal 0.0 - variable yy internal 0.0 variable v equal "(0.2*v_y*ylat * cos(v_xx/xlat * 2.0*PI*4.0/v_x) + 0.5*v_y*ylat - v_yy) > 0.0" create_atoms 1 box var v set x xx set y yy write_dump all atom sinusoid.lammpstrj diff --git a/doc/src/fix.rst b/doc/src/fix.rst index d49850e7da..459bf895b7 100644 --- a/doc/src/fix.rst +++ b/doc/src/fix.rst @@ -292,7 +292,6 @@ accelerated styles exist. * :doc:`mvv/tdpd ` - constant temperature DPD using the modified velocity-Verlet algorithm * :doc:`neb ` - nudged elastic band (NEB) spring forces * :doc:`neb/spin ` - nudged elastic band (NEB) spring forces for spins -* :doc:`neighbor/swap ` - kinetic Monte Carlo (kMC) atom swapping * :doc:`nonaffine/displacement ` - calculate nonaffine displacement of atoms * :doc:`nph ` - constant NPH time integration via Nose/Hoover * :doc:`nph/asphere ` - NPH for aspherical particles @@ -398,6 +397,7 @@ accelerated styles exist. * :doc:`rigid/small ` - constrain many small clusters of atoms to move as a rigid body with NVE integration * :doc:`rx ` - solve reaction kinetic ODEs for a defined reaction set * :doc:`saed/vtk ` - time-average the intensities from :doc:`compute saed ` +* :doc:`set ` - reset an atom property via an atom-style variable every N steps * :doc:`setforce ` - set the force on each atom * :doc:`setforce/spin ` - set magnetic precession vectors on each atom * :doc:`sgcmc ` - fix for hybrid semi-grand canonical MD/MC simulations diff --git a/doc/src/fix_ave_correlate_long.rst b/doc/src/fix_ave_correlate_long.rst index 22fac89706..97b68fe14f 100644 --- a/doc/src/fix_ave_correlate_long.rst +++ b/doc/src/fix_ave_correlate_long.rst @@ -82,10 +82,9 @@ specified values may represent calculations performed by computes and fixes which store their own "group" definitions. Each listed value can be the result of a compute or fix or the -evaluation of an equal-style or vector-style variable. For -vector-style variables, the specified indices can include a wildcard -character. See the :doc:`fix ave/correlate ` page -for details. +evaluation of an equal-style or vector-style variable. The specified +indices can include a wildcard string. See the +:doc:`fix ave/correlate ` page for details on that. The *Nevery* and *Nfreq* arguments specify on what time steps the input values will be used to calculate correlation data and the frequency diff --git a/doc/src/fix_controller.rst b/doc/src/fix_controller.rst index fc8186ef29..4e0414ca92 100644 --- a/doc/src/fix_controller.rst +++ b/doc/src/fix_controller.rst @@ -98,52 +98,53 @@ the following dynamic equation: \frac{dc}{dt} = -\alpha (K_p e + K_i \int_0^t e \, dt + K_d \frac{de}{dt} ) -where *c* is the continuous time analog of the control variable, -*e* =\ *pvar*\ -\ *setpoint* is the error in the process variable, and -:math:`\alpha`, :math:`K_p`, :math:`K_i` , and :math:`K_d` are constants -set by the corresponding -keywords described above. The discretized version of this equation is: +where *c* is the continuous time analog of the control variable, *e* +=\ *pvar*\ -\ *setpoint* is the error in the process variable, and +:math:`\alpha`, :math:`K_p`, :math:`K_i` , and :math:`K_d` are +constants set by the corresponding keywords described above. The +discretized version of this equation is: .. math:: c_n = c_{n-1} -\alpha \left( K_p \tau e_n + K_i \tau^2 \sum_{i=1}^n e_i + K_d (e_n - e_{n-1}) \right) -where :math:`\tau = \mathtt{Nevery} \cdot \mathtt{timestep}` is the time -interval between updates, -and the subscripted variables indicate the values of *c* and *e* at -successive updates. +where :math:`\tau = \mathtt{Nevery} \cdot \mathtt{timestep}` is the +time interval between updates, and the subscripted variables indicate +the values of *c* and *e* at successive updates. From the first equation, it is clear that if the three gain values :math:`K_p`, :math:`K_i`, :math:`K_d` are dimensionless constants, -then :math:`\alpha` must have -units of [unit *cvar*\ ]/[unit *pvar*\ ]/[unit time] e.g. [ eV/K/ps -]. The advantage of this unit scheme is that the value of the -constants should be invariant under a change of either the MD timestep -size or the value of *Nevery*\ . Similarly, if the LAMMPS :doc:`unit style ` is changed, it should only be necessary to change -the value of :math:`\alpha` to reflect this, while leaving :math:`K_p`, -:math:`K_i`, and :math:`K_d` unaltered. +then :math:`\alpha` must have units of [unit *cvar*\ ]/[unit *pvar*\ +]/[unit time] e.g. [ eV/K/ps ]. The advantage of this unit scheme is +that the value of the constants should be invariant under a change of +either the MD timestep size or the value of *Nevery*\ . Similarly, if +the LAMMPS :doc:`unit style ` is changed, it should only be +necessary to change the value of :math:`\alpha` to reflect this, while +leaving :math:`K_p`, :math:`K_i`, and :math:`K_d` unaltered. When choosing the values of the four constants, it is best to first pick a value and sign for :math:`\alpha` that is consistent with the -magnitudes and signs of *pvar* and *cvar*\ . The magnitude of :math:`K_p` -should then be tested over a large positive range keeping :math:`K_i = K_d =0`. -A good value for :math:`K_p` will produce a fast response in *pvar*, -without overshooting the *setpoint*\ . For many applications, proportional -feedback is sufficient, and so :math:`K_i = K_d =0` can be used. In cases -where there is a substantial lag time in the response of *pvar* to a change -in *cvar*, this can be counteracted by increasing :math:`K_d`. In situations +magnitudes and signs of *pvar* and *cvar*\ . The magnitude of +:math:`K_p` should then be tested over a large positive range keeping +:math:`K_i = K_d =0`. A good value for :math:`K_p` will produce a +fast response in *pvar*, without overshooting the *setpoint*\ . For +many applications, proportional feedback is sufficient, and so +:math:`K_i = K_d =0` can be used. In cases where there is a +substantial lag time in the response of *pvar* to a change in *cvar*, +this can be counteracted by increasing :math:`K_d`. In situations where *pvar* plateaus without reaching *setpoint*, this can be -counteracted by increasing :math:`K_i`. In the language of Charles Dickens, -:math:`K_p` represents the error of the present, :math:`K_i` the error of -the past, and :math:`K_d` the error yet to come. +counteracted by increasing :math:`K_i`. In the language of Charles +Dickens, :math:`K_p` represents the error of the present, :math:`K_i` +the error of the past, and :math:`K_d` the error yet to come. Because this fix updates *cvar*, but does not initialize its value, -the initial value :math:`c_0` is that assigned by the user in the input script via -the :doc:`internal-style variable ` command. This value is -used (by every other LAMMPS command that uses the variable) until this -fix performs its first update of *cvar* after *Nevery* timesteps. On -the first update, the value of the derivative term is set to zero, -because the value of :math:`e_{n-1}` is not yet defined. +the initial value :math:`c_0` is that assigned by the user in the +input script via the :doc:`internal-style variable ` +command. This value is used (by every other LAMMPS command that uses +the variable) until this fix performs its first update of *cvar* after +*Nevery* timesteps. On the first update, the value of the derivative +term is set to zero, because the value of :math:`e_{n-1}` is not yet +defined. ---------- @@ -154,21 +155,23 @@ must produce a global quantity, not a per-atom or local quantity. If *pvar* begins with "c\_", a compute ID must follow which has been previously defined in the input script and which generates a global -scalar or vector. See the individual :doc:`compute ` doc page -for details. If no bracketed integer is appended, the scalar +scalar or vector. See the individual :doc:`compute ` doc +page for details. If no bracketed integer is appended, the scalar calculated by the compute is used. If a bracketed integer is appended, the Ith value of the vector calculated by the compute is -used. Users can also write code for their own compute styles and :doc:`add them to LAMMPS `. +used. Users can also write code for their own compute styles and +:doc:`add them to LAMMPS `. If *pvar* begins with "f\_", a fix ID must follow which has been previously defined in the input script and which generates a global scalar or vector. See the individual :doc:`fix ` page for details. Note that some fixes only produce their values on certain timesteps, which must be compatible with when fix controller -references the values, or else an error results. If no bracketed integer -is appended, the scalar calculated by the fix is used. If a bracketed -integer is appended, the Ith value of the vector calculated by the fix -is used. Users can also write code for their own fix style and :doc:`add them to LAMMPS `. +references the values, or else an error results. If no bracketed +integer is appended, the scalar calculated by the fix is used. If a +bracketed integer is appended, the Ith value of the vector calculated +by the fix is used. Users can also write code for their own fix style +and :doc:`add them to LAMMPS `. If *pvar* begins with "v\_", a variable name must follow which has been previously defined in the input script. Only equal-style variables @@ -182,19 +185,21 @@ variable. The target value *setpoint* for the process variable must be a numeric value, in whatever units *pvar* is defined for. -The control variable *cvar* must be the name of an :doc:`internal-style variable ` previously defined in the input script. Note -that it is not specified with a "v\_" prefix, just the name of the -variable. It must be an internal-style variable, because this fix -updates its value directly. Note that other commands can use an -equal-style versus internal-style variable interchangeably. +The control variable *cvar* must be the name of an +:doc:`internal-style variable ` previously defined in the +input script. Note that it is not specified with a "v\_" prefix, just +the name of the variable. It must be an internal-style variable, +because this fix updates its value directly. Note that other commands +can use an equal-style versus internal-style variable interchangeably. ---------- Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" -Currently, no information about this fix is written to :doc:`binary restart files `. None of the :doc:`fix_modify ` options -are relevant to this fix. +Currently, no information about this fix is written to :doc:`binary +restart files `. None of the :doc:`fix_modify ` +options are relevant to this fix. This fix produces a global vector with 3 values which can be accessed by various :doc:`output commands `. The values can be @@ -211,7 +216,8 @@ variable is in. The vector values calculated by this fix are "extensive". No parameter of this fix can be used with the *start/stop* keywords of -the :doc:`run ` command. This fix is not invoked during :doc:`energy minimization `. +the :doc:`run ` command. This fix is not invoked during +:doc:`energy minimization `. Restrictions """""""""""" diff --git a/doc/src/fix_deposit.rst b/doc/src/fix_deposit.rst index 8f88717a00..09cf328fec 100644 --- a/doc/src/fix_deposit.rst +++ b/doc/src/fix_deposit.rst @@ -225,22 +225,25 @@ rotated configuration of the molecule. .. versionadded:: 21Nov2023 -The *var* and *set* keywords can be used together to provide a criterion -for accepting or rejecting the addition of an individual atom, based on its -coordinates. The *name* specified for the *var* keyword is the name of an -:doc:`equal-style variable ` that should evaluate to a zero or -non-zero value based on one or two or three variables that will store the -*x*, *y*, or *z* coordinates of an atom (one variable per coordinate). If -used, these other variables must be :doc:`internal-style variables -` defined in the input script; their initial numeric value can be -anything. They must be internal-style variables, because this command -resets their values directly. The *set* keyword is used to identify the -names of these other variables, one variable for the *x*-coordinate of a -created atom, one for *y*, and one for *z*. When an atom is created, its -:math:`(x,y,z)` coordinates become the values for any *set* variable that -is defined. The *var* variable is then evaluated. If the returned value -is 0.0, the atom is not created. If it is non-zero, the atom is created. -For an example of how to use these keywords, see the +The *var* and *set* keywords can be used together to provide a +criterion for accepting or rejecting the addition of an individual +atom, based on its coordinates. The *name* specified for the *var* +keyword is the name of an :doc:`equal-style variable ` that +should evaluate to a zero or non-zero value based on one or two or +three variables that will store the *x*, *y*, or *z* coordinates of an +atom (one variable per coordinate). If used, these other variables +must be :doc:`internal-style variables ` specified by the +*set* keyword. They must be internal-style variables, because this +command resets their values directly. The internal-style variables do +not need to be defined in the input script (though they can be); if +one (or more) is not defined, then the *set* option creates an +:doc:`internal-style variable ` with the specified name. + +When an atom is about to be created, its :math:`(x,y,z)` coordinates +become the values for any *set* variable that is defined. The *var* +variable is then evaluated. If the returned value is 0.0, the atom is +not created. If it is non-zero, the atom is created. For an example +of how to use the set/var keywords in a similar context, see the :doc:`create_atoms ` command. The *rate* option moves the insertion volume in the z direction (3d) @@ -304,12 +307,13 @@ units of distance or velocity. Restart, fix_modify, output, run start/stop, minimize info """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" -This fix writes the state of the deposition to :doc:`binary restart files `. This includes information about how many -particles have been deposited, the random number generator seed, the -next timestep for deposition, etc. See the -:doc:`read_restart ` command for info on how to re-specify -a fix in an input script that reads a restart file, so that the -operation of the fix continues in an uninterrupted fashion. +This fix writes the state of the deposition to :doc:`binary restart +files `. This includes information about how many particles +have been deposited, the random number generator seed, the next +timestep for deposition, etc. See the :doc:`read_restart +` command for info on how to re-specify a fix in an +input script that reads a restart file, so that the operation of the +fix continues in an uninterrupted fashion. .. note:: diff --git a/doc/src/fix_neighbor_swap.rst b/doc/src/fix_neighbor_swap.rst deleted file mode 100644 index a5e264a98d..0000000000 --- a/doc/src/fix_neighbor_swap.rst +++ /dev/null @@ -1,264 +0,0 @@ -.. index:: fix neighbor/swap - -fix neighbor/swap command -========================= - -Syntax -"""""" - -.. code-block:: LAMMPS - - fix ID group-ID neighbor/swap N X seed T R0 voro-ID keyword values ... - -* ID, group-ID are documented in :doc:`fix ` command -* neighbor/swap = style name of this fix command -* N = invoke this fix every N steps -* X = number of swaps to attempt every N steps -* seed = random # seed (positive integer) -* T = scaling temperature of the MC swaps (temperature units) -* R0 = scaling swap probability of the MC swaps (distance units) -* voro-ID = valid voronoi compute id (compute voronoi/atom) -* one or more keyword/value pairs may be appended to args -* keywords *types* and *diff* are mutually exclusive, but one must be specified -* keyword = *types* or *diff* or *ke* or *region* or *rates* - - .. parsed-literal:: - - *types* values = two or more atom types (Integers in range [1,Ntypes] or type labels) - *diff* values = one atom type - *ke* value = *yes* or *no* - *yes* = kinetic energy is conserved after atom swaps - *no* = no conservation of kinetic energy after atom swaps - *region* value = region-ID - region-ID = ID of region to use as an exchange/move volume - *rates* values = V1 V2 . . . Vntypes values to conduct variable diffusion for different atom types (unitless) - -Examples -"""""""" - -.. code-block:: LAMMPS - - compute voroN all voronoi/atom neighbors yes - fix mc all neighbor/swap 10 160 15238 1000.0 3.0 voroN diff 2 - fix myFix all neighbor/swap 100 1 12345 298.0 3.0 voroN region my_swap_region types 5 6 - fix kmc all neighbor/swap 1 100 345 1.0 3.0 voroN diff 3 rates 3 1 6 - -Description -""""""""""" - -.. versionadded:: 12Jun2025 - -This fix performs Monte-Carlo (MC) evaluations to enable kinetic -Monte Carlo (kMC)-type behavior during MD simulation by allowing -neighboring atoms to swap their positions. In contrast to the :doc:`fix -atom/swap ` command which swaps pairs of atoms anywhere -in the simulation domain, the restriction of the MC swapping to -neighbors enables a hybrid MD/kMC-like simulation. - -Neighboring atoms are defined by using a Voronoi tesselation performed -by the :doc:`compute voronoi/atom ` command. -Two atoms are neighbors if their Voronoi cells share a common face -(3d) or edge (2d). - -The selection of a swap neighbor is made using a distance-based -criterion for weighting the selection probability of each swap, in the -same manner as kMC selects a next event using relative probabilities. -The acceptance or rejection of each swap is determined via the -Metropolis criterion after evaluating the change in system energy due -to the swap. - -A detailed explanation of the original implementation of this -algorithm can be found in :ref:`(Tavenner 2023) ` -where it was used to simulated accelerated diffusion in an MD context. - -Simulating inherently kinetically-limited behaviors which rely on rare -events (such as atomic diffusion in a solid) is challenging for -traditional MD since its relatively short timescale will not naturally -sample many events. This fix addresses this challenge by allowing rare -neighbor hopping events to be sampled in a kMC-like fashion at a much -faster rate (set by the specified *N* and *X* parameters). This enables -the processes of atomic diffusion to be approximated during an MD -simulation, effectively decoupling the MD atomic vibrational timescale -and the atomic hopping (kMC event) timescale. - -The algorithm implemented by this fix is as follows: - - - The MD simulation is paused every *N* steps - - A Voronoi tesselation is performed for the current atom configuration. - - Then *X* atom swaps are attempted, one after the other. - - For each swap, an atom *I* is selected randomly from the list of - atom types specified by either the *types* or *diff* keywords. - - One of *I*'s Voronoi neighbors *J* is selected using the - distance-weighted probability for each neighbor detailed below. - - The *I,J* atom IDs are communicated to all processors so that a - global energy evaluation can be performed for the post-swap state - of the system. - - The swap is accepted or rejected based on the Metropolis criterion - using the energy change of the system and the specified temperature - *T*. - -Here are a few comments on the computational cost of the swapping -algorithm. - - 1. The cost of a global energy evaluation is similar to that of an MD - timestep. - - 2. Similar to other MC algorithms in LAMMPS, improved parallel - efficiency is achieved with a smaller number of atoms per - processor than would typically be used in an standard MD - simulation. This is because the per-energy evaluation cost - increases relative to the balance of MD/MC steps as indicated by - 1., but the communication cost remains relatively constant for a - given number of MD steps. - - 3. The MC portion of the simulation will run dramatically slower if - the pair style uses different cutoffs for different atom types (or - type pairs). This is because each atom swap then requires a - rebuild of the neighbor list to ensure the post-swap global energy - can be computed correctly. - -Limitations are imposed on selection of *I,J* atom pairs to avoid -swapping of atoms which are outside of a reasonable cutoff (e.g. due to -a Voronoi tesselation near free surfaces) though the use of a -distance-weighted probability scaling. - ----------- - -This section gives more details on other arguments and keywords. - -The random number generator (RNG) used by all the processors for MC -operations is initialized with the specified *seed*. - -The distance-based probability is weighted by the specified *R0* which -sets the radius :math:`r_0` in this formula - -.. math:: - - p_{ij} = e^{(\frac{r_{ij}}{r_0})^2} - -where :math:`p_{ij}` is the probability of selecting atom :math:`j` to -swap with atom :math:`i`. Typically, a value for *R0* around the -average nearest-neighbor spacing is appropriate. Since this is simply a -probability weighting, the swapping behavior is not very sensitive to -the exact value of *R0*. - -The required *voro-ID* value is the compute-ID of a -:doc:`compute voronoi/atom ` command like -this: - -.. code-block:: LAMMPS - - compute compute-ID group-ID voronoi/atom neighbors yes - -It must return per-atom list of valid neighbor IDs as in the -:doc:`compute voronoi/atom ` command. - -The keyword *types* takes two or more atom types as its values. Only -atoms *I* of the first atom type will be selected. Only atoms *J* of the -remaining atom types will be considered as potential swap partners. - -The keyword *diff* take a single atom type as its value. Only atoms -*I* of the that atom type will be selected. Atoms *J* of all -remaining atom types will be considered as potential swap partners. -This includes the atom type specified with the *diff* keyword to -account for self-diffusive hops between two atoms of the same type. - -Note that the *neighbors yes* option must be enabled for use with this -fix. The group-ID should include all the atoms which this fix will -potentially select. I.e. the group-ID used in the voronoi compute should -include the same atoms as that indicated by the *types* keyword. If the -*diff* keyword is used, the group-ID should include atoms of all types -in the simulation. - -The keyword *ke* takes *yes* (default) or *no* as its value. It two -atoms are swapped with different masses, then a value of *yes* will -rescale their respective velocities to conserve the kinetic energy of -the system. A value of *no* will perform no rescaling, so that -kinetic energy is not conserved. See the restriction on this keyword -below. - -The *region* keyword takes a *region-ID* as its value. If specified, -then only atoms *I* and *J* within the geometric region will be -considered as swap partners. See the :doc:`region ` command -for details. This means the group-ID for the :doc:`compute -voronoi/atom ` command also need only contain -atoms within the region. - -The keyword *rates* can modify the swap rate based on the type of atom -*J*. Ntype values must be specified, where Ntype = the number of atom -types in the system. Each value is used to scale the probability -weighting given by the equation above. In the third example command -above, a simulation has 3 atoms types. Atom *I*s of type 1 are -eligible for swapping. Swaps may occur with atom *J*s of all 3 types. -Assuming all *J* atoms are equidistant from an atom *I*, *J* atoms of -type 1 will be 3x more likely to be selected as a swap partner than -atoms of type 2. And *J* atoms of type 3 will be 6.5x more likely to -be selected than atoms of type 2. If the *rates* keyword is not used, -all atom types will be treated with the same probability during selection -of swap attempts. - - -Restart, fix_modify, output, run start/stop, minimize info -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -This fix writes the state of the fix to :doc:`binary restart files -`. This includes information about the random number generator -seed, the next timestep for MC exchanges, and the number of exchange -attempts and successes. See the :doc:`read_restart ` -command for info on how to re-specify a fix in an input script that -reads a restart file, so that the operation of the fix continues in an -uninterrupted fashion. - -None of the :doc:`fix_modify ` options are relevant to this -fix. - -This fix computes a global vector of length 2, which can be accessed -by various :doc:`output commands `. The vector values are -the following global cumulative quantities: - - #. swap attempts - #. swap accepts - -The vector values calculated by this fix are "intensive". - -No parameter of this fix can be used with the *start/stop* keywords of -the :doc:`run ` command. This fix is not invoked during -:doc:`energy minimization `. - -Restrictions -"""""""""""" - -This fix is part of the MC package. It is only enabled if LAMMPS was -built with that package. See the :doc:`Build package ` -doc page for more info. Also this fix requires that the :ref:`VORONOI -package ` is installed, otherwise the fix will not be -compiled. - -The :doc:`compute voronoi/atom ` command -referenced by the required voro-ID must return neighboring atoms as -illustrated in the examples above. - -If this fix is used with systems that do not have per-type masses -(e.g. atom style sphere), the *ke* keyword must be set to *off* since -the implemented algorithm will not be able to re-scale velocities -properly. - -Related commands -"""""""""""""""" - -:doc:`fix nvt `, :doc:`compute voronoi/atom ` -:doc:`delete_atoms `, :doc:`fix gcmc `, -:doc:`fix atom/swap `, :doc:`fix mol/swap `, -:doc:`fix sgcmc ` - -Default -""""""" - -The option defaults are *ke* = yes and *rates* = 1 for all atom types. - ----------- - -.. _TavennerMDkMC: - -**(Tavenner 2023)** J Tavenner, M Mendelev, J Lawson, Computational - Materials Science, 218, 111929 (2023). diff --git a/doc/src/fix_set.rst b/doc/src/fix_set.rst new file mode 100644 index 0000000000..cfa8231ccd --- /dev/null +++ b/doc/src/fix_set.rst @@ -0,0 +1,173 @@ +.. index:: fix set + +fix set command +=============== + +Syntax +"""""" + +.. code-block:: LAMMPS + + fix ID group-ID set Nfreq rnflag set-args + +* ID, group-ID are documented in :doc:`fix ` command +* set = style name of this fix command +* Nfreq = reset per-atom properties every this many timesteps +* rnflag = 1 to reneighbor on next timestep, 0 to not +* set-args = identical to args for the :doc:`set ` command + +Examples +"""""""" + +.. code-block:: LAMMPS + + fix 10 all set 1 0 group all i_dump v_new + fix 10 all set 1 0 group all i_dump v_turnoff + +Description +""""""""""" + +Reset one or more properties of one or more atoms once every *Nfreq* +steps during a simulation. + +If the *rnflag* for reneighboring is set to 1, then a reneighboring +will be triggered on the next timestep (since the fix set operation +occurs at the end of the current timestep). This is important to do +if this command changes per-atom properties that need to be +communicated to ghost atoms. If this is not the case, an *rnflag* +setting of 0 can be used; reneighboring will only be triggered on +subsequent timesteps by the usual neighbor list criteria; see the +:doc:`neigh_modify command `. + +Here are two examples where an *rnflag* setting of 1 are needed. If a +custom per-atom property is changed and the :doc:`fix property/atom +` command to create the property used the *ghost +yes* keyword. Or if per-atom charges are changed, all pair styles +which compute Coulombic interactions require charge values for ghost +atoms. In both these examples, the re-neighboring will trigger the +changes in the owned atom properties to be immediately communicated to +ghost atoms. + +The arguments following *Nfreq* and *rnflag* are identical to those +allowed for the :doc:`set ` command, as in the examples above and +below. + +Note that the group-ID setting for this command is ignored. The +syntax for the :doc:`set ` arguments allows selection of which +atoms have their properties reset. + +This command can only be used to reset an atom property using a +per-atom variable. This option in allowed by many, but not all, of +the keyword/value pairs supported by the :doc:`set ` command. +The reason for this restriction is that if a per-atom variable is not +used, this command will typically not change atom properties during +the simulation. + +The :doc:`set ` command can be used with similar syntax to this +command to reset atom properties once before or between simulations. + +---------- + +Here is an example of input script commands which will output atoms +into a dump file only when their x-velocity crosses a threshold value +*vthresh* for the first time. Their position and x-velocity will then +be output every step for *twindow* timesteps. + +.. code-block:: LAMMPS + + variable vthresh equal 2 # threshold velocity + variable twindow equal 10 # dump for this many steps + # + # define custom property i_dump to store timestep threshold is crossed + # + fix 2 all property/atom i_dump + set group all i_dump -1 + # + # fix set command checks for threshold crossings every step + # resets i_dump from -1 to current timestep when crossing occurs + # + variable start atom "vx > v_vthresh && i_dump == -1" + variable new atom ternary(v_start,step,i_dump) + fix 3 all set 1 0 group all i_dump v_new + # + # dump command with thresh which enforces twindow + # + dump 1 all custom 1 tmp.dump id x y vx i_dump + variable dumpflag atom "i_dump >= 0 && (step-i_dump) < v_twindow" + dump_modify 1 thresh v_dumpflag == 1 + # + # run the simulation + # final dump with all atom IDs which crossed threshold on which timestep + # + run 1000 + write_dump all custom tmp.dump.final id i_dump modify thresh i_dump >= 0 + +The tmp.dump.final file lists which atoms crossed the velocity +threshold. This command will print the *twindow* timesteps when a +specific atom ID (104 in this case) was output in the tmp.dump file: + +.. code-block:: LAMMPS + + % grep "^104 " tmp.dump + +If these commands are used instead of the above, then an atom can +cross the velocity threshold multiple times, and will be output for +*twindow* timesteps each time. However the write_dump command is no +longer useful. + +.. code-block:: LAMMPS + + variable vthresh equal 2 # threshold velocity + variable twindow equal 10 # dump for this many steps + # + # define custom property i_dump to store timestep threshold is crossed + # + fix 2 all property/atom i_dump + set group all i_dump -1 + # + # fix set command checks for threshold crossings every step + # resets i_dump from -1 to current timestep when crossing occurs + # + variable start atom "vx > v_vthresh && i_dump == -1" + variable turnon atom ternary(v_start,step,i_dump) + variable stop atom "v_turnon >= 0 && (step-v_turnon) < v_twindow" + variable turnoff atom ternary(v_stop,v_turnon,-1) + fix 3 all set 1 0 group all i_dump v_turnoff + # + # dump command with thresh which enforces twindow + # + dump 1 all custom 1 tmp.dump id x y vx i_dump + variable dumpflag atom "i_dump >= 0 && (step-i_dump) < v_twindow" + dump_modify 1 thresh v_dumpflag == 1 + # + # run the simulation + # + run 1000 + +---------- + +Restart, fix_modify, output, run start/stop, minimize info +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +No information about this fix is written to :doc:`binary restart files +`. None of the :doc:`fix_modify ` options are +relevant to this fix. No global or per-atom quantities are stored by +this fix for access by various :doc:`output commands `. +No parameter of this fix can be used with the *start/stop* keywords of +the :doc:`run ` command. This fix is not invoked during +:doc:`energy minimization `. + +Restrictions +"""""""""""" + +none + +Related commands +"""""""""""""""" + +:doc:`set ` + +Default +""""""" + +none diff --git a/doc/src/kspace_style.rst b/doc/src/kspace_style.rst index e7d5e93d72..3f9bdbb747 100644 --- a/doc/src/kspace_style.rst +++ b/doc/src/kspace_style.rst @@ -32,6 +32,7 @@ .. index:: kspace_style msm/cg/omp .. index:: kspace_style msm/dielectric .. index:: kspace_style scafacos +.. index:: kspace_style zero kspace_style command ==================== @@ -43,7 +44,7 @@ Syntax kspace_style style value -* style = *none* or *ewald* or *ewald/dipole* or *ewald/dipole/spin* or *ewald/disp* or *ewald/disp/dipole* or *ewald/omp* or *ewald/electrode* or *pppm* or *pppm/cg* or *pppm/disp* or *pppm/tip4p* or *pppm/stagger* or *pppm/disp/tip4p* or *pppm/gpu* or *pppm/intel* or *pppm/disp/intel* or *pppm/kk* or *pppm/omp* or *pppm/cg/omp* or *pppm/disp/tip4p/omp* or *pppm/tip4p/omp* or *pppm/dielectic* or *pppm/disp/dielectric* or *pppm/electrode* or *pppm/electrode/intel* or *msm* or *msm/cg* or *msm/omp* or *msm/cg/omp* or *msm/dielectric* or *scafacos* +* style = *none* or *ewald* or *ewald/dipole* or *ewald/dipole/spin* or *ewald/disp* or *ewald/disp/dipole* or *ewald/omp* or *ewald/electrode* or *pppm* or *pppm/cg* or *pppm/disp* or *pppm/tip4p* or *pppm/stagger* or *pppm/disp/tip4p* or *pppm/gpu* or *pppm/intel* or *pppm/disp/intel* or *pppm/kk* or *pppm/omp* or *pppm/cg/omp* or *pppm/disp/tip4p/omp* or *pppm/tip4p/omp* or *pppm/dielectic* or *pppm/disp/dielectric* or *pppm/electrode* or *pppm/electrode/intel* or *msm* or *msm/cg* or *msm/omp* or *msm/cg/omp* or *msm/dielectric* or *scafacos* or *zero* .. parsed-literal:: @@ -121,6 +122,7 @@ Syntax *scafacos* values = method accuracy method = fmm or p2nfft or p3m or ewald or direct accuracy = desired relative error in forces + *zero* value = none Examples """""""" @@ -132,6 +134,7 @@ Examples kspace_style msm 1.0e-4 kspace_style scafacos fmm 1.0e-4 kspace_style none + kspace_style zero Used in input scripts: @@ -375,6 +378,13 @@ other ScaFaCoS options currently exposed to LAMMPS. ---------- +.. versionadded:: 12Jun2025 + +The *zero* style does not do any calculations, but is compatible +with all pair styles that require some version of a kspace style. + +---------- + The specified *accuracy* determines the relative RMS error in per-atom forces calculated by the long-range solver. It is set as a dimensionless number, relative to the force that two unit point diff --git a/doc/src/molecule.rst b/doc/src/molecule.rst index 903084379b..1dcae66f1a 100644 --- a/doc/src/molecule.rst +++ b/doc/src/molecule.rst @@ -34,7 +34,7 @@ Syntax *ioff* value = Ioff Ioff = offset to add to improper types *scale* value = sfactor - sfactor = scale factor to apply to the size and mass of the molecule + sfactor = scale factor to apply to the size, mass, and dipole of the molecule Examples """""""" @@ -42,6 +42,7 @@ Examples .. code-block:: LAMMPS molecule 1 mymol.txt + molecule water tip3p.json molecule 1 co2.txt h2o.txt molecule CO2 co2.txt boff 3 aoff 2 molecule 1 mymol.txt offset 6 9 18 23 14 @@ -65,7 +66,7 @@ templates include: * :doc:`atom_style template ` The ID of a molecule template can only contain alphanumeric characters -and underscores. +and underscores, same as other IDs in LAMMPS. A single template can contain multiple molecules, listed one per file. Some of the commands listed above currently use only the first @@ -74,6 +75,11 @@ contains multiple molecules. The :doc:`atom_style template ` command allows multiple-molecule templates to define a system with more than one templated molecule. +The molecule file can be either in a *native* format or in `JSON format +`_. The details of the two formats are described +below. When referencing multiple molecule files in a single *molecule* +command, each of those files may be either format. + Each filename can be followed by optional keywords which are applied only to the molecule in the file as used in this template. This is to make it easy to use the same molecule file in different molecule @@ -95,40 +101,45 @@ use that attribute (e.g. no bonds). labels will determine the actual types directly depending on the current :doc:`labelmap ` settings. -The *scale* keyword scales the size of the molecule. This can be -useful for modeling polydisperse granular rigid bodies. The scale -factor is applied to each of these properties in the molecule file, if -they are defined: the individual particle coordinates (Coords -section), the individual mass of each particle (Masses section), the -individual diameters of each particle (Diameters section), the total -mass of the molecule (header keyword = mass), the center-of-mass of -the molecule (header keyword = com), and the moments of inertia of the -molecule (header keyword = inertia). +The *scale* keyword scales the size of the molecule. This can be useful +for modeling polydisperse granular rigid bodies. The scale factor is +applied to each of these properties in the molecule file, if they are +defined: the individual particle coordinates (Coords or "coords" +section), the individual mass of each particle (Masses or "masses" +section), the individual diameters of each particle (Diameters or +"diameters" section), the per-atom dipoles (Dipoles or "dipoles" +section) the total mass of the molecule (header keyword = mass), the +center-of-mass of the molecule (header keyword = com), and the moments +of inertia of the molecule (header keyword = inertia). .. note:: The molecule command can be used to define molecules with bonds, - angles, dihedrals, impropers, or special bond lists of neighbors + angles, dihedrals, impropers, and special bond lists of neighbors within a molecular topology, so that you can later add the molecules to your simulation, via one or more of the commands listed above. - Since this topology-related information requires that suitable storage - is reserved when LAMMPS creates the simulation box (e.g. when using - the :doc:`create_box ` command or the - :doc:`read_data ` command) suitable space has to be reserved - so you do not overflow those pre-allocated data structures when adding - molecules later. Both the :doc:`create_box ` command and - the :doc:`read_data ` command have "extra" options which - ensure space is allocated for storing topology info for molecules that - are added later. + Since this topology-related information requires that suitable + storage is reserved when LAMMPS creates the simulation box (e.g. when + using the :doc:`create_box ` command or the + :doc:`read_data ` command) suitable space has to be + reserved at that step so you do not overflow those pre-allocated data + structures when adding molecules later. Both the :doc:`create_box + ` command and the :doc:`read_data ` command + have "extra" options which ensure extra space is allocated for + storing topology info for molecules that are added later. This + feature is *not* available for the :doc:`read_restart command + `, thus binary restart files need to be converted + to data files first. ---------- -Format of a molecule file -""""""""""""""""""""""""" +Format of a native molecule file +"""""""""""""""""""""""""""""""" -The format of an individual molecule file looks similar but is -different than that of a data file read by the :doc:`read_data ` -commands. Here is a simple example for a TIP3P water molecule: +The format of an "native" individual molecule file looks similar but is +*different* from that of a data file read by the :doc:`read_data +` commands. Here is a simple example for a TIP3P water +molecule: .. code-block:: @@ -669,6 +680,189 @@ the file format. ---------- +Format of a JSON molecule file +"""""""""""""""""""""""""""""" + +The format of a JSON format individual molecule file must follow the +`JSON format `_, which evolved from the +JavaScript programming language as a programming-language-neutral data +interchange language. The JSON syntax is independent of its content, +and thus the data in the file must follow suitable conventions to be +correctly processed. LAMMPS provides a `JSON schema file +`_ for JSON format molecule files in the +:ref:`tools/json folder ` to represent those conventions. Using +the schema file any JSON format molecule files can be validated. +Validating a particular JSON format molecule file against this schema +ensures that both, the JSON syntax requirement *and* the LAMMPS +conventions for molecule templates are followed. This is a formal check +only and thus it **cannot** check whether the file contents are +physically meaningful. + +Here is a simple example for the same TIP3P water molecule from above in +JSON format and also using :doc:`type labels ` instead of +numeric types: + +.. code-block:: json + + { + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "title": "Water molecule. TIP3P geometry", + "schema": "https://download.lammps.org/json/molecule-schema.json", + "units": "real", + "coords": { + "format": ["atom-id", "x", "y", "z"], + "data": [ + [1, 0.00000, -0.06556, 0.00000], + [2, 0.75695, 0.52032, 0.00000], + [3, -0.75695, 0.52032, 0.00000] + ] + }, + "types": { + "format": ["atom-id", "type"], + "data": [ + [1, "OW"], + [2, "HO1"], + [3, "HO1"] + ] + }, + "charges": { + "format": ["atom-id", "charge"], + "data": [ + [1, -0.834], + [2, 0.417], + [3, 0.417] + ] + }, + "bonds": { + "format": ["bond-type", "atom1", "atom2"], + "data": [ + ["OW-HO1", 1, 2], + ["OW-HO1", 1, 3] + ] + }, + "angles": { + "format": ["angle-type", "atom1", "atom2", "atom3"], + "data": [ + ["HO1-OW-HO1", 2, 1, 3] + ] + } + } + +Unlike with the native molecule file format, there are no header or body +sections, just a list of keywords with associated data. JSON format +data is read, parsed, and stored in an internal dictionary data +structure in one step and thus the order of keywords is not relevant. + +Data for keywords is either provided directly following the keyword or +as a *data block*. A *data block* is a list that has to include two +keywords, "format" and "data", where the former lists keywords of the +properties that are stored in the columns of the "data" lists. The +names and order of entries in the "format" list (and thus how the data +is interpreted) are currently fixed. + +Since the length of the various lists can be easily obtained from the +internal data structure, several header keywords of the "native" molecule +file are not needed. On the other hand, some additional keywords are +required to identify the conventions applied to the generic JSON file +format. The structure of the data itself mostly follows what is used +for the "native" molecule file format. + +.. list-table:: + :header-rows: 1 + + * - Keyword + - Argument(s) + - Required + - Description + * - application + - "LAMMPS" + - yes + - indicates a LAMMPS JSON file; files from other applications may be accepted in the future + * - format + - "molecule" + - yes + - indicates a molecule template file + * - revision + - an integer + - yes + - currently 1, to facility backward compatibility on changes to the conventions + * - title + - a string + - no + - information about the template which will echoed to the screen and log + * - schema + - URL as string + - no + - location of a JSON schema file for validating the molecule file format + * - units + - a string + - no + - indicates :doc:`units settings ` for this molecule template + * - com + - list with 3 doubles + - no + - overrides the auto-computed center-of-mass for the template + * - masstotal + - double + - no + - overrides the auto-computed total mass for the template + * - inertia + - list with 6 doubles + - no + - overrides the auto-computed moments of inertia + * - coords + - a data block + - no + - contains atom positions with the format "atom-id", "x", "y", "z" (same as Coords) + * - types + - a data block + - yes + - assigns atom types to atoms with the format "atom-id", "type" (same as Types) + * - molecule + - a data block + - no + - assigns molecule-IDs to atoms with the format "atom-id", "molecule-id" (same as Molecules) + * - fragments + - a data block + - no + - assigns atom-ids to fragment-IDs with the format "fragment-id", "atom-id-list" (same as Fragments) + * - charges + - a data block + - no + - assigns charges to atoms with the format "atom-id", "charge" (same as Charges) + * - dipoles + - a data block + - no + - assigns point dipoles to atoms with the format "atom-id", "mux", "muy", "muz" (same as Dipoles) + * - diameters + - a data block + - no + - assigns diameters to atoms with the format "atom-id", "diameter" (same as Diameters) + * - masses + - a data block + - no + - assigns per-atom masses to atoms with the format "atom-id", "mass" (same as Masses) + * - bonds + - a data block + - no + - defines bonds in the molecule template with the format "bond-type", "atom1", "atom2" (same as Bonds without bond-ID) + * - angles + - a data block + - no + - defines angles in the molecule template with the format "angle-type", "atom1", "atom2", "atom3" (same as Angles without angle-ID) + * - dihedrals + - a data block + - no + - defines dihedrals in the molecule template with the format "dihedral-type", "atom1", "atom2", "atom3", "atom4" (same as Dihedrals without dihedral-ID) + * - impropers + - a data block + - no + - defines impropers in the molecule template with the format "improper-type", "atom1", "atom2", "atom3", "atom4" (same as Impropers without improper-ID) + +---------- + Restrictions """""""""""" diff --git a/doc/src/pair_hybrid.rst b/doc/src/pair_hybrid.rst index 93e5621736..05cc8ddf24 100644 --- a/doc/src/pair_hybrid.rst +++ b/doc/src/pair_hybrid.rst @@ -100,6 +100,56 @@ first is assigned to intra-molecular interactions (i.e. both atoms have the same molecule ID), the second to inter-molecular interactions (i.e. interacting atoms have different molecule IDs). +.. admonition:: When **NOT** to use a hybrid pair style + :class: warning + + Using pair style *hybrid* can be very tempting to use if you need a + **many-body potential** supporting a mix of elements for which you + cannot find a potential file that covers *all* of them. Regardless + of how this is set up, there will be *errors*. The major use case + where the error is *small*, is when the many-body sub-styles are used + on different objects (for example a slab and a liquid, a metal and a + nano-machining work piece). In that case the *mixed* terms + **should** be provided by a pair-wise additive potential (like + Lennard-Jones or Morse) to avoid unexpected behavior and reduce + errors. LAMMPS cannot easily check for this condition and thus will + accept good and bad choices alike. + + Outside of this, we *strongly* recommend *against* using pair style + hybrid with many-body potentials for the following reasons: + + 1. When trying to combine EAM or MEAM potentials, there is a *large* + error in the embedding term, since it is computed separately for + each sub-style only. + + 2. When trying to combine many-body potentials like Stillinger-Weber, + Tersoff, AIREBO, Vashishta, or similar, you have to understand + that the potential of a sub-style cannot be applied in a pair-wise + fashion but will need to be applied to multiples of atoms + (e.g. a Tersoff potential of elements A and B includes the + interactions A-A, B-B, A-B, A-A-A, A-A-B, A-B-B, A-B-A, B-A-A, + B-A-B, B-B-A, B-B-B; AIREBO also considers all quadruples of + atom elements). + + 3. When one of the sub-styles uses charge-equilibration (= QEq; like + in ReaxFF or COMB) you have inconsistent QEq behavior because + either you try to apply QEq to *all* atoms but then you are + missing the QEq parameters for the non-QEq pair style (and it + would be inconsistent to apply QEq for pair styles that are not + parameterized for QEq) or else you would have either no charges or + fixed charges interacting with the QEq which also leads to + inconsistent behavior between two sub-styles. When attempting to + use multiple ReaxFF instances to combine different potential + files, you might be able to work around the QEq limitations, but + point 2. still applies. + + We understand that it is frustrating to not be able to run simulations + due to lack of available potential files, but that does not justify + combining potentials in a broken way via pair style hybrid. This is + not what the hybrid pair styles are designed for. + +---------- + Here are two examples of hybrid simulations. The *hybrid* style could be used for a simulation of a metal droplet on a LJ surface. The metal atoms interact with each other via an *eam* potential, the surface atoms @@ -374,12 +424,11 @@ selected sub-style. ---------- -.. note:: - - Several of the potentials defined via the pair_style command in - LAMMPS are really many-body potentials, such as Tersoff, AIREBO, MEAM, - ReaxFF, etc. The way to think about using these potentials in a - hybrid setting is as follows. +Even though the command name "pair_style" would suggest that these are +pair-wise interactions, several of the potentials defined via the +pair_style command in LAMMPS are really many-body potentials, such as +Tersoff, AIREBO, MEAM, ReaxFF, etc. The way to think about using these +potentials in a hybrid setting is as follows. A subset of atom types is assigned to the many-body potential with a single :doc:`pair_coeff ` command, using "\* \*" to include diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index f8f668789d..e5198604c7 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -10,16 +10,17 @@ Syntax plugin command args -* command = *load* or *unload* or *list* or *clear* +* command = *load* or *unload* or *list* or *clear* or *restore* * args = list of arguments for a particular plugin command .. parsed-literal:: *load* file = load plugin(s) from shared object in *file* *unload* style name = unload plugin *name* of style *style* - *style* = *pair* or *bond* or *angle* or *dihedral* or *improper* or *kspace* or *compute* or *fix* or *region* or *command* + *style* = *pair* or *bond* or *angle* or *dihedral* or *improper* or *kspace* or *compute* or *fix* or *region* or *command* or *run* or *min* *list* = print a list of currently loaded plugins *clear* = unload all currently loaded plugins + *restore* = restore all loaded plugins Examples """""""" @@ -31,6 +32,7 @@ Examples plugin unload command hello plugin list plugin clear + plugin restore Description """"""""""" @@ -40,22 +42,46 @@ commands into a LAMMPS binary from so-called dynamic shared object (DSO) files. This enables to add new functionality to an existing LAMMPS binary without having to recompile and link the entire executable. +.. admonition:: Plugins are a global, per-executable property + :class: Hint + + Unlike most settings in LAMMPS, plugins are a per-executable global + property. Loading a plugin means that it is not only available for + the current LAMMPS instance but for all *future* LAMMPS instances. + + After a :doc:`clear ` command, all currently loaded plugins + will be restored and do not need to be loaded again. + + When using the library interface or the Python or Fortran module + to create multiple concurrent LAMMPS instances, all plugins should + be loaded by the first created LAMMPS instance as all future instances + will inherit them. To import plugins that were loaded by a different + LAMMPS instance, use the *restore* command. + + The *load* command will load and initialize all plugins contained in the -plugin DSO with the given filename. A message with information the -plugin style and name and more will be printed. Individual DSO files -may contain multiple plugins. More details about how to write and +plugin DSO with the given filename. A message with information about +the plugin style and name and more will be printed. Individual DSO +files may contain multiple plugins. If a plugin is already loaded +it will be skipped. More details about how to write and compile the plugin DSO is given in programmer's guide part of the manual under :doc:`Developer_plugins`. The *unload* command will remove the given style or the given name from the list of available styles. If the plugin style is currently in use, -that style instance will be deleted. +that style instance will be deleted and replaced by the default setting +for that style. The *list* command will print a list of the loaded plugins and their styles and names. The *clear* command will unload all currently loaded plugins. +.. versionadded:: 12Jun2025 + +The *restore* command will restore all currently loaded plugins. +This allows to "import" plugins into a different LAMMPS instance. + .. admonition:: Automatic loading of plugins :class: note @@ -79,7 +105,7 @@ If plugins access functions or classes from a package, LAMMPS must have been compiled with that package included. Plugins are dependent on the LAMMPS binary interface (ABI) -and particularly the MPI library used. So they are not guaranteed +and particularly the MPI library used. So they are not guaranteed to work when the plugin was compiled with a different MPI library or different compilation settings or a different LAMMPS version. There are no checks, so if there is a mismatch the plugin object diff --git a/doc/src/python.rst b/doc/src/python.rst index 99f32e7c80..bdde9c9a78 100644 --- a/doc/src/python.rst +++ b/doc/src/python.rst @@ -10,7 +10,7 @@ Syntax python mode keyword args ... -* mode = *source* or name of Python function +* mode = *source* or *name* of Python function if mode is *source*: @@ -18,35 +18,39 @@ Syntax keyword = *here* or name of a *Python file* *here* arg = inline - inline = one or more lines of Python code which defines func + inline = one or more lines of Python code which will be executed immediately must be a single argument, typically enclosed between triple quotes *Python file* = name of a file with Python code which will be executed immediately -* if *mode* is the name of a Python function, one or more keywords with/without arguments must be appended +* if *mode* is *name* of a Python function: .. parsed-literal:: + one or more keywords with/without arguments must be appended keyword = *invoke* or *input* or *return* or *format* or *length* or *file* or *here* or *exists* - *invoke* arg = none = invoke the previously defined Python function + *invoke* arg = logreturn (optional) + invoke the previously-defined Python function + if logreturn is specified, print the return value of the invoked function to the screen and logfile *input* args = N i1 i2 ... iN N = # of inputs to function i1,...,iN = value, SELF, or LAMMPS variable name value = integer number, floating point number, or string - SELF = reference to LAMMPS itself which can be accessed by Python function - variable = v_name, where name = name of LAMMPS variable, e.g. v_abc + SELF = reference to LAMMPS itself which can then be accessed by Python function + variable = v_name, where name = name of a LAMMPS variable, e.g. v_abc + internal variable = iv_name, where name = name of a LAMMPS internal-style variable, e.g. iv_xyz *return* arg = varReturn varReturn = v_name = LAMMPS variable name which the return value of the Python function will be assigned to *format* arg = fstring with M characters M = N if no return value, where N = # of inputs M = N+1 if there is a return value - fstring = each character (i,f,s,p) corresponds in order to an input or return value - 'i' = integer, 'f' = floating point, 's' = string, 'p' = SELF + fstring = each character (i,f,s,p) corresponds (in order) to an input or return value + 'i' = integer, 'f' = floating point, 's' = string, 'p' = SELF *length* arg = Nlen Nlen = max length of string returned from Python function *file* arg = filename - filename = file of Python code, which defines func + filename = file of Python code, which defines the Python function *here* arg = inline - inline = one or more lines of Python code which defines func + inline = one or more lines of Python code which defines the Python function must be a single argument, typically enclosed between triple quotes *exists* arg = none = Python code has been loaded by previous python command @@ -56,7 +60,7 @@ Examples .. code-block:: LAMMPS python pForce input 2 v_x 20.0 return v_f format fff file force.py - python pForce invoke + python pForce invoke logreturn python factorial input 1 myN return v_fac format ii here """ def factorial(n): @@ -87,75 +91,149 @@ Examples Description """"""""""" -The *python* command allows interfacing LAMMPS with an embedded Python -interpreter and enables either executing arbitrary python code in that -interpreter, registering a Python function for future execution (as a -python style variable, from a fix interfaced with python, or for direct -invocation), or invoking such a previously registered function. +The *python* command interfaces LAMMPS with an embedded Python +interpreter and enables executing arbitrary python code in that +interpreter. This can be done immediately, by using *mode* = *source*. +Or execution can be deferred, by registering a Python function for later +execution, by using *mode* = *name* of a Python function. -Arguments, including LAMMPS variables, can be passed to the function -from the LAMMPS input script and a value returned by the Python function -assigned to a LAMMPS variable. The Python code for the function can be included -directly in the input script or in a separate Python file. The function -can be standard Python code or it can make "callbacks" to LAMMPS through -its library interface to query or set internal values within LAMMPS. -This is a powerful mechanism for performing complex operations in a -LAMMPS input script that are not possible with the simple input script -and variable syntax which LAMMPS defines. Thus your input script can -operate more like a true programming language. +Later execution can be triggered in one of two ways. One is to use the +python command again with its *invoke* keyword. The other is to trigger +the evaluation of a python-style, equal-style, vector-style, or +atom-style variable. A python-style variable invokes its associated +Python function; its return value becomes the value of the python-style +variable. Equal-, vector-, and atom-style variables can use a Python +function wrapper in their formulas which encodes the python-style +variable name, and specifies arguments (which themselves can be numeric +formulas) to pass to the Python function associated with the +python-style variable. + +As explained on the :doc:`variable ` doc page, the definition +of a python-style variable associates a Python function name with the +variable. Its specification must match the *mode* argument of the +*python* command for the Python function name. For example these two +commands would be consistent: + +.. code-block:: LAMMPS + + variable foo python myMultiply + python myMultiply return v_foo format f file funcs.py + +The two commands can appear in either order in the input script so long +as both are specified before the Python function is invoked for the +first time. + +Note that python-style, equal-style, vector-style, and atom-style +variables can be used in many different ways within LAMMPS. They can be +evaluated directly in an input script, effectively replacing the +variable with its value. Or they can be passed to various commands as +arguments, so that the variable is evaluated multiple times during a +simulation run. See the :doc:`variable ` command doc page for +more details on variable styles which enable Python function evaluation. + +The Python code for a Python function can be included directly in the +input script or in a separate Python file. The function can be standard +Python code or it can make "callbacks" to LAMMPS through its library +interface to query or set internal values within LAMMPS. This is a +powerful mechanism for performing complex operations in a LAMMPS input +script that are not possible with the simple input script and variable +syntax which LAMMPS defines. Thus your input script can operate more +like a true programming language. Use of this command requires building LAMMPS with the PYTHON package which links to the Python library so that the Python interpreter is embedded in LAMMPS. More details about this process are given below. -There are two ways to invoke a Python function once it has been -registered. One is using the *invoke* keyword. The other is to assign -the function to a :doc:`python-style variable ` defined in -your input script. Whenever the variable is evaluated, it will execute -the Python function to assign a value to the variable. Note that -variables can be evaluated in many different ways within LAMMPS. They -can be substituted with their result directly in an input script, or -they can be passed to various commands as arguments, so that the -variable is evaluated during a simulation run. - A broader overview of how Python can be used with LAMMPS is given in the :doc:`Use Python with LAMMPS ` section of the -documentation. There also is an ``examples/python`` directory which +documentation. There is also an ``examples/python`` directory which illustrates use of the python command. ---------- -The first argument of the *python* command is either the *source* -keyword or the name of a Python function. This defines the mode -of the python command. +The first argument to the *python* command is the *mode* setting, which +is either *source* or the *name* of a Python function. .. versionchanged:: 22Dec2022 -If the *source* keyword is used, it is followed by either a file name or -the *here* keyword. No other keywords can be used. The *here* keyword -is followed by a string with python commands, either on a single line -enclosed in quotes, or as multiple lines enclosed in triple quotes. -These Python commands will be passed to the python interpreter and -executed immediately without registering a Python function for future -execution. The code will be loaded into and run in the "main" module of -the Python interpreter. This allows running arbitrary Python code at -any time while processing the LAMMPS input file. This can be used to -pre-load Python modules, initialize global variables, define functions -or classes, or perform operations using the python programming language. -The Python code will be executed in parallel on all MPI processes. No -arguments can be passed. +If *source* is used, it is followed by either the *here* keyword or a +file name containing Python code. The *here* keyword is followed by a +single *inline* argument which is a string containing one or more python +commands. The string can either be on the same line as the *python* +command, enclosed in quotes, or it can be multiple lines enclosed in +triple quotes. -In all other cases, the first argument is the name of a Python function -that will be registered with LAMMPS for future execution. The function -may already be defined (see *exists* keyword) or must be defined using -the *file* or *here* keywords as explained below. +In either case, the in-line code or the file contents are passed to the +python interpreter and executed immediately. The code will be loaded +into and run in the "main" module of the Python interpreter. This +allows running arbitrary Python code at any time while processing the +LAMMPS input file. This can be used to pre-load Python modules, +initialize global variables, define functions or classes, or perform +operations using the Python programming language. The Python code will +be executed in parallel on all the MPI processes being used to run +LAMMPS. Note that no arguments can be passed to the executed Python +code. -If the *invoke* keyword is used, no other keywords can be used, and a +If the *mode* setting is the *name* of a Python function, then it will +be registered with LAMMPS for future execution (or can already be +defined, see the *exists* keyword). One or more keywords must follow +the *mode* function name. One of the keywords must be *invoke*, *file*, +*here*, or *exists*, which specifies what Python code to load into the +Python interpreter. Note that only one of those 4 keywords is allowed +since their operations are mutually exclusive. + +---------- + +If the *invoke* keyword is used, no other keywords can be used. A previous *python* command must have registered the Python function -referenced by this command. This invokes the Python function with the -previously defined arguments and the return value is processed as -explained below. You can invoke the function as many times as you wish -in your input script. +referenced by this command, which can then be invoked multiple times in +an input script via the *invoke* keyword. Each invocation passes +current values for arguments to the Python function. A return value of +the Python function will be ignored unless the Python function is linked +to a :doc:`python style variable ` with the *return* keyword. +This return value can be logged to the screen and logfile by adding the +optional *logreturn* argument to the *invoke* keyword. In that case a +message with the name of the python command and the return value is +printed. Note that return values of python functions are otherwise +*only* accessible when the function is invoked indirectly by evaluating +its associated :doc:`python style variable `, as described +below. + +The *file* keyword gives the name of a file containing Python code, +which should end with a ".py" suffix. The code will be immediately +loaded into and run in the "main" module of the Python interpreter. The +Python code will be executed in parallel on all MPI processes. Note +that Python code which contains a function definition does NOT "execute" +the function when it is run; it simply defines the function so that it +can be invoked later. + +The *here* keyword does the same thing, except that the Python code +follows as a single argument to the *here* keyword. This can be done +using triple quotes as delimiters, as in the examples above and below. +This allows Python code to be listed verbatim in your input script, with +proper indentation, blank lines, and comments, as desired. See the +:doc:`Commands parse ` doc page, for an explanation of +how triple quotes can be used as part of input script syntax. + +The *exists* keyword takes no argument. It simply means that Python +code containing the needed Python function has already been loaded into +the LAMMPS Python interpreter, for example by previous *python source* +command or in a file that was loaded previously with the *file* +keyword. This allows use of a single file of Python code which contains +multiple functions, any of which can be used in the same (or different) +input scripts (see below). + +Note that the Python code that is loaded and run by the *file* or *here* +keyword must contain a function with the specified function *name*. To +operate properly when the function is later invoked, the code for the +function must match the *input* and *return* and *format* keywords +specified by the python command. Otherwise Python will generate an +error. + +---------- + +The other keywords which can be used with the *python* command are +*input*, *return*, *format*, and *length*. The *input* keyword defines how many arguments *N* the Python function expects. If it takes no arguments, then the *input* keyword should not @@ -169,35 +247,63 @@ itself using the :doc:`LAMMPS Python module `. This enables the function to call back to LAMMPS through its library interface as explained below. This allows the Python function to query or set values internal to LAMMPS which can affect the subsequent -execution of the input script. A LAMMPS variable can also be used as an -argument, specified as v_name, where "name" is the name of the variable. -Any style of LAMMPS variable returning a scalar or a string can be used, -as defined by the :doc:`variable ` command. The *format* -keyword must be used to set the type of data that is passed to Python. -Each time the Python function is invoked, the LAMMPS variable is -evaluated and its value is passed to the Python function. +execution of the input script. + +A LAMMPS variable can also be used as an *input* argument, specified as +v_name, where "name" is the name of the variable defined in the input +script. Any style of LAMMPS variable returning a scalar or a string can +be used, as defined by the :doc:`variable ` command. The +style of variable must be consistent with the *format* keyword +specification for the type of data that is passed to Python. Each time +the Python function is invoked, the LAMMPS variable is evaluated and its +value is passed as an argument to the Python function. Note that a +python-style variable can be used as an argument, which means that the a +Python function can use arguments which invoke other Python functions. + +A LAMMPS internal-style variable can also be used as an *input* +argument, specified as iv_name, where "name" is the name of the +internal-style variable. The internal-style variable does not have to +be defined in the input script (though it can be); if it is not defined, +this command creates an :doc:`internal-style variable ` with +the specified name. + +An internal-style variable must be used when an equal-style, +vector-style, or atom-style variable triggers the invocation of the +Python function defined by this command, by including a Python function +wrapper with arguments in its formula. Each of the arguments must be +specified as an internal-style variable via the *input* keyword. + +In brief, the syntax for a Python function wrapper in a variable formula +is ``py_varname(arg1,arg2,...argN)``, where "varname" is the name of a +python-style variable associated with a Python function defined by this +command. One or more arguments to the function wrapper can themselves +be sub-formulas which the variable command will evaluate and pass as +arguments to the Python function. This is done by assigning the numeric +result for each argument to an internal-style variable; thus the *input* +keyword must specify the arguments as internal-style variables and their +format (see below) as "f" for floating point. This is because LAMMPS +variable formulas are calculated with floating point arithmetic (any +integer values are converted to floating point). Note that the Python +function can also have additional inputs, also specified by the *input* +keyword, which are NOT arguments in the Python function wrapper. See +the example below for the ``mixedargs`` Python function. + +See the :doc:`variable ` command doc page for full details on +formula syntax including for Python function wrappers. Examples using +Python function wrappers are shown below. Note that as explained above +with python-style variables, Python function wrappers can be nested; a +sub-formula for an argument can contain its own Python function wrapper +which invokes another Python function. The *return* keyword is only needed if the Python function returns a -value. The specified *varReturn* must be of the form v_name, where -"name" is the name of a python-style LAMMPS variable, defined by the +value. The specified *varReturn* is of the form v_name, where "name" is +the name of a python-style LAMMPS variable, defined by the :doc:`variable ` command. The Python function can return a -numeric or string value, as specified by the *format* keyword. - -As explained on the :doc:`variable ` doc page, the definition -of a python-style variable associates a Python function name with the -variable. This must match the *Python function name* first argument of -the *python* command. For example these two commands would be -consistent: - -.. code-block:: LAMMPS - - variable foo python myMultiply - python myMultiply return v_foo format f file funcs.py - -The two commands can appear in either order in the input script so -long as both are specified before the Python function is invoked for -the first time. Afterwards, the variable 'foo' is associated with -the Python function 'myMultiply'. +numeric or string value, as specified by the *format* keyword. This +return value is *only* accessible when its associated python-style +variable is evaluated. When the *invoke* keyword is used, the return +value of the python function is ignored unless the optional *logreturn* +argument is specified. The *format* keyword must be used if the *input* or *return* keywords are used. It defines an *fstring* with M characters, where M = sum of @@ -214,47 +320,16 @@ but only if the output of the Python function is flagged as a numeric value ("i" or "f") via the *format* keyword. If the *return* keyword is used and the *format* keyword specifies the -output as a string, then the default maximum length of that string is -63 characters (64-1 for the string terminator). If you want to return -a longer string, the *length* keyword can be specified with its *Nlen* -value set to a larger number (the code allocates space for Nlen+1 to -include the string terminator). If the Python function generates a +output as a string, then the default maximum length of that string is 63 +characters (64-1 for the string terminator). If you want to return a +longer string, the *length* keyword can be specified with its *Nlen* +value set to a larger number. LAMMPS will then allocate Nlen+1 space to +include the string terminator. If the Python function generates a string longer than the default 63 or the specified *Nlen*, it will be truncated. ---------- -Either the *file*, *here*, or *exists* keyword must be used, but only -one of them. These keywords specify what Python code to load into the -Python interpreter. The *file* keyword gives the name of a file -containing Python code, which should end with a ".py" suffix. The code -will be immediately loaded into and run in the "main" module of the -Python interpreter. The Python code will be executed in parallel on all -MPI processes. Note that Python code which contains a function -definition does not "execute" the function when it is run; it simply -defines the function so that it can be invoked later. - -The *here* keyword does the same thing, except that the Python code -follows as a single argument to the *here* keyword. This can be done -using triple quotes as delimiters, as in the examples above. This -allows Python code to be listed verbatim in your input script, with -proper indentation, blank lines, and comments, as desired. See the -:doc:`Commands parse ` doc page, for an explanation of -how triple quotes can be used as part of input script syntax. - -The *exists* keyword takes no argument. It means that Python code -containing the required Python function with the given name has already -been executed, for example by a *python source* command or in the same -file that was used previously with the *file* keyword. - -Note that the Python code that is loaded and run must contain a function -with the specified function name. To operate properly when later -invoked, the function code must match the *input* and *return* and -*format* keywords specified by the python command. Otherwise Python -will generate an error. - ----------- - This section describes how Python code can be written to work with LAMMPS. @@ -275,16 +350,16 @@ keyword once to load several functions, and the *exists* keyword thereafter in subsequent python commands to register the other functions that were previously loaded with LAMMPS. -A Python function you define (or more generally, the code you load) -can import other Python modules or classes, it can make calls to other +A Python function you define (or more generally, the code you load) can +import other Python modules or classes, it can make calls to other system functions or functions you define, and it can access or modify global variables (in the "main" module) which will persist between successive function calls. The latter can be useful, for example, to prevent a function from being invoke multiple times per timestep by different commands in a LAMMPS input script that access the returned python-style variable associated with the function. For example, -consider this function loaded with two global variables defined -outside the function: +consider this function loaded with two global variables defined outside +the function: .. code-block:: python @@ -308,32 +383,33 @@ previous value is simply returned, without re-computing it. The "global" statement inside the Python function allows it to overwrite the global variables from within the local context of the function. -Note that if you load Python code multiple times (via multiple python -commands), you can overwrite previously loaded variables and functions -if you are not careful. E.g. if the code above were loaded twice, the -global variables would be re-initialized, which might not be what you -want. Likewise, if a function with the same name exists in two chunks -of Python code you load, the function loaded second will override the -function loaded first. +Also note that if you load Python code multiple times (via multiple +python commands), you can overwrite previously loaded variables and +functions if you are not careful. E.g. if the code above were loaded +twice, the global variables would be re-initialized, which might not be +what you want. Likewise, if a function with the same name exists in two +chunks of Python code you load, the function loaded second will override +the function loaded first. It's important to realize that if you are running LAMMPS in parallel, -each MPI task will load the Python interpreter and execute a local -copy of the Python function(s) you define. There is no connection -between the Python interpreters running on different processors. -This implies three important things. +each MPI task will load the Python interpreter and execute a local copy +of the Python function(s) you define. There is no connection between +the Python interpreters running on different processors. This implies +three important things. First, if you put a print or other statement creating output to the screen in your Python function, you will see P copies of the output, when running on P processors. If the prints occur at (nearly) the same -time, the P copies of the output may be mixed together. When loading -the LAMMPS Python module into the embedded Python interpreter, it is -possible to pass the pointer to the current LAMMPS class instance and -via the Python interface to the LAMMPS library interface, it is possible -to determine the MPI rank of the current process and thus adapt the -Python code so that output will only appear on MPI rank 0. The -following LAMMPS input demonstrates how this could be done. The text -'Hello, LAMMPS!' should be printed only once, even when running LAMMPS -in parallel. +time, the P copies of the output may be mixed together. + +It is possible to avoid this issue, by passing the pointer to the +current LAMMPS class instance to the Python function via the {input} +SELF argument described above. The Python function can then use the +Python interface to the LAMMPS library interface, and determine the MPI +rank of the current process. The Python code can then ensure output +will only appear on MPI rank 0. The following LAMMPS input demonstrates +how this could be done. The text 'Hello, LAMPS!' should be printed only +once, even when running LAMMPS in parallel. .. code-block:: LAMMPS @@ -348,27 +424,26 @@ in parallel. python python_hello invoke -If your Python code loads Python modules that are not pre-loaded by the -Python library, then it will load the module from disk. This may be a -bottleneck if 1000s of processors try to load a module at the same time. -On some large supercomputers, loading of modules from disk by Python may -be disabled. In this case you would need to pre-build a Python library -that has the required modules pre-loaded and link LAMMPS with that -library. +Second, if your Python code loads Python modules that are not pre-loaded +by the Python library, then it will load the module from disk. This may +be a bottleneck if 1000s of processors try to load a module at the same +time. On some large supercomputers, loading of modules from disk by +Python may be disabled. In this case you would need to pre-build a +Python library that has the required modules pre-loaded and link LAMMPS +with that library. -Third, if your Python code calls back to LAMMPS (discussed in the -next section) and causes LAMMPS to perform an MPI operation requires -global communication (e.g. via MPI_Allreduce), such as computing the -global temperature of the system, then you must ensure all your Python +Third, if your Python code calls back to LAMMPS (discussed in the next +section) and causes LAMMPS to perform an MPI operation requires global +communication (e.g. via MPI_Allreduce), such as computing the global +temperature of the system, then you must ensure all your Python functions (running independently on different processors) call back to LAMMPS. Otherwise the code may hang. ---------- -Your Python function can "call back" to LAMMPS through its -library interface, if you use the SELF input to pass Python -a pointer to LAMMPS. The mechanism for doing this in your -Python function is as follows: +As mentioned above, a Python function can "call back" to LAMMPS through +its library interface, if the SELF input is used to pass Python a +pointer to LAMMPS. The mechanism for doing this is as follows: .. code-block:: python @@ -393,15 +468,15 @@ appeared in your input script. In this case, LAMMPS should output Hello from inside Python to the screen and log file. Note that since the LAMMPS print command -itself takes a string in quotes as its argument, the Python string -must be delimited with a different style of quotes. +itself takes a string in quotes as its argument, the Python string must +be delimited with a different style of quotes. -The :doc:`Python_head` page describes the syntax -for how Python wraps the various functions included in the LAMMPS -library interface. +The :doc:`Python_head` page describes the syntax for how Python wraps +the various functions included in the LAMMPS library interface. -A more interesting example is in the ``examples/python/in.python`` script -which loads and runs the following function from ``examples/python/funcs.py``: +A more interesting example is in the ``examples/python/in.python`` +script which loads and runs the following function from +``examples/python/funcs.py``: .. code-block:: python @@ -416,7 +491,7 @@ which loads and runs the following function from ``examples/python/funcs.py``: lmp.set_variable("cut",cut) # set a variable in LAMMPS lmp.command("pair_style lj/cut ${cut}") # LAMMPS command - #lmp.command("pair_style lj/cut %d" % cut) # LAMMPS command option + #lmp.command("pair_style lj/cut %d" % cut) # alternate form of LAMMPS command lmp.command("pair_coeff * * 1.0 1.0") # ditto lmp.command("run 10") # ditto @@ -432,51 +507,160 @@ with these input script commands: python loop invoke This has the effect of looping over a series of 10 short runs (10 -timesteps each) where the pair style cutoff is increased from a value -of 1.0 in distance units, in increments of 0.1. The looping stops -when the per-atom potential energy falls below a threshold of -4.0 in -energy units. More generally, Python can be used to implement a loop -with complex logic, much more so than can be created using the LAMMPS +timesteps each) where the pair style cutoff is increased from a value of +1.0 in distance units, in increments of 0.1. The looping stops when the +per-atom potential energy falls below a threshold of -4.0 in energy +units. More generally, Python can be used to implement a loop with +complex logic, much more so than can be created using the LAMMPS :doc:`jump ` and :doc:`if ` commands. Several LAMMPS library functions are called from the loop function. Get_natoms() returns the number of atoms in the simulation, so that it can be used to normalize the potential energy that is returned by -extract_compute() for the "thermo_pe" compute that is defined by -default for LAMMPS thermodynamic output. Set_variable() sets the -value of a string variable defined in LAMMPS. This library function -is a useful way for a Python function to return multiple values to -LAMMPS, more than the single value that can be passed back via a -return statement. This cutoff value in the "cut" variable is then -substituted (by LAMMPS) in the pair_style command that is executed -next. Alternatively, the "LAMMPS command option" line could be used -in place of the 2 preceding lines, to have Python insert the value -into the LAMMPS command string. +extract_compute() for the "thermo_pe" compute that is defined by default +for LAMMPS thermodynamic output. Set_variable() sets the value of a +string variable defined in LAMMPS. This library function is a useful +way for a Python function to return multiple values to LAMMPS, more than +the single value that can be passed back via a return statement. This +cutoff value in the "cut" variable is then substituted (by LAMMPS) in +the pair_style command that is executed next. Alternatively, the +"alternate form of LAMMPS command" line could be used in place of the 2 +preceding lines, to have Python insert the value into the LAMMPS command +string. .. note:: When using the callback mechanism just described, recognize that - there are some operations you should not attempt because LAMMPS cannot - execute them correctly. If the Python function is invoked between - runs in the LAMMPS input script, then it should be OK to invoke any - LAMMPS input script command via the library interface command() or - file() functions, so long as the command would work if it were - executed in the LAMMPS input script directly at the same point. + there are some operations you should not attempt because LAMMPS + cannot execute them correctly. If the Python function is invoked + between runs in the LAMMPS input script, then it should be OK to + invoke any LAMMPS input script command via the library interface + command() or file() functions, so long as the command would work if + it were executed in the LAMMPS input script directly at the same + point. -However, a Python function can also be invoked during a run, whenever -an associated LAMMPS variable it is assigned to is evaluated. If the -variable is an input argument to another LAMMPS command (e.g. :doc:`fix setforce `), then the Python function will be invoked -inside the class for that command, in one of its methods that is -invoked in the middle of a timestep. You cannot execute arbitrary -input script commands from the Python function (again, via the -command() or file() functions) at that point in the run and expect it -to work. Other library functions such as those that invoke computes -or other variables may have hidden side effects as well. In these -cases, LAMMPS has no simple way to check that something illogical is -being attempted. -The same applies to Python functions called during a simulation run at -each time step using :doc:`fix python/invoke `. +---------- + +As noted above, a Python function can be invoked during a run, whenever +an associated python-style variable it is assigned to is evaluated. + +If the variable is an input argument to another LAMMPS command +(e.g. :doc:`fix setforce `), then the Python function will +be invoked inside the class for that command, possibly in one of its +methods that is invoked in the middle of a timestep. You cannot execute +arbitrary input script commands from the Python function (again, via the +command() or file() functions) at that point in the run and expect it to +work. Other library functions such as those that invoke computes or +other variables may have hidden side effects as well. In these cases, +LAMMPS has no simple way to check that something illogical is being +attempted. + +The same constraints apply to Python functions called during a +simulation run at each time step using the :doc:`fix python/invoke +` command. + +---------- + +As noted above, a Python function can also be invoked within the formula +for an equal-style, vector-style, or atom-style variable. This means +the Python function will be invoked whenever that variable is invoked. +In the case of a vector-style variable, the Python function can be +invoked once per element of the global vector. In the case of an +atom-style variable, the Python function can be invoked once per atom. + +Here are three simple examples using equal-, vector-, and atom-style +variables to trigger execution of a Python function: + +.. code-block:: LAMMPS + + variable foo python truncate + python truncate return v_foo input 1 iv_arg format fi here """ + def truncate(x): + return int(x) + """ + variable ptrunc equal py_foo(press) + print "TRUNCATED pressure = ${ptrunc}" + +The Python ``truncate`` function simply converts a floating-point value +to an integer value. When the LAMMPS print command evaluates the +equal-style ``ptrunc`` variable, the current thermodynamic pressure is +passed to the Python function. The truncated value is output to the +screen and logfile by the print command. Note that the *input* keyword +for the *python* command, specifies an internal-style variable named +"arg" as iv_arg which is required to invoke the Python function from a +Python function wrapper. + +The last 2 lines can be replaced by these to define a vector-style +variable which invokes the same Python ``truncate`` function: + +.. code-block:: LAMMPS + + compute ke all temp + variable ke vector c_ke + variable ketrunc vector py_foo(v_ke) + thermo_style custom step temp epair v_ketrunc[*6] + +The vector-style variable ``ketrunc`` invokes the Python ``truncate`` +function on each of the 6 components of the global kinetic energy tensor +calculated by the :doc:`compute ke ` command. The 6 +truncated values will be printed with thermo output to the screen and +log file. + +Or the last 2 lines of the equal-style variable example can be replaced +by these to define atom-style variables which invoke the same Python +``truncate`` function: + +.. code-block:: LAMMPS + + variable xtrunc atom py_foo(x) + variable ytrunc atom py_foo(y) + variable ztrunc atom py_foo(z) + dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc + +When the dump command invokes the 3 atom-style variables, their +arguments x,y,z to the Python function wrapper are the current per-atom +coordinates of each atom. The Python ``truncate`` function is thus +invoked 3 times for each atom, and the truncated coordinate values for +each atom are written to the dump file. + +Note that when using a Python function wrapper in a variable, arguments +can be passed to the Python function either from the variable formula or +by *input* keyword to the :doc:`python command `. For example, +consider these (made up) commands: + +.. code-block:: LAMMPS + + variable foo python mixedargs + python mixedargs return v_foo input 6 7.5 v_myValue iv_arg1 iv_argy iv_argz v_flag & + format fffffsf here """ + def mixedargs(a,b,x,y,z,flag): + ... + return result + """ + variable flag string optionABC + variable myValue equal "2.0*temp*c_pe" + compute pe all pe + compute peatom all pe/atom + variable field atom py_foo(x+3.0,sqrt(y),(z-zlo)*c_peatom) + +They define a Python ``mixedargs`` function with 6 arguments. Three of +them are internal-style variables, which the variable formula calculates +as numeric values for each atom and passes to the function. In this +example, these arguments are themselves small formulas containing the +x,y,z coordinates of each atom as well as a per-atom compute (c_peratom) +and thermodynamic keyword (zlo). + +The other three arguments ``(7.5,v_myValue,v_flag)`` are defined by the +*python* command. The first and last are constant values ("7.5" and the +``optionABC`` string). The second argument (``myValue``) is the result +of an equal-style variable formula which accesses the system temperature +and potential energy. + +The "result" returned by each invocation of the Python ``mixedargs`` +function becomes the per-atom value in the atom-style "field" variable, +which could be output to a dump file or used elsewhere in the input +script. ---------- @@ -485,12 +669,11 @@ interactively or by using Python to launch a Python script stored in a file, and your code has an error, you will typically see informative error messages. That is not the case when you run Python code from LAMMPS using an embedded Python interpreter. The code will typically -fail silently. LAMMPS will catch some errors but cannot tell you -where in the Python code the problem occurred. For example, if the -Python code cannot be loaded and run because it has syntax or other -logic errors, you may get an error from Python pointing to the -offending line, or you may get one of these generic errors from -LAMMPS: +fail silently. LAMMPS will catch some errors but cannot tell you where +in the Python code the problem occurred. For example, if the Python +code cannot be loaded and run because it has syntax or other logic +errors, you may get an error from Python pointing to the offending line, +or you may get one of these generic errors from LAMMPS: .. parsed-literal:: @@ -504,16 +687,16 @@ you will typically get this generic error from LAMMPS: Python function evaluation failed -Here are three suggestions for debugging your Python code while -running it under LAMMPS. +Here are three suggestions for debugging your Python code while running +it under LAMMPS. First, don't run it under LAMMPS, at least to start with! Debug it using plain Python. Load and invoke your function, pass it arguments, check return values, etc. -Second, add Python print statements to the function to check how far -it gets and intermediate values it calculates. See the discussion -above about printing from Python when running in parallel. +Second, add Python print statements to the function to check how far it +gets and intermediate values it calculates. See the discussion above +about printing from Python when running in parallel. Third, use Python exception handling. For example, say this statement in your Python function is failing, because you have not initialized the @@ -523,8 +706,7 @@ variable foo: foo += 1 -If you put one (or more) statements inside a "try" statement, -like this: +If you put one (or more) statements inside a "try" statement, like this: .. code-block:: python @@ -563,13 +745,15 @@ If you use Python code which calls back to LAMMPS, via the SELF input argument explained above, there is an extra step required when building LAMMPS. LAMMPS must also be built as a shared library and your Python function must be able to load the :doc:`"lammps" Python module -` that wraps the LAMMPS library interface. These are the -same steps required to use Python by itself to wrap LAMMPS. Details on -these steps are explained on the :doc:`Python ` doc page. -Note that it is important that the stand-alone LAMMPS executable and the -LAMMPS shared library be consistent (built from the same source code -files) in order for this to work. If the two have been built at -different times using different source files, problems may occur. +` that wraps the LAMMPS library interface. + +These are the same steps required to use Python by itself to wrap +LAMMPS. Details on these steps are explained on the :doc:`Python +` doc page. Note that it is important that the stand-alone +LAMMPS executable and the LAMMPS shared library be consistent (built +from the same source code files) in order for this to work. If the two +have been built at different times using different source files, +problems may occur. Another limitation of calling back to Python from the LAMMPS module using the *python* command in a LAMMPS input is that both, the Python @@ -583,7 +767,8 @@ global variables will become invisible. Related commands """""""""""""""" -:doc:`shell `, :doc:`variable `, :doc:`fix python/invoke ` +:doc:`shell `, :doc:`variable `, +:doc:`fix python/invoke ` Default """"""" diff --git a/doc/src/run.rst b/doc/src/run.rst index c3dd171f6b..95e2f037db 100644 --- a/doc/src/run.rst +++ b/doc/src/run.rst @@ -103,14 +103,16 @@ must be done. .. note:: - If your input script changes the system between 2 runs, then the - initial setup must be performed to ensure the change is recognized by - all parts of the code that are affected. Examples are adding a - :doc:`fix ` or :doc:`dump ` or :doc:`compute `, changing - a :doc:`neighbor ` list parameter, or writing restart file - which can migrate atoms between processors. LAMMPS has no easy way to - check if this has happened, but it is an error to use the *pre no* - option in this case. + If your input script "changes" the system between 2 runs, then the + initial setup typically needs to be performed to ensure the change + is recognized by all parts of the code that are affected. Examples + are adding a :doc:`fix ` or :doc:`dump ` or + :doc:`compute `, changing a :doc:`neighbor ` + list parameter, using the :doc:`set ` command, or writing a + restart file via the :doc:`write_restart ` command, + which can migrate atoms between processors. LAMMPS has no easy way + to check if this has happened, but it is an error to use the *pre + no* option in these cases. If *post* is specified as "no", the full timing summary is skipped; only a one-line summary timing is printed. diff --git a/doc/src/set.rst b/doc/src/set.rst index 6be590e9b6..d2c865dfbb 100644 --- a/doc/src/set.rst +++ b/doc/src/set.rst @@ -22,21 +22,110 @@ Syntax for style = *region*, ID = a region ID * one or more keyword/value pairs may be appended -* keyword = *type* or *type/fraction* or *type/ratio* or *type/subset* - or *mol* or *x* or *y* or *z* or *vx* or *vy* or *vz* or *charge* or - *dipole* or *dipole/random* or *quat* or *spin/atom* or *spin/atom/random* or - *spin/electron* or *radius/electron* or - *quat* or *quat/random* or *diameter* or *shape* or *length* or *tri* or - *theta* or *theta/random* or *angmom* or *omega* or - *mass* or *density* or *density/disc* or *temperature* or - *volume* or *image* or *bond* or *angle* or *dihedral* or - *improper* or *sph/e* or *sph/cv* or *sph/rho* or - *smd/contact/radius* or *smd/mass/density* or *dpd/theta* or - *edpd/temp* or *edpd/cv* or *cc* or *epsilon* or - *i_name* or *d_name* or *i2_name* or *d2_name* + +* keyword = *angle* or *angmom* or *bond* or *cc* or *charge* or + *density* or *density/disc* or *diameter* or *dihedral* or *dipole* + or *dipole/random* or *dpd/theta* or *edpd/cv* or *edpd/temp* or + *epsilon* or *image* or *improper* or *length* or *mass* or *mol* or + *omega* or *quat* or *quat/random* or *radius/electron* or *shape* or + *smd/contact/radius* or *smd/mass/density* or *sph/cv* or *sph/e* or + *sph/rho* or *spin/atom* or *spin/atom/random* or *spin/electron* or + *temperature* or *theta* or *theta/random* or *tri* or *type* or + *type/fraction* or *type/ratio* or *type/subset* or *volume* or *vx* + or *vy* or *vz* or *x* or *y* or *z* or *i_name* or *d_name* or + *i2_name* or *d2_name* .. parsed-literal:: + *angle* value = numeric angle type or angle type label, for all angles between selected atoms + *angmom* values = Lx Ly Lz + Lx,Ly,Lz = components of angular momentum vector (distance-mass-velocity units) + any of Lx,Ly,Lz can be an atom-style variable (see below) + *bond* value = numeric bond type or bond type label, for all bonds between selected atoms + *cc* values = index cc + index = index of a chemical species (1 to Nspecies) + cc = chemical concentration of tDPD particles for a species (mole/volume units) + cc can be an atom-style variable (see below) + *charge* value = atomic charge (charge units) + value can be an atom-style variable (see below) + *density* value = particle density for a sphere or ellipsoid (mass/distance\^3 units), or for a triangle (mass/distance\^2 units) or line (mass/distance units) particle + value can be an atom-style variable (see below) + *density/disc* value = particle density for a 2d disc or ellipse (mass/distance\^2 units) + value can be an atom-style variable (see below) + *diameter* value = diameter of spherical particle (distance units) + value can be an atom-style variable (see below) + *dihedral* value = numeric dihedral type or dihedral type label, for all dihedrals between selected atoms + *dipole* values = x y z + x,y,z = orientation of dipole moment vector + any of x,y,z can be an atom-style variable (see below) + *dipole/random* values = seed Dlen + seed = random # seed (positive integer) for dipole moment orientations + Dlen = magnitude of dipole moment (dipole units) + *dpd/theta* value = internal temperature of DPD particles (temperature units) + value can be an atom-style variable (see below) + value can be NULL which sets internal temp of each particle to KE temp + *edpd/cv* value = volumetric heat capacity of eDPD particles (energy/temperature/volume units) + value can be an atom-style variable (see below) + *edpd/temp* value = temperature of eDPD particles (temperature units) + value can be an atom-style variable (see below) + *epsilon* value = dielectric constant of the medium where the atoms reside + value can be an atom-style variable (see below) + *image* values = nx ny nz + nx,ny,nz = which periodic image of the simulation box the atom is in + any of nx,ny,nz can be an atom-style variable (see below) + *improper* value = numeric improper type or improper type label, for all impropers between selected atoms + *length* value = len + len = length of line segment (distance units) + len can be an atom-style variable (see below) + *mass* value = per-atom mass (mass units) + value can be an atom-style variable (see below) + *mol* value = molecule ID + the moleclue ID can be an atom-style variable (see below) + *omega* values = Wx Wy Wz + Wx,Wy,Wz = components of angular velocity vector (radians/time units) + any of Wx,Wy,Wz can be an atom-style variable (see below) + *quat* values = a b c theta + a,b,c = unit vector to rotate particle around via right-hand rule + theta = rotation angle (degrees) + any of a,b,c,theta values can be an atom-style variable (see below) + *quat/random* value = seed + seed = random # seed (positive integer) for quaternion orientations + *radius/electron* value = eradius + eradius = electron radius (or fixed-core radius) (distance units) + value can be an atom-style variable (see below) + *shape* values = Sx Sy Sz + Sx,Sy,Sz = 3 diameters of ellipsoid (distance units) + any of Sx,Sy,Sz can be an atom-style variable (see below) + *smd/contact/radius* value = radius for short range interactions, i.e. contact and friction + value can be an atom-style variable (see below) + *smd/mass/density* value = set particle mass based on volume by providing a mass density + value can be an atom-style variable (see below) + *sph/cv* value = heat capacity of SPH particles (need units) + value can be an atom-style variable (see below) + *sph/e* value = energy of SPH particles (need units) + value can be an atom-style variable (see below) + *sph/rho* value = density of SPH particles (need units) + value can be an atom-style variable (see below) + *spin/atom* values = g x y z + g = magnitude of magnetic spin vector (in Bohr magneton's unit) + x,y,z = orientation of magnetic spin vector + any of x,y,z can be an atom-style variable (see below) + *spin/atom/random* values = seed Dlen + seed = random # seed (positive integer) for magnetic spin orientations + Dlen = magnitude of magnetic spin vector (in Bohr magneton's unit) + *spin/electron* value = espin + espin = electron spin (+1/-1), 0 = nuclei, 2 = fixed-core, 3 = pseudo-cores (i.e. ECP) + value can be an atom-style variable (see below) + *temperature* value = temperature for finite-size particles (temperature units) + value can be an atom-style variable (see below) + *theta* value = angle (degrees) + angle = orientation of line segment with respect to x-axis + value can be an atom-style variable (see below) + *theta/random* value = seed + seed = random # seed (positive integer) for line segment orienations + *tri* value = side + side = side length of equilateral triangle (distance units) + value can be an atom-style variable (see below) *type* value = numeric atom type or type label value can be an atom-style variable (see below) *type/fraction* values = type fraction seed @@ -51,104 +140,22 @@ Syntax type = numeric atom type or type label Nsubset = exact number of selected atoms to set to new atom type seed = random # seed (positive integer) - *mol* value = molecule ID - value can be an atom-style variable (see below) - *x*,\ *y*,\ *z* value = atom coordinate (distance units) + *volume* value = particle volume for Peridynamic particle (distance\^3 units) value can be an atom-style variable (see below) *vx*,\ *vy*,\ *vz* value = atom velocity (velocity units) value can be an atom-style variable (see below) - *charge* value = atomic charge (charge units) + *x*,\ *y*,\ *z* value = atom coordinate (distance units) value can be an atom-style variable (see below) - *dipole* values = x y z - x,y,z = orientation of dipole moment vector - any of x,y,z can be an atom-style variable (see below) - *dipole/random* value = seed Dlen - seed = random # seed (positive integer) for dipole moment orientations - Dlen = magnitude of dipole moment (dipole units) - *spin/atom* values = g x y z - g = magnitude of magnetic spin vector (in Bohr magneton's unit) - x,y,z = orientation of magnetic spin vector - any of x,y,z can be an atom-style variable (see below) - *spin/atom/random* value = seed Dlen - seed = random # seed (positive integer) for magnetic spin orientations - Dlen = magnitude of magnetic spin vector (in Bohr magneton's unit) - *radius/electron* values = eradius - eradius = electron radius (or fixed-core radius) (distance units) - *spin/electron* value = espin - espin = electron spin (+1/-1), 0 = nuclei, 2 = fixed-core, 3 = pseudo-cores (i.e. ECP) - *quat* values = a b c theta - a,b,c = unit vector to rotate particle around via right-hand rule - theta = rotation angle (degrees) - any of a,b,c,theta can be an atom-style variable (see below) - *quat/random* value = seed - seed = random # seed (positive integer) for quaternion orientations - *diameter* value = diameter of spherical particle (distance units) - value can be an atom-style variable (see below) - *shape* value = Sx Sy Sz - Sx,Sy,Sz = 3 diameters of ellipsoid (distance units) - *length* value = len - len = length of line segment (distance units) - len can be an atom-style variable (see below) - *tri* value = side - side = side length of equilateral triangle (distance units) - side can be an atom-style variable (see below) - *theta* value = angle (degrees) - angle = orientation of line segment with respect to x-axis - angle can be an atom-style variable (see below) - *theta/random* value = seed - seed = random # seed (positive integer) for line segment orienations - *angmom* values = Lx Ly Lz - Lx,Ly,Lz = components of angular momentum vector (distance-mass-velocity units) - any of Lx,Ly,Lz can be an atom-style variable (see below) - *omega* values = Wx Wy Wz - Wx,Wy,Wz = components of angular velocity vector (radians/time units) - any of wx,wy,wz can be an atom-style variable (see below) - *mass* value = per-atom mass (mass units) - value can be an atom-style variable (see below) - *density* value = particle density for a sphere or ellipsoid (mass/distance\^3 units), or for a triangle (mass/distance\^2 units) or line (mass/distance units) particle - value can be an atom-style variable (see below) - *density/disc* value = particle density for a 2d disc or ellipse (mass/distance\^2 units) - value can be an atom-style variable (see below) - *temperature* value = temperature for finite-size particles (temperature units) - value can be an atom-style variable (see below) - *volume* value = particle volume for Peridynamic particle (distance\^3 units) - value can be an atom-style variable (see below) - *image* nx ny nz - nx,ny,nz = which periodic image of the simulation box the atom is in - any of nx,ny,nz can be an atom-style variable (see below) - *bond* value = numeric bond type or bond type label, for all bonds between selected atoms - *angle* value = numeric angle type or angle type label, for all angles between selected atoms - *dihedral* value = numeric dihedral type or dihedral type label, for all dihedrals between selected atoms - *improper* value = numeric improper type or improper type label, for all impropers between selected atoms - *rheo/rho* value = density of RHEO particles (mass/distance\^3) - *rheo/status* value = status or phase of RHEO particles (unitless) - *sph/e* value = energy of SPH particles (need units) - value can be an atom-style variable (see below) - *sph/cv* value = heat capacity of SPH particles (need units) - value can be an atom-style variable (see below) - *sph/rho* value = density of SPH particles (need units) - value can be an atom-style variable (see below) - *smd/contact/radius* = radius for short range interactions, i.e. contact and friction - value can be an atom-style variable (see below) - *smd/mass/density* = set particle mass based on volume by providing a mass density - value can be an atom-style variable (see below) - *dpd/theta* value = internal temperature of DPD particles (temperature units) - value can be an atom-style variable (see below) - value can be NULL which sets internal temp of each particle to KE temp - *edpd/temp* value = temperature of eDPD particles (temperature units) - value can be an atom-style variable (see below) - *edpd/cv* value = volumetric heat capacity of eDPD particles (energy/temperature/volume units) - value can be an atom-style variable (see below) - *cc* values = index cc - index = index of a chemical species (1 to Nspecies) - cc = chemical concentration of tDPD particles for a species (mole/volume units) - *epsilon* value = dielectric constant of the medium where the atoms reside *i_name* value = custom integer vector with name + value can be an atom-style variable (see below) *d_name* value = custom floating-point vector with name - *i2_name* value = column of a custom integer array with name + value can be an atom-style variable (see below) + *i2_name* value = custom integer array with name column specified as i2_name[N] where N is 1 to Ncol - *d2_name* value = column of a custom floating-point array with name + value can be an atom-style variable (see below) + *d2_name* value = custom floating-point array with name column specified as d2_name[N] where N is 1 to Ncol + value can be an atom-style variable (see below) Examples """""""" @@ -177,22 +184,26 @@ Description Set one or more properties of one or more atoms. Since atom properties are initially assigned by the :doc:`read_data `, -:doc:`read_restart ` or :doc:`create_atoms ` -commands, this command changes those assignments. This can be useful -for overriding the default values assigned by the -:doc:`create_atoms ` command (e.g. charge = 0.0). It can -be useful for altering pairwise and molecular force interactions, +:doc:`read_restart ` or :doc:`create_atoms +` commands, this command changes those assignments. +This can be useful for overriding the default values assigned by the +:doc:`create_atoms ` command (e.g. charge = 0.0). It +can be useful for altering pairwise and molecular force interactions, since force-field coefficients are defined in terms of types. It can be used to change the labeling of atoms by atom type or molecule ID -when they are output in :doc:`dump ` files. It can also be useful -for debugging purposes; i.e. positioning an atom at a precise location -to compute subsequent forces or energy. +when they are output in :doc:`dump ` files. It can also be +useful for debugging purposes; i.e. positioning an atom at a precise +location to compute subsequent forces or energy. Note that the *style* and *ID* arguments determine which atoms have their properties reset. The remaining keywords specify which properties to reset and what the new values are. Some strings like *type* or *mol* can be used as a style and/or a keyword. +The :doc:`fix set ` command can be used with similar syntax +to this command to reset atom properties once every *N* steps during a +simulation using via atom-style variables. + ---------- This section describes how to select which atoms to change @@ -211,8 +222,8 @@ can be specified, e.g. "C". The style *mol* selects all the atoms in a range of molecule IDs. In each of the range cases, the range can be specified as a single -numeric value, or a wildcard asterisk can be used to specify a range -of values. This takes the form "\*" or "\*n" or "n\*" or "m\*n". For +numeric value, or with a wildcard asterisk to specify a range of +values. This takes the form "\*" or "\*n" or "n\*" or "m\*n". For example, for the style *type*, if N = the number of atom types, then an asterisk with no numeric values means all types from 1 to N. A leading asterisk means all types from 1 to n (inclusive). A trailing @@ -222,25 +233,25 @@ means all types from m to n (inclusive). For all the styles except The style *group* selects all the atoms in the specified group. The style *region* selects all the atoms in the specified geometric -region. See the :doc:`group ` and :doc:`region ` commands -for details of how to specify a group or region. +region. See the :doc:`group ` and :doc:`region ` +commands for details of how to specify a group or region. ---------- -This section describes the keyword options for which properties to +The next section describes the keyword options for which properties to change, for the selected atoms. Note that except where explicitly prohibited below, all of the keywords allow an :doc:`atom-style or atomfile-style variable ` to be used as the specified value(s). If the value is a -variable, it should be specified as v_name, where name is the -variable name. In this case, the variable will be evaluated, and its -resulting per-atom value used to determine the value assigned to each -selected atom. Note that the per-atom value from the variable will be -ignored for atoms that are not selected via the *style* and *ID* -settings explained above. A simple way to use per-atom values from -the variable to reset a property for all atoms is to use style *atom* -with *ID* = "\*"; this selects all atom IDs. +variable, it should be specified as v_name, where name is the variable +name. In this case, the variable will be evaluated, and its resulting +per-atom value used to determine the value assigned to each selected +atom. Note that the per-atom value from the variable will be ignored +for atoms that are not selected via the *style* and *ID* settings +explained above. A simple way to use per-atom values from the +variable to reset a property for all atoms is to use style *atom* with +*ID* = "\*"; this selects all atom IDs. Atom-style variables can specify formulas with various mathematical functions, and include :doc:`thermo_style ` command @@ -256,52 +267,110 @@ from a file. .. note:: Atom-style and atomfile-style variables return floating point - per-atom values. If the values are assigned to an integer variable, - such as the molecule ID, then the floating point value is truncated to - its integer portion, e.g. a value of 2.6 would become 2. + per-atom values. If the values are assigned to an integer + variable, such as the molecule ID, then the floating point value is + truncated to its integer portion, e.g. a value of 2.6 would + become 2. + +---------- .. versionchanged:: 28Mar2023 - Support for type labels was added for setting atom, bond, angle, - dihedral, and improper types + Support for type labels was added for setting angle types -Keyword *type* sets the atom type for all selected atoms. A specified -value can be either a numeric atom type or an atom type label. When -using a numeric type, the specified value must be from 1 to ntypes, -where ntypes was set by the :doc:`create_box ` command or -the *atom types* field in the header of the data file read by the -:doc:`read_data ` command. When using a type label it must -have been defined previously. See the :doc:`Howto type labels -` doc page for the allowed syntax of type labels -and a general discussion of how type labels can be used. +Keyword *angle* sets the angle type of all angles of selected atoms to +the specified value. The value can be a numeric type from 1 to +nangletypes. Or it can be a angle type label. See the :doc:`Howto +type labels ` doc page for the allowed syntax of +type labels and a general discussion of how type labels can be used. +All atoms in a particular angle must be selected atoms in order for +the change to be made. The value of nangletypes was set by the *angle +types* field in the header of the data file read by the +:doc:`read_data ` command. This keyword does NOT allow use +of an atom-style variable. -Keyword *type/fraction* sets the atom type for a fraction of the selected -atoms. The actual number of atoms changed is not guaranteed -to be exactly the specified fraction (0 <= *fraction* <= 1), but -should be statistically close. Random numbers are used in such a way -that a particular atom is changed or not changed, regardless of how -many processors are being used. This keyword does not allow use of an -atom-style variable. +Keyword *angmom* sets the angular momentum of selected atoms. The +particles must be ellipsoids as defined by the :doc:`atom_style +ellipsoid ` command or triangles as defined by the +:doc:`atom_style tri ` command. The angular momentum +vector of the particles is set to the 3 specified components. -Keywords *type/ratio* and *type/subset* also set the atom type for a -fraction of the selected atoms. The actual number of atoms changed -will be exactly the requested number. For *type/ratio* the specified -fraction (0 <= *fraction* <= 1) determines the number. For -*type/subset*, the specified *Nsubset* is the number. An iterative -algorithm is used which ensures the correct number of atoms are -selected, in a perfectly random fashion. Which atoms are selected -will change with the number of processors used. These keywords do not -allow use of an atom-style variable. +.. versionchanged:: 28Mar2023 -Keyword *mol* sets the molecule ID for all selected atoms. The -:doc:`atom style ` being used must support the use of -molecule IDs. + Support for type labels was added for setting bond types -Keywords *x*, *y*, *z*, and *charge* set the coordinates or -charge of all selected atoms. For *charge*, the :doc:`atom style -` being used must support the use of atomic -charge. Keywords *vx*, *vy*, and *vz* set the velocities of all -selected atoms. +Keyword *bond* sets the bond type of all bonds of selected atoms to +the specified value. The value can be a numeric type from 1 to +nbondtypes. Or it can be a bond type label. See the :doc:`Howto type +labels ` doc page for the allowed syntax of type +labels and a general discussion of how type labels can be used. All +atoms in a particular bond must be selected atoms in order for the +change to be made. The value of nbondtypes was set by the *bond +types* field in the header of the data file read by the +:doc:`read_data ` command. This keyword does NOT allow use +of an atom-style variable. + +Keyword *cc* sets the chemical concentration of a tDPD particle for a +specified species as defined by the DPD-MESO package. Currently, only +:doc:`atom_style tdpd ` defines particles with this +attribute. An integer for "index" selects a chemical species (1 to +Nspecies) where Nspecies is set by the atom_style command. The value +for the chemical concentration must be >= 0.0. + +Keyword *charge* set the charge of all selected atoms. The :doc:`atom +style ` being used must support the use of atomic charge. + +Keyword *density* or *density/disc* also sets the mass of all selected +particles, but in a different way. The particles must have a per-atom +mass attribute, as defined by the :doc:`atom_style ` +command. If the atom has a radius attribute (see :doc:`atom_style +sphere `) and its radius is non-zero, its mass is set from +the density and particle volume for 3d systems (the input density is +assumed to be in mass/distance\^3 units). For 2d, the default is for +LAMMPS to model particles with a radius attribute as spheres. +However, if the *density/disc* keyword is used, then they can be +modeled as 2d discs (circles). Their mass is set from the density and +particle area (the input density is assumed to be in mass/distance\^2 +units). + +If the atom has a shape attribute (see :doc:`atom_style ellipsoid +`) and its 3 shape parameters are non-zero, then its mass +is set from the density and particle volume (the input density is +assumed to be in mass/distance\^3 units). The *density/disc* keyword +has no effect; it does not (yet) treat 3d ellipsoids as 2d ellipses. + +If the atom has a length attribute (see :doc:`atom_style line +`) and its length is non-zero, then its mass is set from +the density and line segment length (the input density is assumed to +be in mass/distance units). If the atom has an area attribute (see +:doc:`atom_style tri `) and its area is non-zero, then its +mass is set from the density and triangle area (the input density is +assumed to be in mass/distance\^2 units). + +If none of these cases are valid, then the mass is set to the density +value directly (the input density is assumed to be in mass units). + +Keyword *diameter* sets the size of the selected atoms. The particles +must be finite-size spheres as defined by the :doc:`atom_style sphere +` command. The diameter of a particle can be set to 0.0, +which means they will be treated as point particles. Note that this +command does not adjust the particle mass, even if it was defined with +a density, e.g. via the :doc:`read_data ` command. + +.. versionchanged:: 28Mar2023 + + Support for type labels was added for setting dihedral types + +Keyword *dihedral* sets the dihedral type of all dihedrals of selected +atoms to the specified value. The value can be a numeric type from 1 +to ndihedraltypes. Or it can be a dihedral type label. See the +:doc:`Howto type labels ` doc page for the allowed +syntax of type labels and a general discussion of how type labels can +be used. All atoms in a particular dihedral must be selected atoms in +order for the change to be made. The value of ndihedraltypes was set +by the *dihedral types* field in the header of the data file read by +the :doc:`read_data ` command. This keyword does NOT allow +use of an atom-style variable. Keyword *dipole* uses the specified x,y,z values as components of a vector to set as the orientation of the dipole moment vectors of the @@ -313,40 +382,106 @@ moment vectors for the selected atoms and sets the magnitude of each to the specified *Dlen* value. For 2d systems, the z component of the orientation is set to 0.0. Random numbers are used in such a way that the orientation of a particular atom is the same, regardless of how -many processors are being used. This keyword does not allow use of an +many processors are being used. This keyword does NOT allow use of an atom-style variable. -.. versionchanged:: 15Sep2022 +Keyword *dpd/theta* sets the internal temperature of a DPD particle as +defined by the DPD-REACT package. If the specified value is a number +it must be >= 0.0. If the specified value is NULL, then the kinetic +temperature Tkin of each particle is computed as 3/2 k Tkin = KE = 1/2 +m v\^2 = 1/2 m (vx\*vx+vy\*vy+vz\*vz). Each particle's internal +temperature is set to Tkin. If the specified value is an atom-style +variable, then the variable is evaluated for each particle. If a +value >= 0.0, the internal temperature is set to that value. If it is +< 0.0, the computation of Tkin is performed and the internal +temperature is set to that value. -Keyword *spin/atom* uses the specified g value to set the magnitude of the -magnetic spin vectors, and the x,y,z values as components of a vector -to set as the orientation of the magnetic spin vectors of the selected -atoms. This keyword was previously called *spin*. +Keywords *edpd/temp* and *edpd/cv* set the temperature and volumetric +heat capacity of an eDPD particle as defined by the DPD-MESO package. +Currently, only :doc:`atom_style edpd ` defines particles +with these attributes. The values for the temperature and heat +capacity must be positive. -.. versionchanged:: 15Sep2022 +Keyword *epsilon* sets the dielectric constant of a particle to be +that of the medium where the particle resides as defined by the +DIELECTRIC package. Currently, only :doc:`atom_style dielectric +` defines particles with this attribute. The value for the +dielectric constant must be >= 0.0. Note that the set command with +this keyword will rescale the particle charge accordingly so that the +real charge (e.g., as read from a data file) stays intact. To change +the real charges, one needs to use the set command with the *charge* +keyword. Care must be taken to ensure that the real and scaled charges +and the dielectric constants are consistent. -Keyword *spin/atom/random* randomizes the orientation of the magnetic spin -vectors for the selected atoms and sets the magnitude of each to the -specified *Dlen* value. This keyword was previously called *spin/random*. +Keyword *image* sets which image of the simulation box the atom is +considered to be in. An image of 0 means it is inside the box as +defined. A value of 2 means add 2 box lengths to get the true value. +A value of -1 means subtract 1 box length to get the true value. +LAMMPS updates these flags as atoms cross periodic boundaries during +the simulation. The flags can be output with atom snapshots via the +:doc:`dump ` command. If a value of NULL is specified for any +of nx,ny,nz, then the current image value for that dimension is +unchanged. For non-periodic dimensions only a value of 0 can be +specified. This command can be useful after a system has been +equilibrated and atoms have diffused one or more box lengths in +various directions. This command can then reset the image values for +atoms so that they are effectively inside the simulation box, e.g if a +diffusion coefficient is about to be measured via the :doc:`compute +msd ` command. Care should be taken not to reset the +image flags of two atoms in a bond to the same value if the bond +straddles a periodic boundary (rather they should be different by +/- +1). This will not affect the dynamics of a simulation, but may mess +up analysis of the trajectories if a LAMMPS diagnostic or your own +analysis relies on the image flags to unwrap a molecule which +straddles the periodic box. -.. versionadded:: 15Sep2022 +.. versionchanged:: 28Mar2023 -Keyword *radius/electron* uses the specified value to set the radius of -electrons or fixed cores. + Support for type labels was added for setting improper types -.. versionadded:: 15Sep2022 +Keyword *improper* sets the improper type of all impropers of selected +atoms to the specified value. The value can be a numeric type from 1 +to nimpropertypes. Or it can be a improper type label. See the +:doc:`Howto type labels ` doc page for the allowed +syntax of type labels and a general discussion of how type labels can +be used. All atoms in a particular improper must be selected atoms in +order for the change to be made. The value of nimpropertypes was set +by the *improper types* field in the header of the data file read by +the :doc:`read_data ` command. This keyword does NOT allow +use of an atom-style variable. -Keyword *spin/electron* sets the spin of an electron (+/- 1) or indicates -nuclei (=0), fixed-cores (=2), or pseudo-cores (= 3). +Keyword *length* sets the length of selected atoms. The particles +must be line segments as defined by the :doc:`atom_style line +` command. If the specified value is non-zero the line +segment is (re)set to a length = the specified value, centered around +the particle position, with an orientation along the x-axis. If the +specified value is 0.0, the particle will become a point particle. +Note that this command does not adjust the particle mass, even if it +was defined with a density, e.g. via the :doc:`read_data ` +command. + +Keyword *mass* sets the mass of all selected particles. The particles +must have a per-atom mass attribute, as defined by the +:doc:`atom_style ` command. See the "mass" command for +how to set mass values on a per-type basis. + +Keyword *mol* sets the molecule ID for all selected atoms. The +:doc:`atom style ` being used must support the use of +molecule IDs. + +Keyword *omega* sets the angular velocity of selected atoms. The +particles must be spheres as defined by the :doc:`atom_style sphere +` command. The angular velocity vector of the particles +is set to the 3 specified components. Keyword *quat* uses the specified values to create a quaternion (4-vector) that represents the orientation of the selected atoms. The particles must define a quaternion for their orientation (e.g. ellipsoids, triangles, body particles) as defined by the -:doc:`atom_style ` command. Note that particles defined by -:doc:`atom_style ellipsoid ` have 3 shape parameters. The 3 -values must be non-zero for each particle set by this command. They -are used to specify the aspect ratios of an ellipsoidal particle, +:doc:`atom_style ` command. Note that particles defined +by :doc:`atom_style ellipsoid ` have 3 shape parameters. +The 3 values must be non-zero for each particle set by this command. +They are used to specify the aspect ratios of an ellipsoidal particle, which is oriented by default with its x-axis along the simulation box's x-axis, and similarly for y and z. If this body is rotated (via the right-hand rule) by an angle theta around a unit rotation vector @@ -360,51 +495,77 @@ ignored, since a rotation vector of (0,0,1) is the only valid choice. Keyword *quat/random* randomizes the orientation of the quaternion for the selected atoms. The particles must define a quaternion for their orientation (e.g. ellipsoids, triangles, body particles) as defined by -the :doc:`atom_style ` command. Random numbers are used in -such a way that the orientation of a particular atom is the same, +the :doc:`atom_style ` command. Random numbers are used +in such a way that the orientation of a particular atom is the same, regardless of how many processors are being used. For 2d systems, only orientations in the xy plane are generated. As with keyword *quat*, for ellipsoidal particles, the 3 shape values must be non-zero -for each particle set by this command. This keyword does not allow +for each particle set by this command. This keyword does NOT allow use of an atom-style variable. -Keyword *diameter* sets the size of the selected atoms. The particles -must be finite-size spheres as defined by the :doc:`atom_style sphere -` command. The diameter of a particle can be set to 0.0, -which means they will be treated as point particles. Note that this -command does not adjust the particle mass, even if it was defined with -a density, e.g. via the :doc:`read_data ` command. +.. versionadded:: 15Sep2022 + +Keyword *radius/electron* uses the specified value to set the radius +of electrons or fixed cores. Keyword *shape* sets the size and shape of the selected atoms. The particles must be ellipsoids as defined by the :doc:`atom_style -ellipsoid ` command. The *Sx*, *Sy*, *Sz* settings -are the 3 diameters of the ellipsoid in each direction. All 3 can be -set to the same value, which means the ellipsoid is effectively a -sphere. They can also all be set to 0.0 which means the particle will -be treated as a point particle. Note that this command does not -adjust the particle mass, even if it was defined with a density, -e.g. via the :doc:`read_data ` command. +ellipsoid ` command. The *Sx*, *Sy*, *Sz* settings are +the 3 diameters of the ellipsoid in each direction. All 3 can be set +to the same value, which means the ellipsoid is effectively a sphere. +They can also all be set to 0.0 which means the particle will be +treated as a point particle. Note that this command does not adjust +the particle mass, even if it was defined with a density, e.g. via the +:doc:`read_data ` command. -Keyword *length* sets the length of selected atoms. The particles -must be line segments as defined by the :doc:`atom_style line -` command. If the specified value is non-zero the line -segment is (re)set to a length = the specified value, centered around -the particle position, with an orientation along the x-axis. If the -specified value is 0.0, the particle will become a point particle. -Note that this command does not adjust the particle mass, even if it -was defined with a density, e.g. via the :doc:`read_data ` -command. +Keyword *smd/contact/radius* only applies to simulations with the +Smooth Mach Dynamics package MACHDYN. Itsets an interaction radius +for computing short-range interactions, e.g. repulsive forces to +prevent different individual physical bodies from penetrating each +other. Note that the SPH smoothing kernel diameter used for computing +long range, nonlocal interactions, is set using the *diameter* +keyword. -Keyword *tri* sets the size of selected atoms. The particles must be -triangles as defined by the :doc:`atom_style tri ` command. -If the specified value is non-zero the triangle is (re)set to be an -equilateral triangle in the xy plane with side length = the specified -value, with a centroid at the particle position, with its base -parallel to the x axis, and the y-axis running from the center of the -base to the top point of the triangle. If the specified value is 0.0, -the particle will become a point particle. Note that this command -does not adjust the particle mass, even if it was defined with a -density, e.g. via the :doc:`read_data ` command. +Keyword *smd/mass/density* sets the mass of all selected particles, +but it is only applicable to the Smooth Mach Dynamics package MACHDYN. +It assumes that the particle volume has already been correctly set and +calculates particle mass from the provided mass density value. + +Keywords *sph/cv*, *sph/e*, and *sph/rho* set the heat capacity, +energy, and density of smoothed particle hydrodynamics (SPH) +particles. See `this PDF guide `_ to +using SPH in LAMMPS. + +.. note:: + + Note that the SPH PDF guide file has not been updated for many + years and thus does not reflect the current *syntax* of the SPH + package commands. For that, please refer to the LAMMPS manual. + +.. versionchanged:: 15Sep2022 + +Keyword *spin/atom* uses the specified g value to set the magnitude of +the magnetic spin vectors, and the x,y,z values as components of a +vector to set as the orientation of the magnetic spin vectors of the +selected atoms. This keyword was previously called *spin*. + +.. versionchanged:: 15Sep2022 + +Keyword *spin/atom/random* randomizes the orientation of the magnetic +spin vectors for the selected atoms and sets the magnitude of each to +the specified *Dlen* value. This keyword does NOT allow use of an +atom-style variable. This keyword was previously called +*spin/random*. + +.. versionadded:: 15Sep2022 + +Keyword *spin/electron* sets the spin of an electron (+/- 1) or +indicates nuclei (=0), fixed-cores (=2), or pseudo-cores (= 3). + +Keyword *temperature* sets the temperature of a finite-size particle. +Currently, only the GRANULAR package supports this attribute. The +temperature must be added using an instance of :doc:`fix property/atom +` The values for the temperature must be positive. Keyword *theta* sets the orientation of selected atoms. The particles must be line segments as defined by the :doc:`atom_style line @@ -413,169 +574,71 @@ orientation angle of the line segments with respect to the x axis. Keyword *theta/random* randomizes the orientation of theta for the selected atoms. The particles must be line segments as defined by the -:doc:`atom_style line ` command. Random numbers are used in -such a way that the orientation of a particular atom is the same, +:doc:`atom_style line ` command. Random numbers are used +in such a way that the orientation of a particular atom is the same, regardless of how many processors are being used. This keyword does -not allow use of an atom-style variable. +NOT allow use of an atom-style variable. -Keyword *angmom* sets the angular momentum of selected atoms. The -particles must be ellipsoids as defined by the :doc:`atom_style -ellipsoid ` command or triangles as defined by the -:doc:`atom_style tri ` command. The angular momentum -vector of the particles is set to the 3 specified components. +Keyword *tri* sets the size of selected atoms. The particles must be +triangles as defined by the :doc:`atom_style tri ` +command. If the specified value is non-zero the triangle is (re)set +to be an equilateral triangle in the xy plane with side length = the +specified value, with a centroid at the particle position, with its +base parallel to the x axis, and the y-axis running from the center of +the base to the top point of the triangle. If the specified value is +0.0, the particle will become a point particle. Note that this +command does not adjust the particle mass, even if it was defined with +a density, e.g. via the :doc:`read_data ` command. -Keyword *omega* sets the angular velocity of selected atoms. The -particles must be spheres as defined by the :doc:`atom_style sphere -` command. The angular velocity vector of the particles is -set to the 3 specified components. +.. versionchanged:: 28Mar2023 -Keyword *mass* sets the mass of all selected particles. The particles -must have a per-atom mass attribute, as defined by the :doc:`atom_style -` command. See the "mass" command for how to set mass -values on a per-type basis. + Support for type labels was added for setting atom types -Keyword *density* or *density/disc* also sets the mass of all selected -particles, but in a different way. The particles must have a per-atom -mass attribute, as defined by the :doc:`atom_style ` -command. If the atom has a radius attribute (see :doc:`atom_style -sphere `) and its radius is non-zero, its mass is set from -the density and particle volume for 3d systems (the input density is -assumed to be in mass/distance\^3 units). For 2d, the default is for -LAMMPS to model particles with a radius attribute as spheres. However, -if the *density/disc* keyword is used, then they can be modeled as 2d -discs (circles). Their mass is set from the density and particle area -(the input density is assumed to be in mass/distance\^2 units). - -If the atom has a shape attribute (see :doc:`atom_style ellipsoid -`) and its 3 shape parameters are non-zero, then its mass is -set from the density and particle volume (the input density is assumed -to be in mass/distance\^3 units). The *density/disc* keyword has no -effect; it does not (yet) treat 3d ellipsoids as 2d ellipses. - -If the atom has a length attribute (see :doc:`atom_style line -`) and its length is non-zero, then its mass is set from the -density and line segment length (the input density is assumed to be in -mass/distance units). If the atom has an area attribute (see -:doc:`atom_style tri `) and its area is non-zero, then its -mass is set from the density and triangle area (the input density is -assumed to be in mass/distance\^2 units). - -If none of these cases are valid, then the mass is set to the density -value directly (the input density is assumed to be in mass units). - -Keyword *temperature* sets the temperature of a finite-size particle. -Currently, only the GRANULAR package supports this attribute. The -temperature must be added using an instance of -:doc:`fix property/atom ` The values for the -temperature must be positive. - -Keyword *volume* sets the volume of all selected particles. Currently, -only the :doc:`atom_style peri ` command defines particles -with a volume attribute. Note that this command does not adjust the -particle mass. - -Keyword *image* sets which image of the simulation box the atom is -considered to be in. An image of 0 means it is inside the box as -defined. A value of 2 means add 2 box lengths to get the true value. A -value of -1 means subtract 1 box length to get the true value. LAMMPS -updates these flags as atoms cross periodic boundaries during the -simulation. The flags can be output with atom snapshots via the -:doc:`dump ` command. If a value of NULL is specified for any of -nx,ny,nz, then the current image value for that dimension is unchanged. -For non-periodic dimensions only a value of 0 can be specified. This -command can be useful after a system has been equilibrated and atoms -have diffused one or more box lengths in various directions. This -command can then reset the image values for atoms so that they are -effectively inside the simulation box, e.g if a diffusion coefficient is -about to be measured via the :doc:`compute msd ` command. -Care should be taken not to reset the image flags of two atoms in a bond -to the same value if the bond straddles a periodic boundary (rather they -should be different by +/- 1). This will not affect the dynamics of a -simulation, but may mess up analysis of the trajectories if a LAMMPS -diagnostic or your own analysis relies on the image flags to unwrap a -molecule which straddles the periodic box. - -Keywords *bond*, *angle*, *dihedral*, and *improper*, set the bond -type (angle type, etc) of all bonds (angles, etc) of selected atoms to -the specified value. The value can be a numeric type from 1 to -nbondtypes (nangletypes, etc). Or it can be a type label (bond type -label, angle type label, etc). See the :doc:`Howto type labels +Keyword *type* sets the atom type for all selected atoms. A specified +value can be either a numeric atom type or an atom type label. When +using a numeric type, the specified value must be from 1 to ntypes, +where ntypes was set by the :doc:`create_box ` command or +the *atom types* field in the header of the data file read by the +:doc:`read_data ` command. When using a type label it must +have been defined previously. See the :doc:`Howto type labels ` doc page for the allowed syntax of type labels -and a general discussion of how type labels can be used. All atoms in -a particular bond (angle, etc) must be selected atoms in order for the -change to be made. The value of nbondtypes (nangletypes, etc) was set -by the *bond types* (\ *angle types*, etc) field in the header of the -data file read by the :doc:`read_data ` command. These -keywords do not allow use of an atom-style variable. +and a general discussion of how type labels can be used. -Keywords *rheo/rho* and *rheo/status* set the density and the status of -rheo particles. In particular, one can only set the phase in the status -as described by the :doc:`RHEO howto page `. +Keyword *type/fraction* sets the atom type for a fraction of the +selected atoms. The actual number of atoms changed is not guaranteed +to be exactly the specified fraction (0 <= *fraction* <= 1), but +should be statistically close. Random numbers are used in such a way +that a particular atom is changed or not changed, regardless of how +many processors are being used. This keyword does NOT allow use of an +atom-style variable. -Keywords *sph/e*, *sph/cv*, and *sph/rho* set the energy, heat capacity, -and density of smoothed particle hydrodynamics (SPH) particles. See -`this PDF guide `_ to using SPH in LAMMPS. +Keywords *type/ratio* and *type/subset* also set the atom type for a +fraction of the selected atoms. The actual number of atoms changed +will be exactly the requested number. For *type/ratio* the specified +fraction (0 <= *fraction* <= 1) determines the number. For +*type/subset*, the specified *Nsubset* is the number. An iterative +algorithm is used which ensures the correct number of atoms are +selected, in a perfectly random fashion. Which atoms are selected +will change with the number of processors used. These keywords do not +allow use of an atom-style variable. -.. note:: +Keyword *volume* sets the volume of all selected particles. +Currently, only the :doc:`atom_style peri ` command +defines particles with a volume attribute. Note that this command +does not adjust the particle mass. - Please note that the SPH PDF guide file has not been updated for - many years and thus does not reflect the current *syntax* of the - SPH package commands. For that please refer to the LAMMPS manual. +Keywords *vx*, *vy*, and *vz* set the velocities of all selected +atoms. -Keyword *smd/mass/density* sets the mass of all selected particles, but -it is only applicable to the Smooth Mach Dynamics package MACHDYN. It -assumes that the particle volume has already been correctly set and -calculates particle mass from the provided mass density value. - -Keyword *smd/contact/radius* only applies to simulations with the Smooth -Mach Dynamics package MACHDYN. Itsets an interaction radius for -computing short-range interactions, e.g. repulsive forces to prevent -different individual physical bodies from penetrating each other. Note -that the SPH smoothing kernel diameter used for computing long range, -nonlocal interactions, is set using the *diameter* keyword. - -Keyword *dpd/theta* sets the internal temperature of a DPD particle as -defined by the DPD-REACT package. If the specified value is a number it -must be >= 0.0. If the specified value is NULL, then the kinetic -temperature Tkin of each particle is computed as 3/2 k Tkin = KE = 1/2 m -v\^2 = 1/2 m (vx\*vx+vy\*vy+vz\*vz). Each particle's internal -temperature is set to Tkin. If the specified value is an atom-style -variable, then the variable is evaluated for each particle. If a value ->= 0.0, the internal temperature is set to that value. If it is < 0.0, -the computation of Tkin is performed and the internal temperature is set -to that value. - -Keywords *edpd/temp* and *edpd/cv* set the temperature and volumetric -heat capacity of an eDPD particle as defined by the DPD-MESO package. -Currently, only :doc:`atom_style edpd ` defines particles -with these attributes. The values for the temperature and heat capacity -must be positive. - -Keyword *cc* sets the chemical concentration of a tDPD particle for a -specified species as defined by the DPD-MESO package. Currently, only -:doc:`atom_style tdpd ` defines particles with this -attribute. An integer for "index" selects a chemical species (1 to -Nspecies) where Nspecies is set by the atom_style command. The value for -the chemical concentration must be >= 0.0. - -Keyword *epsilon* sets the dielectric constant of a particle, precisely -of the medium where the particle resides as defined by the DIELECTRIC -package. Currently, only :doc:`atom_style dielectric ` -defines particles with this attribute. The value for the dielectric -constant must be >= 0.0. Note that the set command with this keyword -will rescale the particle charge accordingly so that the real charge -(e.g., as read from a data file) stays intact. To change the real -charges, one needs to use the set command with the *charge* -keyword. Care must be taken to ensure that the real and scaled charges, -and dielectric constants are consistent. +Keywords *x*, *y*, *z* set the coordinates of all selected atoms. Keywords *i_name*, *d_name*, *i2_name*, *d2_name* refer to custom per-atom integer and floating-point vectors or arrays that have been added via the :doc:`fix property/atom ` command. When that command is used specific names are given to each attribute which are the "name" portion of these keywords. For arrays *i2_name* -and *d2_name*, the column of the array must also be included following -the name in brackets: e.g. d2_xyz[2], i2_mySpin[3]. +and *d2_name*, the column of the array to set must also be included +following the name in brackets: e.g. d2_xyz[2] or i2_mySpin[3]. Restrictions """""""""""" @@ -584,7 +647,7 @@ You cannot set an atom attribute (e.g. *mol* or *q* or *volume*\ ) if the :doc:`atom_style ` does not have that attribute. This command requires inter-processor communication to coordinate the -setting of bond types (angle types, etc). This means that your system +setting of bond types (angle types, etc). This means that the system must be ready to perform a simulation before using one of these keywords (force fields set, atom mass set, etc). This is not necessary for other keywords. @@ -599,7 +662,7 @@ Related commands """""""""""""""" :doc:`create_box `, :doc:`create_atoms `, -:doc:`read_data ` +:doc:`read_data `, :doc:`fix set ` Default """"""" diff --git a/doc/src/variable.rst b/doc/src/variable.rst index 75a13e47e1..691ee811d2 100644 --- a/doc/src/variable.rst +++ b/doc/src/variable.rst @@ -45,7 +45,8 @@ Syntax *universe* args = one or more strings *world* args = one string for each partition of processors - *equal* or *vector* or *atom* args = one formula containing numbers, thermo keywords, math operations, built-in functions, atom values and vectors, compute/fix/variable references + *equal* or *vector* or *atom* args = one formula containing numbers, thermo keywords, + math operations, built-in functions, atom values and vectors, compute/fix/variable references numbers = 0.0, 100, -5.4, 2.8e-4, etc constants = PI, version, on, off, true, false, yes, no thermo keywords = vol, ke, press, etc from :doc:`thermo_style ` @@ -67,8 +68,12 @@ Syntax bound(group,dir,region), gyration(group,region), ke(group,reigon), angmom(group,dim,region), torque(group,dim,region), inertia(group,dimdim,region), omega(group,dim,region) - special functions = sum(x), min(x), max(x), ave(x), trap(x), slope(x), sort(x), rsort(x), gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name), extract_setting(name), label2type(kind,label), is_typelabel(kind,label), is_timeout() - feature functions = is_available(category,feature), is_active(category,feature), is_defined(category,id) + special functions = sum(x), min(x), max(x), ave(x), trap(x), slope(x), sort(x), rsort(x), \ gmask(x), rmask(x), grmask(x,y), next(x), is_file(name), is_os(name), + extract_setting(name), label2type(kind,label), + is_typelabel(kind,label), is_timeout() + feature functions = is_available(category,feature), is_active(category,feature), + is_defined(category,id) + python function wrapper = py_varname(x,y,z,...) atom value = id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i] atom vector = id, mass, type, mol, radius, q, x, y, z, vx, vy, vz, fx, fy, fz custom atom property = i_name, d_name, i_name[i], d_name[i], i2_name[i], d2_name[i], i2_name[i][j], d2_name[i][j] @@ -127,18 +132,21 @@ command), or used as input to an averaging fix (see the :doc:`fix ave/time ` command). Variables of style *vector* store a formula which produces a vector of such values which can be used as input to various averaging fixes, or elements of which can be part of -thermodynamic output. Variables of style *atom* store a formula which -when evaluated produces one numeric value per atom which can be output -to a dump file (see the :doc:`dump custom ` command) or used as -input to an averaging fix (see the :doc:`fix ave/chunk -` and :doc:`fix ave/atom ` commands). -Variables of style *atomfile* can be used anywhere in an input script -that atom-style variables are used; they get their per-atom values -from a file rather than from a formula. Variables of style *python* -can be hooked to Python functions using code you provide, so that the -variable gets its value from the evaluation of the Python code. -Variables of style *internal* are used by a few commands which set -their value directly. +thermodynamic output. + +Variables of style *atom* store a formula which when evaluated +produces one numeric value per atom which can be output to a dump file +(see the :doc:`dump custom ` command) or used as input to an +averaging fix (see the :doc:`fix ave/chunk ` and +:doc:`fix ave/atom ` commands). Variables of style +*atomfile* can be used anywhere in an input script that atom-style +variables are used; they get their per-atom values from a file rather +than from a formula. + +Variables of style *python* can be hooked to Python functions using +Python code you provide, so that the variable gets its value from the +evaluation of the Python code. Variables of style *internal* are used +by a few commands which set their value directly. .. note:: @@ -166,15 +174,16 @@ simulation. .. note:: - When an input script line is encountered that defines a variable - of style *equal* or *vector* or *atom* or *python* that contains a - formula or Python code, the formula is NOT immediately evaluated. It - will be evaluated every time when the variable is **used** instead. If - you simply want to evaluate a formula in place you can use as - so-called. See the section below about "Immediate Evaluation of - Variables" for more details on the topic. This is also true of a - *format* style variable since it evaluates another variable when it is - invoked. + When an input script line is encountered that defines a variable of + style *equal* or *vector* or *atom* or *python* that contains a + formula or links to Python code, the formula or Python code is NOT + immediately evaluated. Instead, it is evaluated each time the + variable is **used**. If you simply want to evaluate a formula in + place you can use a so-called immediate variable. as described in + the preceding note. Or see the section below about "Immediate + Evaluation of Variables" for more details on the topic. This is + also true of a *format* style variable since it evaluates another + variable when it is invoked. Variables of style *equal* and *vector* and *atom* can be used as inputs to various other commands which evaluate their formulas as @@ -183,12 +192,12 @@ this context, variables of style *timer* or *internal* or *python* can be used in place of an equal-style variable, with the following two caveats. -First, internal-style variables can be used except by commands that -set the value stored by the internal variable. When the LAMMPS -command evaluates the internal-style variable, it will use the value -set (internally) by another command. Second, python-style variables -can be used so long as the associated Python function, as defined by -the :doc:`python ` command, returns a numeric value. When the +First, internal-style variables require their values be set by code +elsewhere in LAMMPS. When a LAMMPS input script or command evaluates +an internal-style variable, it must have a current value set +(internally) via that mechanism. Second, python-style variables can +be used so long as the associated Python function, as defined by the +:doc:`python ` command, returns a numeric value. When the LAMMPS command evaluates the python-style variable, the Python function will be executed. @@ -388,13 +397,24 @@ using the :doc:`command-line switch -var `. For the *internal* style a numeric value is provided. This value will be assigned to the variable until a LAMMPS command sets it to a new -value. There are currently only two LAMMPS commands that require -*internal* variables as inputs, because they reset them: -:doc:`create_atoms ` and :doc:`fix controller -`. As mentioned above, an internal-style variable can -be used in place of an equal-style variable anywhere else in an input -script, e.g. as an argument to another command that allows for -equal-style variables. +value. + +Note however, that most commands which use internal-style variables do +not require them to be defined in the input script. They create one or +more internal-style variables if they do not already exist. Examples +are these commands: + +* :doc:`create_atoms ` +* :doc:`fix deposit ` +* :doc:`compute bond/local ` +* :doc:`compute angle/local ` +* :doc:`compute dihedral/local ` +* :doc:`python ` command in conjunction with Python function wrappers used in equal- and atom-style variable formulas + +A command which does require an internal-style variable to be defined in +the input script is the :doc:`fix controller ` command, +because another (arbitrary) command typically also references the +variable. ---------- @@ -439,6 +459,15 @@ python-style variable can be used in place of an equal-style variable anywhere in an input script, e.g. as an argument to another command that allows for equal-style variables. +A python-style variable can also be used within the formula for an +equal-style or atom-style formula in a Python function wrapper, as +explained below for variable formulas. In this context, the usage +syntax is py_varname(arg1,arg2,...), where varname is the name of the +python-style variable. When a Python wrapper function is used in an +atom-style formula, it can be invoked once per atom using arguments +specific to each atom. The resulting values in the atom-style +variable can thus be calculated by Python code. + ---------- For the *string* style, a single string is assigned to the variable. @@ -528,9 +557,9 @@ is a valid (though strange) variable formula: Specifically, a formula can contain numbers, constants, thermo keywords, math operators, math functions, group functions, region -functions, special functions, feature functions, atom values, atom -vectors, custom atom properties, compute references, fix references, and references to other -variables. +functions, special functions, feature functions, Python function +wrappers, atom values, atom vectors, custom atom properties, compute +references, fix references, and references to other variables. +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Number | 0.2, 100, 1.0e20, -15.4, etc | @@ -551,6 +580,8 @@ variables. +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Feature functions | is_available(category,feature), is_active(category,feature), is_defined(category,id) | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Python func wrapper | py_varname(x,y,z,...) | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Atom values | id[i], mass[i], type[i], mol[i], x[i], y[i], z[i], vx[i], vy[i], vz[i], fx[i], fy[i], fz[i], q[i] | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Atom vectors | id, mass, type, mol, x, y, z, vx, vy, vz, fx, fy, fz, q | @@ -1161,6 +1192,84 @@ variable name. ---------- +Python Function wrapper +------------------------ + +A Python function wrapper enables the formula for an equal-style or +atom-style variable to invoke functions coded in Python. In the case +of an equal-style variable, the Python-coded function will be invoked +once. In the case of an atom-style variable, it can be invoked once +per atom, if one or more of its arguments include a per-atom quantity, +e.g. the position of an atom. As illustrated below, the reason to use +a Python function wrapper is to make it easy to pass LAMMPS-related +arguments to the Python-coded function associated with a python-style +variable. + +The syntax for defining a Python function wrapper is + +.. code-block:: LAMMPS + + py_varname(arg1,arg2,...argN) + +where *varname* is the name of a python-style variable which couples +to a Python-coded function. The function will be passed the zero or +more arguments listed in parentheses: *arg1*, *arg2*, ... *argN*. As +with Math Functions, each argument can itself be an arbitrarily +complex formula. + +A Python function wrapper can be used in the following manner by an +input script: + +.. code-block:: LAMMPS + + variable foo python truncate + python truncate return v_foo input 1 v_arg format fi here """ + def truncate(x): + return int(x) + """ + variable xtrunc atom py_foo(x) + variable ytrunc atom py_foo(y) + variable ztrunc atom py_foo(z) + dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc + +The first two commands define a python-style variable *foo* and couple +it to the Python-coded function *truncate()* which takes a single +floating point argument, and returns its truncated integer value. In +this case, the Python code for truncate() is included in the *python* +command; it could also be contained in a file. See the :doc:`python +` command doc page for details. + +The next three commands define atom-style variables *xtrunc*, +*ytrunc*, and *ztrunc*. Each of them include the same Python function +wrapper in their formula, with a different argument. The atom-style +variable *xtrunc* will invoke the python-style variable *foo*, which +will in turn invoke the Python-coded *truncate()* method. Because +*xtrunc* is an atom-style variable, and the argument *x* in the Python +function wrapper is a per-atom quantity (the x-coord of each atom), +each processor will invoke the *truncate()* method once per atom, for +the atoms it owns. + +When invoked for the Ith atom, the value of the *arg* internal-style +variable, defined by the *python* command, is set to the x-coord of +the Ith atom. The call via python-style variable *foo* to the Python +*truncate()* function passes the value of the *arg* variable as the +function's first (and only) argument. Likewise, the return value of +the Python function is stored by the python-style variable *foo* and +used in the *xtrunc* atom-style variable formula for the Ith atom. + +The resulting per-atom vector for *xtrunc* will thus contain the +truncated x-coord of every atom in the system. The dump command +includes the truncated xyz coords for each atom in its output. + +See the :doc:`python ` command for more details on options the +*python* command can specify as well as examples of more complex Python +functions which can be wrapped in this manner. In particular, the +Python function can take a variety of arguments, some generated by the +*python* command, and others by the arguments of the Python function +wrapper. + +---------- + Atom Values and Vectors ----------------------- diff --git a/doc/utils/sphinx-config/conf.py.in b/doc/utils/sphinx-config/conf.py.in index 073143a7e9..3a7f19e5a8 100644 --- a/doc/utils/sphinx-config/conf.py.in +++ b/doc/utils/sphinx-config/conf.py.in @@ -370,6 +370,17 @@ latex_elements = { {% \hypersetup{pageanchor=false}% avoid duplicate destination warnings \begin{titlepage}% + \sffamily\Large + The LAMMPS developers are thinking about dropping the PDF format version of + the LAMMPS manual. This would allow us to focus on the HTML version, use + HTML-only features, and skip checking if the documentation source files, + especially the embedded mathematical expressions, are compatible with \LaTeX{} output. + + Please let us know how you feel about this change by sending an email to + \texttt{developers@lammps.org} stating whether you agree or disagree with + removing support for the PDF format version of the manual and optionally + provide arguments for your preference. + \clearpage \sffamily\bfseries \begingroup % for PDF information dictionary \def\endgraf{ }\def\and{\& }% diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 773e51bf60..c505595e1d 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -82,6 +82,7 @@ Alessandro Alexey ali aliceblue +aliphatic Allera Allinger allocatable @@ -229,6 +230,7 @@ Bagi Bagnold Baig Bajaj +bak Bal balancer Balankura @@ -1176,6 +1178,7 @@ Fermionic Ferrand fexternal Fexternal +ffast ffield ffl fflush @@ -1817,6 +1820,7 @@ Karniadakis Karplus Karttunen kate +katom Katsnelson Katsura Kaufmann @@ -2195,6 +2199,7 @@ Materias mathbf mathjax matlab +Matom Matous matplotlib Matsubara @@ -2704,6 +2709,7 @@ Nprocs npt nr Nr +Nrecent Nrecompute Nrepeat nreset @@ -2782,6 +2788,7 @@ ocl octahedral octants Odegard +Og Ohara O'Hearn ohenrich @@ -3123,6 +3130,7 @@ Pxy pxz py Py +pyargs pydir pylammps PyLammps @@ -3363,6 +3371,7 @@ Rmin RMS rmsd rnage +rnflag rng rNEMD ro @@ -3418,6 +3427,7 @@ ry Ryckaert Rycroft Rydbergs +Ryzen rz Rz Sabry @@ -3837,6 +3847,7 @@ Thiaville Thibaudeau Thijsse Thirumalai +Threadripper threebody thrid ThunderX @@ -4062,6 +4073,7 @@ Vaiwala valent Valeriu valgrind +validator Valone valuev Valuev @@ -4071,6 +4083,7 @@ Vanduyfhuys varargs varavg variational +varname Varshalovich Varshney vashishta diff --git a/examples/COUPLE/plugin/liblammpsplugin.c b/examples/COUPLE/plugin/liblammpsplugin.c index 5003b3826b..21f330df9e 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.c +++ b/examples/COUPLE/plugin/liblammpsplugin.c @@ -79,6 +79,7 @@ liblammpsplugin_t *liblammpsplugin_load(const char *lib) ADDSYM(mpi_finalize); ADDSYM(kokkos_finalize); ADDSYM(python_finalize); + ADDSYM(plugin_finalize); ADDSYM(error); ADDSYM(expand); diff --git a/examples/COUPLE/plugin/liblammpsplugin.h b/examples/COUPLE/plugin/liblammpsplugin.h index 73cef7bc19..637f3db79b 100644 --- a/examples/COUPLE/plugin/liblammpsplugin.h +++ b/examples/COUPLE/plugin/liblammpsplugin.h @@ -134,6 +134,7 @@ struct _liblammpsplugin { void (*mpi_finalize)(); void (*kokkos_finalize)(); void (*python_finalize)(); + void (*plugin_finalize)(); void (*error)(void *, int, const char *); char *(*expand)(void *, const char *); diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/in.tiny_nylon.stabilized b/examples/PACKAGES/reaction/tiny_nylon_json/in.tiny_nylon.stabilized new file mode 100644 index 0000000000..a8a4492bfb --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/in.tiny_nylon.stabilized @@ -0,0 +1,61 @@ +# two monomer nylon example +# reaction produces a condensed water molecule + +units real + +boundary p p p + +atom_style full + +kspace_style pppm 1.0e-4 + +pair_style lj/class2/coul/long 8.5 + +angle_style class2 + +bond_style class2 + +dihedral_style class2 + +improper_style class2 + +special_bonds lj/coul 0 0 1 +pair_modify tail yes mix sixthpower + +read_data tiny_nylon.data & + extra/bond/per/atom 5 & + extra/angle/per/atom 15 & + extra/dihedral/per/atom 15 & + extra/improper/per/atom 25 & + extra/special/per/atom 25 + +velocity all create 300.0 4928459 dist gaussian + +molecule mol1 rxn1_stp1_unreacted.json +molecule mol2 rxn1_stp1_reacted.json +molecule mol3 rxn1_stp2_unreacted.json +molecule mol4 rxn1_stp2_reacted.json + +thermo 50 + +# dump 1 all xyz 1 test_vis.xyz +# dump_modify 1 types labels + +fix myrxns all bond/react stabilization yes statted_grp .03 & + react rxn1 all 1 0.0 2.9 mol1 mol2 rxn1_stp1_map & + react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map rescale_charges yes + +fix 1 statted_grp_REACT nvt temp 300 300 100 + +# optionally, you can customize behavior of reacting atoms, +# by using the internally-created 'bond_react_MASTER_group', like so: +fix 4 bond_react_MASTER_group temp/rescale 1 300 300 10 1 + +thermo_style custom step temp press density f_myrxns[*] + +# restart 100 restart1 restart2 + +run 10000 + +# write_restart restart_longrun +# write_data restart_longrun.data diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.1 b/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.1 new file mode 100644 index 0000000000..eb8546b4be --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.1 @@ -0,0 +1,444 @@ +LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-583-g5868aa095d-modified) +OMP_NUM_THREADS environment is not set. Defaulting to 1 thread. (src/comm.cpp:99) + using 1 OpenMP thread(s) per MPI task +# two monomer nylon example +# reaction produces a condensed water molecule + +units real + +boundary p p p + +atom_style full + +kspace_style pppm 1.0e-4 + +pair_style lj/class2/coul/long 8.5 + +angle_style class2 + +bond_style class2 + +dihedral_style class2 + +improper_style class2 + +special_bonds lj/coul 0 0 1 +pair_modify tail yes mix sixthpower + +read_data tiny_nylon.data extra/bond/per/atom 5 extra/angle/per/atom 15 extra/dihedral/per/atom 15 extra/improper/per/atom 25 extra/special/per/atom 25 +Reading data file ... + orthogonal box = (-25 -25 -25) to (25 25 25) + 1 by 1 by 1 MPI processor grid + reading atom labelmap ... + reading bond labelmap ... + reading angle labelmap ... + reading dihedral labelmap ... + reading improper labelmap ... + reading atoms ... + 44 atoms + reading velocities ... + 44 velocities + scanning bonds ... + 9 = max bonds/atom + scanning angles ... + 21 = max angles/atom + scanning dihedrals ... + 29 = max dihedrals/atom + scanning impropers ... + 29 = max impropers/atom + orthogonal box = (-25 -25 -25) to (25 25 25) + 1 by 1 by 1 MPI processor grid + reading bonds ... + 42 bonds + reading angles ... + 74 angles + reading dihedrals ... + 100 dihedrals + reading impropers ... + 16 impropers +Finding 1-2 1-3 1-4 neighbors ... + special bond factors lj: 0 0 1 + special bond factors coul: 0 0 1 + 4 = max # of 1-2 neighbors + 6 = max # of 1-3 neighbors + 35 = max # of special neighbors + special bonds CPU = 0.000 seconds + read_data CPU = 0.005 seconds + +velocity all create 300.0 4928459 dist gaussian + +molecule mol1 rxn1_stp1_unreacted.json +Read molecule template mol1: +(no title) + 1 molecules + 0 fragments + 18 atoms with max type 0 + 16 bonds with max type 8 + 25 angles with max type 25 + 23 dihedrals with max type 33 + 2 impropers with max type 3 +molecule mol2 rxn1_stp1_reacted.json +Read molecule template mol2: +(no title) + 1 molecules + 0 fragments + 18 atoms with max type 0 + 17 bonds with max type 11 + 31 angles with max type 24 + 39 dihedrals with max type 30 + 0 impropers with max type 0 +molecule mol3 rxn1_stp2_unreacted.json +Read molecule template mol3: +(no title) + 1 molecules + 0 fragments + 15 atoms with max type 0 + 14 bonds with max type 11 + 25 angles with max type 24 + 30 dihedrals with max type 30 + 0 impropers with max type 0 +molecule mol4 rxn1_stp2_reacted.json +Read molecule template mol4: +(no title) + 1 molecules + 0 fragments + 15 atoms with max type 0 + 13 bonds with max type 13 + 19 angles with max type 26 + 16 dihedrals with max type 29 + 2 impropers with max type 5 + +thermo 50 + +# dump 1 all xyz 1 test_vis.xyz +# dump_modify 1 types labels + +fix myrxns all bond/react stabilization yes statted_grp .03 react rxn1 all 1 0.0 2.9 mol1 mol2 rxn1_stp1_map react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map rescale_charges yes +WARNING: Fix bond/react: Atom affected by reaction rxn1 is too close to template edge (src/REACTION/fix_bond_react.cpp:2708) +WARNING: Fix bond/react: Atom affected by reaction rxn2 is too close to template edge (src/REACTION/fix_bond_react.cpp:2708) +dynamic group bond_react_MASTER_group defined +dynamic group statted_grp_REACT defined + +fix 1 statted_grp_REACT nvt temp 300 300 100 + +# optionally, you can customize behavior of reacting atoms, +# by using the internally-created 'bond_react_MASTER_group', like so: +fix 4 bond_react_MASTER_group temp/rescale 1 300 300 10 1 + +thermo_style custom step temp press density f_myrxns[*] + +# restart 100 restart1 restart2 + +run 10000 + +CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE + +Your simulation uses code contributions which should be cited: + +- Type Label Framework: https://doi.org/10.1021/acs.jpcb.3c08419 + +@Article{Gissinger24, + author = {Jacob R. Gissinger, Ilia Nikiforov, Yaser Afshar, Brendon Waters, Moon-ki Choi, Daniel S. Karls, Alexander Stukowski, Wonpil Im, Hendrik Heinz, Axel Kohlmeyer, and Ellad B. Tadmor}, + title = {Type Label Framework for Bonded Force Fields in LAMMPS}, + journal = {J. Phys. Chem. B}, + year = 2024, + volume = 128, + number = 13, + pages = {3282--3297} +} + +- fix bond/react: reacter.org doi:10.1016/j.polymer.2017.09.038, doi:10.1021/acs.macromol.0c02012, doi:10.1016/j.cpc.2024.109287 + +@Article{Gissinger17, + author = {J. R. Gissinger and B. D. Jensen and K. E. Wise}, + title = {Modeling Chemical Reactions in Classical Molecular Dynamics Simulations}, + journal = {Polymer}, + year = 2017, + volume = 128, + pages = {211--217} +} + +@Article{Gissinger20, + author = {J. R. Gissinger, B. D. Jensen, K. E. Wise}, + title = {{REACTER}: A Heuristic Method for Reactive Molecular Dynamics}, + journal = {Macromolecules}, + year = 2020, + volume = 53, + number = 22, + pages = {9953--9961} +} + +@Article{Gissinger24, + author = {J. R. Gissinger, B. D. Jensen, K. E. Wise}, + title = {Molecular Modeling of Reactive Systems with REACTER}, + journal = {Computer Physics Communications}, + year = 2024, + volume = 304, + number = 109287 +} + +CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE + +PPPM initialization ... + using 12-bit tables for long-range coulomb (src/kspace.cpp:342) + G vector (1/distance) = 0.23408048 + grid = 15 15 15 + stencil order = 5 + estimated absolute RMS force accuracy = 0.018627396 + estimated relative force accuracy = 5.6095851e-05 + using double precision FFTW3 + 3d grid and FFT values/proc = 8000 3375 +Generated 55 of 55 mixed pair_coeff terms from sixthpower/sixthpower mixing rule +Neighbor list info ... + update: every = 1 steps, delay = 0 steps, check = yes + max neighbors/atom: 2000, page size: 100000 + master list distance cutoff = 10.5 + ghost atom cutoff = 10.5 + binsize = 5.25, bins = 10 10 10 + 2 neighbor lists, perpetual/occasional/extra = 1 1 0 + (1) pair lj/class2/coul/long, perpetual + attributes: half, newton on + pair build: half/bin/newton + stencil: half/bin/3d + bin: standard + (2) fix bond/react, occasional, copy from (1) + attributes: half, newton on + pair build: copy + stencil: none + bin: none +Per MPI rank memory allocation (min/avg/max) = 34 | 34 | 34 Mbytes + Step Temp Press Density f_myrxns[1] f_myrxns[2] + 0 300 357.25588 0.0034851739 0 0 + 50 328.952 -39.291672 0.0034851739 1 0 + 100 311.59495 29.741131 0.0034851739 1 1 + 150 325.04965 -19.624049 0.0034851739 1 1 + 200 313.15254 15.730975 0.0034851739 1 1 + 250 307.88805 -22.944869 0.0034851739 1 1 + 300 287.9343 6.7916738 0.0034851739 1 1 + 350 314.40601 12.913246 0.0034851739 1 1 + 400 304.58587 6.9804459 0.0034851739 1 1 + 450 300.97668 14.68689 0.0034851739 1 1 + 500 345.19498 -29.961085 0.0034851739 1 1 + 550 229.37329 12.88087 0.0034851739 1 1 + 600 239.75504 -3.575567 0.0034851739 1 1 + 650 257.58951 -5.0844118 0.0034851739 1 1 + 700 249.84121 22.825491 0.0034851739 1 1 + 750 287.62022 28.059197 0.0034851739 1 1 + 800 352.31339 -50.233738 0.0034851739 1 1 + 850 316.12821 22.493396 0.0034851739 1 1 + 900 283.85252 26.129698 0.0034851739 1 1 + 950 290.30591 -25.743121 0.0034851739 1 1 + 1000 346.57856 -2.1188627 0.0034851739 1 1 + 1050 251.23391 20.636107 0.0034851739 1 1 + 1100 267.48303 -15.912571 0.0034851739 1 1 + 1150 282.66256 14.019753 0.0034851739 1 1 + 1200 227.56518 -27.076623 0.0034851739 1 1 + 1250 232.7204 -1.0387593 0.0034851739 1 1 + 1300 277.28783 32.449571 0.0034851739 1 1 + 1350 308.5732 -11.129035 0.0034851739 1 1 + 1400 264.96641 -20.93416 0.0034851739 1 1 + 1450 262.83931 30.82366 0.0034851739 1 1 + 1500 276.23182 -24.480049 0.0034851739 1 1 + 1550 319.00599 -9.8372065 0.0034851739 1 1 + 1600 316.48228 -15.940319 0.0034851739 1 1 + 1650 277.213 1.3856424 0.0034851739 1 1 + 1700 302.34736 -27.623644 0.0034851739 1 1 + 1750 312.06738 -52.786773 0.0034851739 1 1 + 1800 232.82457 -8.2208725 0.0034851739 1 1 + 1850 219.66312 0.0019676295 0.0034851739 1 1 + 1900 245.49515 -24.208901 0.0034851739 1 1 + 1950 262.40648 -1.8634047 0.0034851739 1 1 + 2000 307.5184 10.588572 0.0034851739 1 1 + 2050 316.54878 8.3925575 0.0034851739 1 1 + 2100 318.99324 29.042199 0.0034851739 1 1 + 2150 352.69886 18.443891 0.0034851739 1 1 + 2200 326.32556 -38.670826 0.0034851739 1 1 + 2250 333.73083 15.388091 0.0034851739 1 1 + 2300 258.51644 -42.524581 0.0034851739 1 1 + 2350 259.55019 -23.484535 0.0034851739 1 1 + 2400 289.75114 28.367986 0.0034851739 1 1 + 2450 312.34376 10.345659 0.0034851739 1 1 + 2500 298.65847 -14.077573 0.0034851739 1 1 + 2550 237.48617 -55.545493 0.0034851739 1 1 + 2600 266.79718 -17.165884 0.0034851739 1 1 + 2650 297.32794 -30.336059 0.0034851739 1 1 + 2700 258.77594 -1.3622741 0.0034851739 1 1 + 2750 240.05564 -0.4418629 0.0034851739 1 1 + 2800 189.88737 -13.20747 0.0034851739 1 1 + 2850 260.84014 -18.597276 0.0034851739 1 1 + 2900 299.99473 -7.3249394 0.0034851739 1 1 + 2950 377.90497 18.041664 0.0034851739 1 1 + 3000 263.92156 -54.260856 0.0034851739 1 1 + 3050 273.64003 26.973976 0.0034851739 1 1 + 3100 276.52232 -3.8583179 0.0034851739 1 1 + 3150 351.27802 84.899229 0.0034851739 1 1 + 3200 327.48822 -10.339189 0.0034851739 1 1 + 3250 356.11987 16.719201 0.0034851739 1 1 + 3300 263.01966 -18.033913 0.0034851739 1 1 + 3350 285.41196 -24.466282 0.0034851739 1 1 + 3400 233.65271 -39.293739 0.0034851739 1 1 + 3450 276.32895 44.300639 0.0034851739 1 1 + 3500 300.42687 28.489275 0.0034851739 1 1 + 3550 254.03142 -24.335427 0.0034851739 1 1 + 3600 304.6963 4.5827938 0.0034851739 1 1 + 3650 321.1785 -15.477109 0.0034851739 1 1 + 3700 256.00258 25.354176 0.0034851739 1 1 + 3750 356.60667 -5.4004451 0.0034851739 1 1 + 3800 361.31092 -1.481153 0.0034851739 1 1 + 3850 253.80196 -1.2120222 0.0034851739 1 1 + 3900 258.01895 19.280499 0.0034851739 1 1 + 3950 261.55352 -47.57161 0.0034851739 1 1 + 4000 254.54757 -8.6460339 0.0034851739 1 1 + 4050 301.56201 22.484551 0.0034851739 1 1 + 4100 304.73035 -31.321217 0.0034851739 1 1 + 4150 307.14343 0.5455717 0.0034851739 1 1 + 4200 291.05765 51.79974 0.0034851739 1 1 + 4250 333.73864 -24.668278 0.0034851739 1 1 + 4300 370.55562 -11.922425 0.0034851739 1 1 + 4350 408.03441 8.5963114 0.0034851739 1 1 + 4400 329.13138 -25.679871 0.0034851739 1 1 + 4450 366.98033 26.287047 0.0034851739 1 1 + 4500 311.80954 31.766893 0.0034851739 1 1 + 4550 292.28765 13.064974 0.0034851739 1 1 + 4600 320.72746 -49.078838 0.0034851739 1 1 + 4650 271.7173 -3.6846681 0.0034851739 1 1 + 4700 305.69502 37.947369 0.0034851739 1 1 + 4750 304.57521 52.411498 0.0034851739 1 1 + 4800 299.22173 12.555443 0.0034851739 1 1 + 4850 317.1756 -14.332118 0.0034851739 1 1 + 4900 330.62798 23.762097 0.0034851739 1 1 + 4950 342.34292 -21.428049 0.0034851739 1 1 + 5000 415.76656 10.136854 0.0034851739 1 1 + 5050 381.90086 18.17029 0.0034851739 1 1 + 5100 380.63709 28.042746 0.0034851739 1 1 + 5150 321.578 31.648896 0.0034851739 1 1 + 5200 301.22258 -29.246031 0.0034851739 1 1 + 5250 277.39891 -33.660526 0.0034851739 1 1 + 5300 259.34417 -47.78529 0.0034851739 1 1 + 5350 254.36079 10.616064 0.0034851739 1 1 + 5400 242.39213 29.468553 0.0034851739 1 1 + 5450 273.79403 -21.736668 0.0034851739 1 1 + 5500 367.30713 -6.0185517 0.0034851739 1 1 + 5550 309.52317 20.860119 0.0034851739 1 1 + 5600 316.82746 -10.339826 0.0034851739 1 1 + 5650 248.8707 33.659016 0.0034851739 1 1 + 5700 314.30152 19.05817 0.0034851739 1 1 + 5750 307.09966 14.458312 0.0034851739 1 1 + 5800 313.07936 15.825371 0.0034851739 1 1 + 5850 255.76382 23.890796 0.0034851739 1 1 + 5900 274.57947 -0.19520339 0.0034851739 1 1 + 5950 286.72486 -0.92397306 0.0034851739 1 1 + 6000 304.60873 -4.3983652 0.0034851739 1 1 + 6050 271.84784 -60.940602 0.0034851739 1 1 + 6100 283.48258 -8.2903551 0.0034851739 1 1 + 6150 336.07933 -8.2576526 0.0034851739 1 1 + 6200 363.09919 -17.309847 0.0034851739 1 1 + 6250 305.63822 14.475989 0.0034851739 1 1 + 6300 274.59195 49.13711 0.0034851739 1 1 + 6350 302.91015 -21.034604 0.0034851739 1 1 + 6400 293.87945 0.74647589 0.0034851739 1 1 + 6450 320.03843 47.624624 0.0034851739 1 1 + 6500 329.4551 -52.788127 0.0034851739 1 1 + 6550 320.74765 7.9598838 0.0034851739 1 1 + 6600 290.63144 -19.66089 0.0034851739 1 1 + 6650 266.9576 18.962642 0.0034851739 1 1 + 6700 274.61389 -9.823603 0.0034851739 1 1 + 6750 210.31417 -4.5767817 0.0034851739 1 1 + 6800 258.23732 44.233497 0.0034851739 1 1 + 6850 269.33119 -41.097429 0.0034851739 1 1 + 6900 318.88077 -21.601871 0.0034851739 1 1 + 6950 333.85796 26.067522 0.0034851739 1 1 + 7000 320.59631 -44.226656 0.0034851739 1 1 + 7050 348.15593 56.564077 0.0034851739 1 1 + 7100 291.30894 7.6597589 0.0034851739 1 1 + 7150 261.87574 -1.926724 0.0034851739 1 1 + 7200 318.17418 61.577301 0.0034851739 1 1 + 7250 269.73912 -41.921242 0.0034851739 1 1 + 7300 235.40776 -7.8606915 0.0034851739 1 1 + 7350 344.03017 73.910798 0.0034851739 1 1 + 7400 335.03212 13.288114 0.0034851739 1 1 + 7450 350.0822 -22.899498 0.0034851739 1 1 + 7500 360.69025 -78.556242 0.0034851739 1 1 + 7550 316.85895 -26.533434 0.0034851739 1 1 + 7600 271.65568 32.425371 0.0034851739 1 1 + 7650 264.7899 -40.230568 0.0034851739 1 1 + 7700 281.36612 8.9888402 0.0034851739 1 1 + 7750 276.96176 1.8256977 0.0034851739 1 1 + 7800 330.1516 -19.751957 0.0034851739 1 1 + 7850 325.80791 -12.52799 0.0034851739 1 1 + 7900 322.07086 35.21732 0.0034851739 1 1 + 7950 310.09493 -37.779071 0.0034851739 1 1 + 8000 309.87152 40.561377 0.0034851739 1 1 + 8050 252.91862 -27.545616 0.0034851739 1 1 + 8100 308.23471 -25.313089 0.0034851739 1 1 + 8150 278.69561 19.299388 0.0034851739 1 1 + 8200 270.81879 33.341696 0.0034851739 1 1 + 8250 312.58182 19.404863 0.0034851739 1 1 + 8300 302.20303 19.388391 0.0034851739 1 1 + 8350 299.55144 -12.952851 0.0034851739 1 1 + 8400 330.13129 -34.998178 0.0034851739 1 1 + 8450 281.66968 24.865214 0.0034851739 1 1 + 8500 262.4416 1.0196786 0.0034851739 1 1 + 8550 268.47784 44.761905 0.0034851739 1 1 + 8600 274.80898 8.6257741 0.0034851739 1 1 + 8650 264.82423 -3.9236534 0.0034851739 1 1 + 8700 268.88205 3.7102664 0.0034851739 1 1 + 8750 315.78315 -44.215318 0.0034851739 1 1 + 8800 280.19362 -4.2172962 0.0034851739 1 1 + 8850 331.88665 58.274571 0.0034851739 1 1 + 8900 314.70472 36.746006 0.0034851739 1 1 + 8950 395.82228 53.308443 0.0034851739 1 1 + 9000 351.95636 29.133084 0.0034851739 1 1 + 9050 369.74695 -37.487774 0.0034851739 1 1 + 9100 259.4453 -2.1826545 0.0034851739 1 1 + 9150 294.9952 -44.357151 0.0034851739 1 1 + 9200 273.94092 12.07438 0.0034851739 1 1 + 9250 257.18809 -29.792606 0.0034851739 1 1 + 9300 269.54343 -19.617554 0.0034851739 1 1 + 9350 301.09849 -26.979046 0.0034851739 1 1 + 9400 316.93675 -46.890327 0.0034851739 1 1 + 9450 304.52185 -8.8525101 0.0034851739 1 1 + 9500 371.73494 11.979311 0.0034851739 1 1 + 9550 291.23417 8.1900851 0.0034851739 1 1 + 9600 359.20912 38.693544 0.0034851739 1 1 + 9650 321.73443 33.448943 0.0034851739 1 1 + 9700 362.4237 36.54429 0.0034851739 1 1 + 9750 296.19875 -11.425255 0.0034851739 1 1 + 9800 329.9426 16.039783 0.0034851739 1 1 + 9850 296.79216 40.176303 0.0034851739 1 1 + 9900 345.63868 43.479483 0.0034851739 1 1 + 9950 282.27772 -49.08352 0.0034851739 1 1 + 10000 286.24393 -12.060687 0.0034851739 1 1 +Loop time of 1.29509 on 1 procs for 10000 steps with 44 atoms + +Performance: 667.133 ns/day, 0.036 hours/ns, 7721.451 timesteps/s, 339.744 katom-step/s +99.6% CPU use with 1 MPI tasks x 1 OpenMP threads + +MPI task timing breakdown: +Section | min time | avg time | max time |%varavg| %total +--------------------------------------------------------------- +Pair | 0.065326 | 0.065326 | 0.065326 | 0.0 | 5.04 +Bond | 0.23111 | 0.23111 | 0.23111 | 0.0 | 17.84 +Kspace | 0.94073 | 0.94073 | 0.94073 | 0.0 | 72.64 +Neigh | 0.0032599 | 0.0032599 | 0.0032599 | 0.0 | 0.25 +Comm | 0.0013719 | 0.0013719 | 0.0013719 | 0.0 | 0.11 +Output | 0.00093869 | 0.00093869 | 0.00093869 | 0.0 | 0.07 +Modify | 0.049692 | 0.049692 | 0.049692 | 0.0 | 3.84 +Other | | 0.002667 | | | 0.21 + +Nlocal: 44 ave 44 max 44 min +Histogram: 1 0 0 0 0 0 0 0 0 0 +Nghost: 14 ave 14 max 14 min +Histogram: 1 0 0 0 0 0 0 0 0 0 +Neighs: 869 ave 869 max 869 min +Histogram: 1 0 0 0 0 0 0 0 0 0 + +Total # of neighbors = 869 +Ave neighs/atom = 19.75 +Ave special neighs/atom = 6.4090909 +Neighbor list builds = 209 +Dangerous builds = 0 + +# write_restart restart_longrun +# write_data restart_longrun.data +Total wall time: 0:00:01 diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.4 b/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.4 new file mode 100644 index 0000000000..158d027156 --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/log.tiny_nylon.stabilized.12Jun2025.g++.4 @@ -0,0 +1,444 @@ +LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-583-g5868aa095d-modified) +OMP_NUM_THREADS environment is not set. Defaulting to 1 thread. (src/comm.cpp:99) + using 1 OpenMP thread(s) per MPI task +# two monomer nylon example +# reaction produces a condensed water molecule + +units real + +boundary p p p + +atom_style full + +kspace_style pppm 1.0e-4 + +pair_style lj/class2/coul/long 8.5 + +angle_style class2 + +bond_style class2 + +dihedral_style class2 + +improper_style class2 + +special_bonds lj/coul 0 0 1 +pair_modify tail yes mix sixthpower + +read_data tiny_nylon.data extra/bond/per/atom 5 extra/angle/per/atom 15 extra/dihedral/per/atom 15 extra/improper/per/atom 25 extra/special/per/atom 25 +Reading data file ... + orthogonal box = (-25 -25 -25) to (25 25 25) + 1 by 2 by 2 MPI processor grid + reading atom labelmap ... + reading bond labelmap ... + reading angle labelmap ... + reading dihedral labelmap ... + reading improper labelmap ... + reading atoms ... + 44 atoms + reading velocities ... + 44 velocities + scanning bonds ... + 9 = max bonds/atom + scanning angles ... + 21 = max angles/atom + scanning dihedrals ... + 29 = max dihedrals/atom + scanning impropers ... + 29 = max impropers/atom + orthogonal box = (-25 -25 -25) to (25 25 25) + 1 by 2 by 2 MPI processor grid + reading bonds ... + 42 bonds + reading angles ... + 74 angles + reading dihedrals ... + 100 dihedrals + reading impropers ... + 16 impropers +Finding 1-2 1-3 1-4 neighbors ... + special bond factors lj: 0 0 1 + special bond factors coul: 0 0 1 + 4 = max # of 1-2 neighbors + 6 = max # of 1-3 neighbors + 35 = max # of special neighbors + special bonds CPU = 0.000 seconds + read_data CPU = 0.006 seconds + +velocity all create 300.0 4928459 dist gaussian + +molecule mol1 rxn1_stp1_unreacted.json +Read molecule template mol1: +(no title) + 1 molecules + 0 fragments + 18 atoms with max type 0 + 16 bonds with max type 8 + 25 angles with max type 25 + 23 dihedrals with max type 33 + 2 impropers with max type 3 +molecule mol2 rxn1_stp1_reacted.json +Read molecule template mol2: +(no title) + 1 molecules + 0 fragments + 18 atoms with max type 0 + 17 bonds with max type 11 + 31 angles with max type 24 + 39 dihedrals with max type 30 + 0 impropers with max type 0 +molecule mol3 rxn1_stp2_unreacted.json +Read molecule template mol3: +(no title) + 1 molecules + 0 fragments + 15 atoms with max type 0 + 14 bonds with max type 11 + 25 angles with max type 24 + 30 dihedrals with max type 30 + 0 impropers with max type 0 +molecule mol4 rxn1_stp2_reacted.json +Read molecule template mol4: +(no title) + 1 molecules + 0 fragments + 15 atoms with max type 0 + 13 bonds with max type 13 + 19 angles with max type 26 + 16 dihedrals with max type 29 + 2 impropers with max type 5 + +thermo 50 + +# dump 1 all xyz 1 test_vis.xyz +# dump_modify 1 types labels + +fix myrxns all bond/react stabilization yes statted_grp .03 react rxn1 all 1 0.0 2.9 mol1 mol2 rxn1_stp1_map react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map rescale_charges yes +WARNING: Fix bond/react: Atom affected by reaction rxn1 is too close to template edge (src/REACTION/fix_bond_react.cpp:2708) +WARNING: Fix bond/react: Atom affected by reaction rxn2 is too close to template edge (src/REACTION/fix_bond_react.cpp:2708) +dynamic group bond_react_MASTER_group defined +dynamic group statted_grp_REACT defined + +fix 1 statted_grp_REACT nvt temp 300 300 100 + +# optionally, you can customize behavior of reacting atoms, +# by using the internally-created 'bond_react_MASTER_group', like so: +fix 4 bond_react_MASTER_group temp/rescale 1 300 300 10 1 + +thermo_style custom step temp press density f_myrxns[*] + +# restart 100 restart1 restart2 + +run 10000 + +CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE + +Your simulation uses code contributions which should be cited: + +- Type Label Framework: https://doi.org/10.1021/acs.jpcb.3c08419 + +@Article{Gissinger24, + author = {Jacob R. Gissinger, Ilia Nikiforov, Yaser Afshar, Brendon Waters, Moon-ki Choi, Daniel S. Karls, Alexander Stukowski, Wonpil Im, Hendrik Heinz, Axel Kohlmeyer, and Ellad B. Tadmor}, + title = {Type Label Framework for Bonded Force Fields in LAMMPS}, + journal = {J. Phys. Chem. B}, + year = 2024, + volume = 128, + number = 13, + pages = {3282--3297} +} + +- fix bond/react: reacter.org doi:10.1016/j.polymer.2017.09.038, doi:10.1021/acs.macromol.0c02012, doi:10.1016/j.cpc.2024.109287 + +@Article{Gissinger17, + author = {J. R. Gissinger and B. D. Jensen and K. E. Wise}, + title = {Modeling Chemical Reactions in Classical Molecular Dynamics Simulations}, + journal = {Polymer}, + year = 2017, + volume = 128, + pages = {211--217} +} + +@Article{Gissinger20, + author = {J. R. Gissinger, B. D. Jensen, K. E. Wise}, + title = {{REACTER}: A Heuristic Method for Reactive Molecular Dynamics}, + journal = {Macromolecules}, + year = 2020, + volume = 53, + number = 22, + pages = {9953--9961} +} + +@Article{Gissinger24, + author = {J. R. Gissinger, B. D. Jensen, K. E. Wise}, + title = {Molecular Modeling of Reactive Systems with REACTER}, + journal = {Computer Physics Communications}, + year = 2024, + volume = 304, + number = 109287 +} + +CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE + +PPPM initialization ... + using 12-bit tables for long-range coulomb (src/kspace.cpp:342) + G vector (1/distance) = 0.23408048 + grid = 15 15 15 + stencil order = 5 + estimated absolute RMS force accuracy = 0.018627396 + estimated relative force accuracy = 5.6095851e-05 + using double precision FFTW3 + 3d grid and FFT values/proc = 3380 960 +Generated 55 of 55 mixed pair_coeff terms from sixthpower/sixthpower mixing rule +Neighbor list info ... + update: every = 1 steps, delay = 0 steps, check = yes + max neighbors/atom: 2000, page size: 100000 + master list distance cutoff = 10.5 + ghost atom cutoff = 10.5 + binsize = 5.25, bins = 10 10 10 + 2 neighbor lists, perpetual/occasional/extra = 1 1 0 + (1) pair lj/class2/coul/long, perpetual + attributes: half, newton on + pair build: half/bin/newton + stencil: half/bin/3d + bin: standard + (2) fix bond/react, occasional, copy from (1) + attributes: half, newton on + pair build: copy + stencil: none + bin: none +Per MPI rank memory allocation (min/avg/max) = 33.49 | 33.72 | 34.28 Mbytes + Step Temp Press Density f_myrxns[1] f_myrxns[2] + 0 300 357.25588 0.0034851739 0 0 + 50 328.952 -39.291672 0.0034851739 1 0 + 100 311.59495 29.741131 0.0034851739 1 1 + 150 325.04965 -19.624049 0.0034851739 1 1 + 200 313.15254 15.730975 0.0034851739 1 1 + 250 307.88805 -22.944869 0.0034851739 1 1 + 300 287.9343 6.7916738 0.0034851739 1 1 + 350 314.40601 12.913246 0.0034851739 1 1 + 400 304.58587 6.9804459 0.0034851739 1 1 + 450 300.97668 14.68689 0.0034851739 1 1 + 500 345.19498 -29.961085 0.0034851739 1 1 + 550 229.37329 12.88087 0.0034851739 1 1 + 600 239.75504 -3.575567 0.0034851739 1 1 + 650 257.58951 -5.0844118 0.0034851739 1 1 + 700 249.84121 22.825491 0.0034851739 1 1 + 750 287.62022 28.059197 0.0034851739 1 1 + 800 352.31339 -50.233738 0.0034851739 1 1 + 850 316.12821 22.493396 0.0034851739 1 1 + 900 283.85252 26.129698 0.0034851739 1 1 + 950 290.30591 -25.743121 0.0034851739 1 1 + 1000 346.57856 -2.1188627 0.0034851739 1 1 + 1050 251.23391 20.636107 0.0034851739 1 1 + 1100 267.48303 -15.912571 0.0034851739 1 1 + 1150 282.66256 14.019753 0.0034851739 1 1 + 1200 227.56518 -27.076623 0.0034851739 1 1 + 1250 232.7204 -1.0387593 0.0034851739 1 1 + 1300 277.28783 32.449571 0.0034851739 1 1 + 1350 308.5732 -11.129035 0.0034851739 1 1 + 1400 264.96641 -20.93416 0.0034851739 1 1 + 1450 262.83931 30.82366 0.0034851739 1 1 + 1500 276.23182 -24.480049 0.0034851739 1 1 + 1550 319.00599 -9.8372065 0.0034851739 1 1 + 1600 316.48228 -15.940319 0.0034851739 1 1 + 1650 277.213 1.3856424 0.0034851739 1 1 + 1700 302.34736 -27.623644 0.0034851739 1 1 + 1750 312.06738 -52.786773 0.0034851739 1 1 + 1800 232.82457 -8.2208725 0.0034851739 1 1 + 1850 219.66312 0.0019675712 0.0034851739 1 1 + 1900 245.49515 -24.2089 0.0034851739 1 1 + 1950 262.40648 -1.8634053 0.0034851739 1 1 + 2000 307.5184 10.588572 0.0034851739 1 1 + 2050 316.54878 8.3925573 0.0034851739 1 1 + 2100 318.99324 29.042199 0.0034851739 1 1 + 2150 352.69886 18.443891 0.0034851739 1 1 + 2200 326.32556 -38.670827 0.0034851739 1 1 + 2250 333.73083 15.388091 0.0034851739 1 1 + 2300 258.51644 -42.524579 0.0034851739 1 1 + 2350 259.55019 -23.484534 0.0034851739 1 1 + 2400 289.75114 28.367991 0.0034851739 1 1 + 2450 312.34376 10.34566 0.0034851739 1 1 + 2500 298.65847 -14.077574 0.0034851739 1 1 + 2550 237.48617 -55.545492 0.0034851739 1 1 + 2600 266.79719 -17.165888 0.0034851739 1 1 + 2650 297.32793 -30.336065 0.0034851739 1 1 + 2700 258.77594 -1.3622773 0.0034851739 1 1 + 2750 240.05564 -0.44186476 0.0034851739 1 1 + 2800 189.88736 -13.20747 0.0034851739 1 1 + 2850 260.84014 -18.597268 0.0034851739 1 1 + 2900 299.99469 -7.3249657 0.0034851739 1 1 + 2950 377.90515 18.041562 0.0034851739 1 1 + 3000 263.92164 -54.260872 0.0034851739 1 1 + 3050 273.63965 26.974167 0.0034851739 1 1 + 3100 276.52188 -3.8587984 0.0034851739 1 1 + 3150 351.27821 84.898762 0.0034851739 1 1 + 3200 327.48898 -10.338223 0.0034851739 1 1 + 3250 356.1212 16.719521 0.0034851739 1 1 + 3300 263.02135 -18.034405 0.0034851739 1 1 + 3350 285.4132 -24.466598 0.0034851739 1 1 + 3400 233.65385 -39.293822 0.0034851739 1 1 + 3450 276.32817 44.300742 0.0034851739 1 1 + 3500 300.43096 28.490491 0.0034851739 1 1 + 3550 254.03694 -24.336803 0.0034851739 1 1 + 3600 304.68561 4.5774591 0.0034851739 1 1 + 3650 321.19694 -15.472971 0.0034851739 1 1 + 3700 256.01801 25.355422 0.0034851739 1 1 + 3750 356.6048 -5.4033701 0.0034851739 1 1 + 3800 361.31685 -1.4920086 0.0034851739 1 1 + 3850 253.81335 -1.20491 0.0034851739 1 1 + 3900 257.99176 19.272863 0.0034851739 1 1 + 3950 261.56599 -47.578625 0.0034851739 1 1 + 4000 254.55501 -8.6462427 0.0034851739 1 1 + 4050 301.57465 22.466646 0.0034851739 1 1 + 4100 304.71812 -31.337506 0.0034851739 1 1 + 4150 307.05688 0.54710967 0.0034851739 1 1 + 4200 291.11678 51.789059 0.0034851739 1 1 + 4250 333.71702 -24.64525 0.0034851739 1 1 + 4300 370.53725 -11.852814 0.0034851739 1 1 + 4350 407.98596 8.5128018 0.0034851739 1 1 + 4400 329.43106 -25.637208 0.0034851739 1 1 + 4450 366.91557 26.250029 0.0034851739 1 1 + 4500 311.63134 31.61568 0.0034851739 1 1 + 4550 292.18008 13.020734 0.0034851739 1 1 + 4600 320.76549 -49.324846 0.0034851739 1 1 + 4650 271.72115 -3.6666938 0.0034851739 1 1 + 4700 305.52974 38.114013 0.0034851739 1 1 + 4750 304.37057 52.050863 0.0034851739 1 1 + 4800 300.50548 13.158616 0.0034851739 1 1 + 4850 317.66606 -14.951608 0.0034851739 1 1 + 4900 331.39591 23.823575 0.0034851739 1 1 + 4950 340.899 -19.557136 0.0034851739 1 1 + 5000 414.45112 8.7124515 0.0034851739 1 1 + 5050 382.95479 18.555744 0.0034851739 1 1 + 5100 381.26344 22.674486 0.0034851739 1 1 + 5150 309.88477 36.184971 0.0034851739 1 1 + 5200 312.4976 -31.403643 0.0034851739 1 1 + 5250 285.1896 -36.180241 0.0034851739 1 1 + 5300 249.68604 -55.078779 0.0034851739 1 1 + 5350 251.76542 12.156887 0.0034851739 1 1 + 5400 278.40908 15.846527 0.0034851739 1 1 + 5450 281.22116 -12.005198 0.0034851739 1 1 + 5500 362.38064 -14.608206 0.0034851739 1 1 + 5550 287.39255 38.446058 0.0034851739 1 1 + 5600 264.87699 -20.21112 0.0034851739 1 1 + 5650 300.31424 -15.899086 0.0034851739 1 1 + 5700 318.6444 18.688317 0.0034851739 1 1 + 5750 321.74224 -7.5355749 0.0034851739 1 1 + 5800 314.65871 33.512178 0.0034851739 1 1 + 5850 307.11597 -16.492491 0.0034851739 1 1 + 5900 259.10441 -49.668159 0.0034851739 1 1 + 5950 292.09045 -22.04962 0.0034851739 1 1 + 6000 243.99229 -15.110485 0.0034851739 1 1 + 6050 276.66494 -21.052012 0.0034851739 1 1 + 6100 328.39444 64.522811 0.0034851739 1 1 + 6150 289.49991 -18.518616 0.0034851739 1 1 + 6200 358.50076 -9.3115021 0.0034851739 1 1 + 6250 303.15143 -42.618735 0.0034851739 1 1 + 6300 297.17364 18.116143 0.0034851739 1 1 + 6350 254.2382 2.4963759 0.0034851739 1 1 + 6400 227.56255 23.535315 0.0034851739 1 1 + 6450 280.70805 13.243012 0.0034851739 1 1 + 6500 251.14485 -18.466759 0.0034851739 1 1 + 6550 284.32321 9.0351234 0.0034851739 1 1 + 6600 284.03779 -10.683306 0.0034851739 1 1 + 6650 315.1569 -39.736019 0.0034851739 1 1 + 6700 302.50424 5.9013011 0.0034851739 1 1 + 6750 368.279 44.343503 0.0034851739 1 1 + 6800 279.67266 -35.967233 0.0034851739 1 1 + 6850 298.98045 3.0539699 0.0034851739 1 1 + 6900 236.94852 0.55971293 0.0034851739 1 1 + 6950 303.38382 -9.1880246 0.0034851739 1 1 + 7000 264.56096 3.904024 0.0034851739 1 1 + 7050 272.26968 -44.608801 0.0034851739 1 1 + 7100 235.38132 13.141913 0.0034851739 1 1 + 7150 272.0985 26.601686 0.0034851739 1 1 + 7200 314.41177 -7.6236617 0.0034851739 1 1 + 7250 313.29915 -45.214663 0.0034851739 1 1 + 7300 309.20548 -11.691744 0.0034851739 1 1 + 7350 342.22769 18.805899 0.0034851739 1 1 + 7400 301.02701 32.830071 0.0034851739 1 1 + 7450 318.19524 48.620851 0.0034851739 1 1 + 7500 290.00663 -9.1444424 0.0034851739 1 1 + 7550 238.07666 32.654941 0.0034851739 1 1 + 7600 275.95317 -23.982336 0.0034851739 1 1 + 7650 274.54472 -29.183684 0.0034851739 1 1 + 7700 307.25317 11.981268 0.0034851739 1 1 + 7750 299.40271 -19.390542 0.0034851739 1 1 + 7800 314.18813 -6.0794802 0.0034851739 1 1 + 7850 271.14043 40.491364 0.0034851739 1 1 + 7900 266.43792 37.250574 0.0034851739 1 1 + 7950 330.21996 31.783381 0.0034851739 1 1 + 8000 313.80294 44.878972 0.0034851739 1 1 + 8050 331.1094 1.9597746 0.0034851739 1 1 + 8100 267.80448 26.159121 0.0034851739 1 1 + 8150 267.17305 16.135234 0.0034851739 1 1 + 8200 290.248 -13.058622 0.0034851739 1 1 + 8250 231.77635 -42.850795 0.0034851739 1 1 + 8300 233.58288 29.137765 0.0034851739 1 1 + 8350 270.2913 -11.177011 0.0034851739 1 1 + 8400 254.69873 3.1775639 0.0034851739 1 1 + 8450 334.88277 -27.336924 0.0034851739 1 1 + 8500 354.95137 -9.751911 0.0034851739 1 1 + 8550 327.78998 8.2348621 0.0034851739 1 1 + 8600 306.15772 1.3756734 0.0034851739 1 1 + 8650 325.68699 6.5939205 0.0034851739 1 1 + 8700 302.84804 13.912169 0.0034851739 1 1 + 8750 299.34988 10.311212 0.0034851739 1 1 + 8800 258.77684 -8.0783535 0.0034851739 1 1 + 8850 299.8835 -9.61362 0.0034851739 1 1 + 8900 350.28205 -34.897861 0.0034851739 1 1 + 8950 316.90133 -52.845562 0.0034851739 1 1 + 9000 337.50548 18.172556 0.0034851739 1 1 + 9050 306.67254 37.062973 0.0034851739 1 1 + 9100 310.74844 58.178538 0.0034851739 1 1 + 9150 301.53463 5.9650738 0.0034851739 1 1 + 9200 284.61384 10.677528 0.0034851739 1 1 + 9250 247.86475 -15.325203 0.0034851739 1 1 + 9300 309.74481 -60.070902 0.0034851739 1 1 + 9350 290.17111 5.6049467 0.0034851739 1 1 + 9400 268.79925 -9.2423032 0.0034851739 1 1 + 9450 315.80445 18.124612 0.0034851739 1 1 + 9500 351.58161 -14.465115 0.0034851739 1 1 + 9550 344.93028 43.161438 0.0034851739 1 1 + 9600 265.92273 0.3396608 0.0034851739 1 1 + 9650 261.1649 -20.46043 0.0034851739 1 1 + 9700 262.05627 27.187613 0.0034851739 1 1 + 9750 249.12463 15.911339 0.0034851739 1 1 + 9800 262.40626 -16.159153 0.0034851739 1 1 + 9850 267.48677 0.32143902 0.0034851739 1 1 + 9900 316.15674 40.664589 0.0034851739 1 1 + 9950 323.94181 20.356291 0.0034851739 1 1 + 10000 307.90841 4.8392456 0.0034851739 1 1 +Loop time of 0.937214 on 4 procs for 10000 steps with 44 atoms + +Performance: 921.881 ns/day, 0.026 hours/ns, 10669.916 timesteps/s, 469.476 katom-step/s +96.5% CPU use with 4 MPI tasks x 1 OpenMP threads + +MPI task timing breakdown: +Section | min time | avg time | max time |%varavg| %total +--------------------------------------------------------------- +Pair | 0.00042896 | 0.01827 | 0.04944 | 14.9 | 1.95 +Bond | 0.00067626 | 0.061292 | 0.18867 | 31.0 | 6.54 +Kspace | 0.50738 | 0.64527 | 0.7136 | 10.4 | 68.85 +Neigh | 0.0031476 | 0.0031815 | 0.0032165 | 0.0 | 0.34 +Comm | 0.01335 | 0.020817 | 0.035424 | 6.1 | 2.22 +Output | 0.0010449 | 0.0012202 | 0.0016969 | 0.8 | 0.13 +Modify | 0.16314 | 0.18266 | 0.20201 | 4.3 | 19.49 +Other | | 0.004499 | | | 0.48 + +Nlocal: 11 ave 40 max 0 min +Histogram: 2 1 0 0 0 0 0 0 0 1 +Nghost: 22 ave 40 max 4 min +Histogram: 1 0 0 0 0 2 0 0 0 1 +Neighs: 216.5 ave 845 max 0 min +Histogram: 3 0 0 0 0 0 0 0 0 1 + +Total # of neighbors = 866 +Ave neighs/atom = 19.681818 +Ave special neighs/atom = 6.4090909 +Neighbor list builds = 212 +Dangerous builds = 0 + +# write_restart restart_longrun +# write_data restart_longrun.data +Total wall time: 0:00:00 diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_map b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_map new file mode 100644 index 0000000000..0de209fdfd --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_map @@ -0,0 +1,35 @@ +this is a nominal superimpose file + +2 edgeIDs +18 equivalences + +InitiatorIDs + +10 +1 + +EdgeIDs + +16 +8 + +Equivalences + +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +11 11 +12 12 +13 13 +14 14 +15 15 +16 16 +17 17 +18 18 diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_reacted.json b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_reacted.json new file mode 100644 index 0000000000..f8b1ed0ef1 --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_reacted.json @@ -0,0 +1,177 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "schema": "https://download.lammps.org/json/molecule-schema.json", + "coords": { + "format": ["atom-id", "x", "y", "z"], + "data": [ + [1, -5.522237178, -0.752722499, 1.631158408], + [2, -5.170398325, -0.545733378, 0.178129978], + [3, -6.469694974, -0.553071841, -0.648889109], + [4, -6.052075697, -1.721152483, 1.744647858], + [5, -6.183058842, 0.071386755, 1.971497329], + [6, -4.489339595, -1.389196844, -0.173156276], + [7, -4.637590712, 0.453703382, 0.051251954], + [8, -5.618657658, 0.13891881, 4.386106928], + [9, -4.669491736, -0.989818781, 3.943591338], + [10, -4.270193542, -0.766405234, 2.474102239], + [11, -3.348470373, -1.875393291, 2.024289246], + [12, -3.569793683, 0.564183226, 2.345995471], + [13, -5.201078949, -1.993301389, 4.044218837], + [14, -3.736681607, -0.984819193, 4.598304847], + [15, -4.255401979, 1.370923174, 2.679069013], + [16, -6.136393628, -0.339866195, -2.13677499], + [17, -6.996331494, -1.555519161, -0.517408063], + [18, -7.153308038, 0.284949373, -0.289930394] + ] + }, + "types": { + "format": ["atom-id", "type"], + "data": [ + [1, "n"], + [2, "c2"], + [3, "c2"], + [4, "hn"], + [5, "hn"], + [6, "hc"], + [7, "hc"], + [8, "c2"], + [9, "c2"], + [10, "c_1"], + [11, "o_1"], + [12, "o"], + [13, "hc"], + [14, "hc"], + [15, "ho"], + [16, "c2"], + [17, "hc"], + [18, "hc"] + ] + }, + "molecules": { + "format": ["atom-id", "molecule-id"], + "data": [ + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1], + [7, 1], + [8, 1], + [9, 1], + [10, 1], + [11, 1], + [12, 1], + [13, 1], + [14, 1], + [15, 1], + [16, 1], + [17, 1], + [18, 1] + ] + }, + "bonds": { + "format": ["bond-type", "atom1", "atom2"], + "data": [ + ["n-c2", 1, 2], + ["n-hn", 1, 4], + ["n-hn", 1, 5], + ["n-c_1", 1, 10], + ["c2-c2", 2, 3], + ["c2-hc", 2, 6], + ["c2-hc", 2, 7], + ["c2-c2", 3, 16], + ["c2-hc", 3, 17], + [ "c2-hc", 3, 18], + [ "c2-c2", 8, 9], + [ "c2-c_1", 9, 10], + [ "c2-hc", 9, 13], + [ "c2-hc", 9, 14], + [ "c_1-o_1", 10, 11], + [ "c_1-o", 10, 12], + [ "o-ho", 12, 15] + ] + }, + "angles": { + "format": ["angle-type", "atom1", "atom2", "atom3"], + "data": [ + ["c2-n-hn", 2, 1, 4], + ["c2-n-hn", 2, 1, 5], + ["c2-n-c_1", 2, 1, 10], + ["hn-n-hn", 4, 1, 5], + ["hn-n-c_1", 4, 1, 10], + ["hn-n-c_1", 5, 1, 10], + ["n-c2-c2", 1, 2, 3], + ["n-c2-hc", 1, 2, 6], + ["n-c2-hc", 1, 2, 7], + [ "c2-c2-hc", 3, 2, 6], + [ "c2-c2-hc", 3, 2, 7], + [ "hc-c2-hc", 6, 2, 7], + [ "c2-c2-c2", 2, 3, 16], + [ "c2-c2-hc", 2, 3, 17], + [ "c2-c2-hc", 2, 3, 18], + [ "c2-c2-hc", 16, 3, 17], + [ "c2-c2-hc", 16, 3, 18], + [ "hc-c2-hc", 17, 3, 18], + [ "c2-c2-c_1", 8, 9, 10], + [ "c2-c2-hc", 8, 9, 13], + [ "c2-c2-hc", 8, 9, 14], + [ "hc-c2-c_1", 13, 9, 10], + [ "hc-c2-c_1", 14, 9, 10], + [ "hc-c2-hc", 13, 9, 14], + [ "c2-c_1-o_1", 9, 10, 11], + [ "c2-c_1-o", 9, 10, 12], + [ "n-c_1-c2", 1, 10, 9], + [ "o_1-c_1-o", 11, 10, 12], + [ "n-c_1-o_1", 1, 10, 11], + [ "n-c_1-o", 1, 10, 12], + [ "c_1-o-ho", 10, 12, 15] + ] + }, + "dihedrals": { + "format": ["dihedral-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + ["hn-n-c2-c2", 4, 1, 2, 3], + ["hn-n-c2-hc", 4, 1, 2, 6], + ["hn-n-c2-hc", 4, 1, 2, 7], + ["hn-n-c2-c2", 5, 1, 2, 3], + ["hn-n-c2-hc", 5, 1, 2, 6], + ["hn-n-c2-hc", 5, 1, 2, 7], + ["c_1-n-c2-c2", 10, 1, 2, 3], + ["c_1-n-c2-hc", 10, 1, 2, 6], + ["c_1-n-c2-hc", 10, 1, 2, 7], + [ "c2-n-c_1-c2", 2, 1, 10, 9], + [ "c2-n-c_1-o_1", 2, 1, 10, 11], + [ "c2-n-c_1-o", 2, 1, 10, 12], + [ "hn-n-c_1-c2", 4, 1, 10, 9], + [ "hn-n-c_1-o_1", 4, 1, 10, 11], + [ "hn-n-c_1-o", 4, 1, 10, 12], + [ "hn-n-c_1-c2", 5, 1, 10, 9], + [ "hn-n-c_1-o_1", 5, 1, 10, 11], + [ "hn-n-c_1-o", 5, 1, 10, 12], + [ "n-c2-c2-c2", 1, 2, 3, 16], + [ "n-c2-c2-hc", 1, 2, 3, 17], + [ "n-c2-c2-hc", 1, 2, 3, 18], + [ "c2-c2-c2-hc", 16, 3, 2, 6], + [ "hc-c2-c2-hc", 6, 2, 3, 17], + [ "hc-c2-c2-hc", 6, 2, 3, 18], + [ "c2-c2-c2-hc", 16, 3, 2, 7], + [ "hc-c2-c2-hc", 7, 2, 3, 17], + [ "hc-c2-c2-hc", 7, 2, 3, 18], + [ "c2-c2-c_1-o_1", 8, 9, 10, 11], + [ "c2-c2-c_1-o", 8, 9, 10, 12], + [ "c2-c2-c_1-n", 8, 9, 10, 1], + [ "hc-c2-c_1-o_1", 13, 9, 10, 11], + [ "hc-c2-c_1-o", 13, 9, 10, 12], + [ "hc-c2-c_1-n", 13, 9, 10, 1], + [ "hc-c2-c_1-o_1", 14, 9, 10, 11], + [ "hc-c2-c_1-o", 14, 9, 10, 12], + [ "hc-c2-c_1-n", 14, 9, 10, 1], + [ "c2-c_1-o-ho", 9, 10, 12, 15], + [ "o_1-c_1-o-ho", 11, 10, 12, 15], + [ "n-c_1-o-ho", 1, 10, 12, 15] + ] + } +} diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_unreacted.json b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_unreacted.json new file mode 100644 index 0000000000..be909ee217 --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp1_unreacted.json @@ -0,0 +1,184 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "schema": "https://download.lammps.org/json/molecule-schema.json", + "coords": { + "format": ["atom-id", "x", "y", "z"], + "data": [ + [1, -4.922858499, -0.946981747, 1.146055346], + [2, -5.047194816, -0.935266843, -0.358172771], + [3, -6.526281447, -0.755365854, -0.743523227], + [4, -5.282604074, 0.020446894, 1.552710361], + [5, -3.860696509, -1.09585019, 1.428304925], + [6, -4.662381862, -1.920899862, -0.781524026], + [7, -4.43397654, -0.072765142, -0.784070641], + [8, -5.506279186, 0.202610302, 4.825815562], + [9, -4.449176624, -0.844592213, 4.423366146], + [10, -4.103915981, -0.749628655, 2.925195217], + [11, -3.376248536, -1.886171498, 2.245643443], + [12, -4.49323543, 0.477213651, 2.137199034], + [13, -4.849052953, -1.888876753, 4.66399375], + [14, -3.49182295, -0.66291331, 5.018510248], + [15, -5.020776528, 1.189745133, 2.805427194], + [16, -3.964987378, 2.900602044, -1.55134117], + [17, -4.460693773, 2.836101897, 0.668881952], + [18, -4.828494, 3.219655862, -0.122111278] + ] + }, + "types": { + "format": ["atom-id", "type"], + "data": [ + [1, "na"], + [2, "c2"], + [3, "c2"], + [4, "hn"], + [5, "hn"], + [6, "hc"], + [7, "hc"], + [8, "c2"], + [9, "c2"], + [10, "c_1"], + [11, "o_1"], + [12, "o"], + [13, "hc"], + [14, "hc"], + [15, "ho"], + [16, "c2"], + [17, "hc"], + [18, "hc"] + ] + }, + "molecules": { + "format": ["atom-id", "molecule-id"], + "data": [ + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1], + [7, 1], + [8, 1], + [9, 1], + [10, 1], + [11, 1], + [12, 1], + [13, 1], + [14, 1], + [15, 1], + [16, 1], + [17, 1], + [18, 1] + ] + }, + "bonds": { + "format": ["bond-type", "atom1", "atom2"], + "data": [ + ["na-c2", 1, 2], + ["na-hn", 1, 4], + ["na-hn", 1, 5], + ["c2-c2", 2, 3], + ["c2-hc", 2, 6], + ["c2-hc", 2, 7], + ["c2-c2", 3, 16], + ["c2-hc", 3, 17], + ["c2-hc", 3, 18], + [ "c2-c2", 8, 9], + [ "c2-c_1", 9, 10], + [ "c2-hc", 9, 13], + [ "c2-hc", 9, 14], + [ "c_1-o_1", 10, 11], + [ "c_1-o", 10, 12], + [ "o-ho", 12, 15] + ] + }, + "angles": { + "format": ["angle-type", "atom1", "atom2", "atom3"], + "data": [ + ["c2-na-hn", 2, 1, 4], + ["c2-na-hn", 2, 1, 5], + ["hn-na-hn", 4, 1, 5], + ["na-c2-c2", 1, 2, 3], + ["na-c2-hc", 1, 2, 6], + ["na-c2-hc", 1, 2, 7], + ["c2-c2-hc", 3, 2, 6], + ["c2-c2-hc", 3, 2, 7], + ["hc-c2-hc", 6, 2, 7], + [ "c2-c2-c2", 2, 3, 16], + [ "c2-c2-hc", 2, 3, 17], + [ "c2-c2-hc", 2, 3, 18], + [ "c2-c2-hc", 16, 3, 17], + [ "c2-c2-hc", 16, 3, 18], + [ "hc-c2-hc", 17, 3, 18], + [ "c2-c2-c_1", 8, 9, 10], + [ "c2-c2-hc", 8, 9, 13], + [ "c2-c2-hc", 8, 9, 14], + [ "hc-c2-c_1", 13, 9, 10], + [ "hc-c2-c_1", 14, 9, 10], + [ "hc-c2-hc", 13, 9, 14], + [ "c2-c_1-o_1", 9, 10, 11], + [ "c2-c_1-o", 9, 10, 12], + [ "o_1-c_1-o", 11, 10, 12], + [ "c_1-o-ho", 10, 12, 15] + ] + }, + "dihedrals": { + "format": ["dihedral-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + ["hn-na-c2-c2", 4, 1, 2, 3], + ["hn-na-c2-hc", 4, 1, 2, 6], + ["hn-na-c2-hc", 4, 1, 2, 7], + ["hn-na-c2-c2", 5, 1, 2, 3], + ["hn-na-c2-hc", 5, 1, 2, 6], + ["hn-na-c2-hc", 5, 1, 2, 7], + ["na-c2-c2-c2", 1, 2, 3, 16], + ["na-c2-c2-hc", 1, 2, 3, 17], + ["na-c2-c2-hc", 1, 2, 3, 18], + [ "c2-c2-c2-hc", 16, 3, 2, 6], + [ "hc-c2-c2-hc", 6, 2, 3, 17], + [ "hc-c2-c2-hc", 6, 2, 3, 18], + [ "c2-c2-c2-hc", 16, 3, 2, 7], + [ "hc-c2-c2-hc", 7, 2, 3, 17], + [ "hc-c2-c2-hc", 7, 2, 3, 18], + [ "c2-c2-c_1-o_1", 8, 9, 10, 11], + [ "c2-c2-c_1-o", 8, 9, 10, 12], + [ "hc-c2-c_1-o_1", 13, 9, 10, 11], + [ "hc-c2-c_1-o", 13, 9, 10, 12], + [ "hc-c2-c_1-o_1", 14, 9, 10, 11], + [ "hc-c2-c_1-o", 14, 9, 10, 12], + [ "c2-c_1-o-ho", 9, 10, 12, 15], + [ "o_1-c_1-o-ho", 11, 10, 12, 15] + ] + }, + "charges": { + "format": ["atom-id", "charge"], + "data": [ + [1, -0.3], + [2, 0.0], + [3, 0.0], + [4, 0.0], + [5, 0.0], + [6, 0.0], + [7, 0.0], + [8, 0.0], + [9, 0.0], + [10, 0.3], + [11, 0.0], + [12, 0.0], + [13, 0.0], + [14, 0.0], + [15, 0.0], + [16, 0.0], + [17, 0.0], + [18, 0.0] + ] + }, + "impropers": { + "format": [ "improper-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + [ "c2-na-hn-hn", 2, 1, 4, 5], + [ "c2-c_1-o_1-o", 9, 10, 11, 12] + ] + } +} diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_map b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_map new file mode 100644 index 0000000000..8389688dd5 --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_map @@ -0,0 +1,32 @@ +this is a nominal superimpose file + +2 edgeIDs +15 equivalences + +InitiatorIDs + +4 +12 + +EdgeIDs + +8 +3 + +Equivalences + +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +11 11 +12 12 +13 13 +14 14 +15 15 diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_reacted.json b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_reacted.json new file mode 100644 index 0000000000..8d8979675a --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_reacted.json @@ -0,0 +1,156 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "schema": "https://download.lammps.org/json/molecule-schema.json", + "coords": { + "format": ["atom-id", "x", "y", "z"], + "data": [ + [1, -4.856280281, -1.050467974, 1.432625159], + [2, -5.047194816, -0.935266843, -0.358172771], + [3, -6.526281447, -0.755365854, -0.743523227], + [4, -5.282604074, 0.020446894, 1.552710361], + [5, -3.860696509, -1.09585019, 1.428304925], + [6, -4.662381862, -1.920899862, -0.781524026], + [7, -4.43397654, -0.072765142, -0.784070641], + [8, -5.506279186, 0.202610302, 4.825815562], + [9, -4.449176624, -0.844592213, 4.423366146], + [10, -4.103915981, -0.749628655, 2.925195217], + [11, -3.376248536, -1.886171498, 2.245643443], + [12, -4.49323543, 0.477213651, 2.137199034], + [13, -4.849052953, -1.888876753, 4.66399375], + [14, -3.49182295, -0.66291331, 5.018510248], + [15, -5.020776528, 1.189745133, 2.805427194] + ] + }, + "types": { + "format": ["atom-id", "type"], + "data": [ + [1, "n"], + [2, "c2"], + [3, "c2"], + [4, "hw"], + [5, "hn"], + [6, "hc"], + [7, "hc"], + [8, "c2"], + [9, "c2"], + [10, "c_1"], + [11, "o_1"], + [12, "o*"], + [13, "hc"], + [14, "hc"], + [15, "hw"] + ] + }, + "molecules": { + "format": ["atom-id", "molecule-id"], + "data": [ + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1], + [7, 1], + [8, 1], + [9, 1], + [10, 1], + [11, 1], + [12, 1], + [13, 1], + [14, 1], + [15, 1] + ] + }, + "bonds": { + "format": ["bond-type", "atom1", "atom2"], + "data": [ + ["n-c2", 1, 2], + ["n-hn", 1, 5], + ["n-c_1", 1, 10], + ["c2-c2", 2, 3], + ["c2-hc", 2, 6], + ["c2-hc", 2, 7], + ["hw-o*", 4, 12], + ["c2-c2", 8, 9], + ["c2-c_1", 9, 10], + [ "c2-hc", 9, 13], + [ "c2-hc", 9, 14], + [ "c_1-o_1", 10, 11], + [ "hw-o*", 15, 12] + ] + }, + "angles": { + "format": ["angle-type", "atom1", "atom2", "atom3"], + "data": [ + ["c2-n-hn", 2, 1, 5], + ["c2-n-c_1", 2, 1, 10], + ["hn-n-c_1", 5, 1, 10], + ["n-c2-c2", 1, 2, 3], + ["n-c2-hc", 1, 2, 6], + ["n-c2-hc", 1, 2, 7], + ["c2-c2-hc", 3, 2, 6], + ["c2-c2-hc", 3, 2, 7], + ["hc-c2-hc", 6, 2, 7], + [ "c2-c2-c_1", 8, 9, 10], + [ "c2-c2-hc", 8, 9, 13], + [ "c2-c2-hc", 8, 9, 14], + [ "hc-c2-c_1", 13, 9, 10], + [ "hc-c2-c_1", 14, 9, 10], + [ "hc-c2-hc", 13, 9, 14], + [ "c2-c_1-o_1", 9, 10, 11], + [ "n-c_1-c2", 1, 10, 9], + [ "n-c_1-o_1", 1, 10, 11], + [ "hw-o*-hw", 15, 12, 4] + ] + }, + "dihedrals": { + "format": ["dihedral-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + ["hn-n-c2-c2", 5, 1, 2, 3], + ["hn-n-c2-hc", 5, 1, 2, 6], + ["hn-n-c2-hc", 5, 1, 2, 7], + ["c_1-n-c2-c2", 10, 1, 2, 3], + ["c_1-n-c2-hc", 10, 1, 2, 6], + ["c_1-n-c2-hc", 10, 1, 2, 7], + ["c2-n-c_1-c2", 2, 1, 10, 9], + ["c2-n-c_1-o_1", 2, 1, 10, 11], + ["hn-n-c_1-c2", 5, 1, 10, 9], + [ "hn-n-c_1-o_1", 5, 1, 10, 11], + [ "c2-c2-c_1-o_1", 8, 9, 10, 11], + [ "c2-c2-c_1-n", 8, 9, 10, 1], + [ "hc-c2-c_1-o_1", 13, 9, 10, 11], + [ "hc-c2-c_1-n", 13, 9, 10, 1], + [ "hc-c2-c_1-o_1", 14, 9, 10, 11], + [ "hc-c2-c_1-n", 14, 9, 10, 1] + ] + }, + "charges": { + "format": ["atom-id", "charge"], + "data": [ + [1, -0.60533], + [2, -0.01149], + [3, -0.76306], + [4, 0.38], + [5, 0.29346], + [6, 0.1836], + [7, 0.15396], + [8, -0.72636], + [9, -0.27437], + [10, 0.40603], + [11, -0.6553], + [12, -0.76], + [13, 0.21423], + [14, 0.18949], + [15, 0.38] + ] + }, + "impropers": { + "format": ["improper-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + ["c2-n-hn-c_1", 2, 1, 5, 10], + ["n-c_1-c2-o_1", 1, 10, 9, 11] + ] + } +} diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_unreacted.json b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_unreacted.json new file mode 100644 index 0000000000..1d7acaaedf --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/rxn1_stp2_unreacted.json @@ -0,0 +1,170 @@ +{ + "application": "LAMMPS", + "format": "molecule", + "revision": 1, + "schema": "https://download.lammps.org/json/molecule-schema.json", + "coords": { + "format": ["atom-id", "x", "y", "z"], + "data": [ + [1, -4.922858499, -0.946981747, 1.146055346], + [2, -5.047194816, -0.935266843, -0.358172771], + [3, -6.526281447, -0.755365854, -0.743523227], + [4, -5.282604074, 0.020446894, 1.552710361], + [5, -3.860696509, -1.09585019, 1.428304925], + [6, -4.662381862, -1.920899862, -0.781524026], + [7, -4.43397654, -0.072765142, -0.784070641], + [8, -5.506279186, 0.202610302, 4.825815562], + [9, -4.449176624, -0.844592213, 4.423366146], + [10, -4.103915981, -0.749628655, 2.925195217], + [11, -3.376248536, -1.886171498, 2.245643443], + [12, -4.49323543, 0.477213651, 2.137199034], + [13, -4.849052953, -1.888876753, 4.66399375], + [14, -3.49182295, -0.66291331, 5.018510248], + [15, -5.020776528, 1.189745133, 2.805427194] + ] + }, + "types": { + "format": ["atom-id", "type"], + "data": [ + [1, "n"], + [2, "c2"], + [3, "c2"], + [4, "hn"], + [5, "hn"], + [6, "hc"], + [7, "hc"], + [8, "c2"], + [9, "c2"], + [10, "c_1"], + [11, "o_1"], + [12, "o"], + [13, "hc"], + [14, "hc"], + [15, "ho"] + ] + }, + "molecules": { + "format": ["atom-id", "molecule-id"], + "data": [ + [1, 1], + [2, 1], + [3, 1], + [4, 1], + [5, 1], + [6, 1], + [7, 1], + [8, 1], + [9, 1], + [10, 1], + [11, 1], + [12, 1], + [13, 1], + [14, 1], + [15, 1] + ] + }, + "bonds": { + "format": ["bond-type", "atom1", "atom2"], + "data": [ + ["n-c2", 1, 2], + ["n-hn", 1, 4], + ["n-hn", 1, 5], + ["n-c_1", 1, 10], + ["c2-c2", 2, 3], + ["c2-hc", 2, 6], + ["c2-hc", 2, 7], + ["c2-c2", 8, 9], + ["c2-c_1", 9, 10], + ["c2-hc", 9, 13], + ["c2-hc", 9, 14], + ["c_1-o_1", 10, 11], + ["c_1-o", 10, 12], + ["o-ho", 12, 15] + ] + }, + "angles": { + "format": ["angle-type", "atom1", "atom2", "atom3"], + "data": [ + ["c2-n-hn", 2, 1, 4], + ["c2-n-hn", 2, 1, 5], + ["c2-n-c_1", 2, 1, 10], + ["hn-n-hn", 4, 1, 5], + ["hn-n-c_1", 4, 1, 10], + ["hn-n-c_1", 5, 1, 10], + ["n-c2-c2", 1, 2, 3], + ["n-c2-hc", 1, 2, 6], + ["n-c2-hc", 1, 2, 7], + ["c2-c2-hc", 3, 2, 6], + ["c2-c2-hc", 3, 2, 7], + ["hc-c2-hc", 6, 2, 7], + ["c2-c2-c_1", 8, 9, 10], + ["c2-c2-hc", 8, 9, 13], + ["c2-c2-hc", 8, 9, 14], + ["hc-c2-c_1", 13, 9, 10], + ["hc-c2-c_1", 14, 9, 10], + ["hc-c2-hc", 13, 9, 14], + ["c2-c_1-o_1", 9, 10, 11], + ["c2-c_1-o", 9, 10, 12], + ["n-c_1-c2", 1, 10, 9], + ["o_1-c_1-o", 11, 10, 12], + ["n-c_1-o_1", 1, 10, 11], + ["n-c_1-o", 1, 10, 12], + ["c_1-o-ho", 10, 12, 15] + ] + }, + "dihedrals": { + "format": ["dihedral-type", "atom1", "atom2", "atom3", "atom4"], + "data": [ + ["hn-n-c2-c2", 4, 1, 2, 3], + ["hn-n-c2-hc", 4, 1, 2, 6], + ["hn-n-c2-hc", 4, 1, 2, 7], + ["hn-n-c2-c2", 5, 1, 2, 3], + ["hn-n-c2-hc", 5, 1, 2, 6], + ["hn-n-c2-hc", 5, 1, 2, 7], + ["c_1-n-c2-c2", 10, 1, 2, 3], + ["c_1-n-c2-hc", 10, 1, 2, 6], + ["c_1-n-c2-hc", 10, 1, 2, 7], + ["c2-n-c_1-c2", 2, 1, 10, 9], + ["c2-n-c_1-o_1", 2, 1, 10, 11], + ["c2-n-c_1-o", 2, 1, 10, 12], + ["hn-n-c_1-c2", 4, 1, 10, 9], + ["hn-n-c_1-o_1", 4, 1, 10, 11], + ["hn-n-c_1-o", 4, 1, 10, 12], + ["hn-n-c_1-c2", 5, 1, 10, 9], + ["hn-n-c_1-o_1", 5, 1, 10, 11], + ["hn-n-c_1-o", 5, 1, 10, 12], + ["c2-c2-c_1-o_1", 8, 9, 10, 11], + ["c2-c2-c_1-o", 8, 9, 10, 12], + ["c2-c2-c_1-n", 8, 9, 10, 1], + ["hc-c2-c_1-o_1", 13, 9, 10, 11], + ["hc-c2-c_1-o", 13, 9, 10, 12], + ["hc-c2-c_1-n", 13, 9, 10, 1], + ["hc-c2-c_1-o_1", 14, 9, 10, 11], + ["hc-c2-c_1-o", 14, 9, 10, 12], + ["hc-c2-c_1-n", 14, 9, 10, 1], + ["c2-c_1-o-ho", 9, 10, 12, 15], + ["o_1-c_1-o-ho", 11, 10, 12, 15], + ["n-c_1-o-ho", 1, 10, 12, 15] + ] + }, + "charges": { + "format": ["atom-id", "charge"], + "data": [ + [1, -0.3], + [2, 0.0], + [3, 0.0], + [4, 0.0], + [5, 0.0], + [6, 0.0], + [7, 0.0], + [8, 0.0], + [9, 0.0], + [10, 0.3], + [11, 0.0], + [12, 0.0], + [13, 0.0], + [14, 0.0], + [15, 0.0] + ] + } +} diff --git a/examples/PACKAGES/reaction/tiny_nylon_json/tiny_nylon.data b/examples/PACKAGES/reaction/tiny_nylon_json/tiny_nylon.data new file mode 100644 index 0000000000..73c776c430 --- /dev/null +++ b/examples/PACKAGES/reaction/tiny_nylon_json/tiny_nylon.data @@ -0,0 +1,820 @@ +LAMMPS data file via write_data, version 27 Jun 2024, timestep = 0, units = real + +44 atoms +11 atom types +42 bonds +13 bond types +74 angles +26 angle types +100 dihedrals +33 dihedral types +16 impropers +5 improper types + +-25 25 xlo xhi +-25 25 ylo yhi +-25 25 zlo zhi + +Atom Type Labels + +1 c2 +2 c_1 +3 o +4 hc +5 ho +6 o_1 +7 na +8 hn +9 n +10 hw +11 o* + +Bond Type Labels + +1 c2-hc +2 c2-c2 +3 c_1-o +4 c2-c_1 +5 c_1-o_1 +6 o-ho +7 na-c2 +8 na-hn +9 n-c2 +10 n-hn +11 n-c_1 +12 c2-na +13 hw-o* + +Angle Type Labels + +1 hc-c2-hc +2 c2-c2-hc +3 c2-c2-c2 +4 c2-c_1-o_1 +5 o-c_1-o_1 +6 c2-c_1-o +7 c_1-o-ho +8 c2-c2-c_1 +9 c_1-c2-hc +10 c2-c2-na +11 na-c2-hc +12 c2-na-hn +13 hn-na-hn +14 c2-n-hn +15 c2-n-c_1 +16 hn-n-hn +17 hn-n-c_1 +18 n-c2-c2 +19 n-c2-hc +20 hc-c2-c_1 +21 n-c_1-c2 +22 o_1-c_1-o +23 n-c_1-o_1 +24 n-c_1-o +25 na-c2-c2 +26 hw-o*-hw + +Dihedral Type Labels + +1 c_1-c2-c2-hc +2 hc-c2-c2-hc +3 c2-c2-c2-c_1 +4 c2-c2-c2-hc +5 c2-c2-c2-c2 +6 c2-c_1-o-ho +7 o_1-c_1-o-ho +8 c2-c2-c_1-o +9 hc-c2-c_1-o +10 c2-c2-c_1-o_1 +11 hc-c2-c_1-o_1 +12 na-c2-c2-hc +13 c2-c2-c2-na +14 c2-c2-na-hn +15 hn-na-c2-hc +16 hn-n-c2-c2 +17 hn-n-c2-hc +18 c_1-n-c2-c2 +19 c_1-n-c2-hc +20 c2-n-c_1-c2 +21 c2-n-c_1-o_1 +22 c2-n-c_1-o +23 hn-n-c_1-c2 +24 hn-n-c_1-o_1 +25 hn-n-c_1-o +26 n-c2-c2-c2 +27 n-c2-c2-hc +28 c2-c2-c_1-n +29 hc-c2-c_1-n +30 n-c_1-o-ho +31 hn-na-c2-c2 +32 hc-c2-na-hn +33 na-c2-c2-c2 + +Improper Type Labels + +1 c2-c_1-o-o_1 +2 c2-na-hn-hn +3 c2-c_1-o_1-o +4 c2-n-hn-c_1 +5 n-c_1-c2-o_1 + +Masses + +1 12.0112 +2 12.0112 +3 15.9994 +4 1.00797 +5 1.00797 +6 15.9994 +7 14.0067 +8 1.00797 +9 14.0067 +10 1.00797 +11 15.9994 + +Pair Coeffs # lj/class2/coul/long + +1 0.054 4.01 +2 0.12 3.81 +3 0.24 3.535 +4 0.02 2.7 +5 0.013 1.098 +6 0.267 3.3 +7 0.065 4.07 +8 0.013 1.098 +9 0.106 4.07 +10 0.013 1.098 +11 0.26 3.61 + +Bond Coeffs # class2 + +1 1.101 345 -691.89 844.6 +2 1.53 299.67 -501.77 679.81 +3 1.3649 368.731 -832.478 1274.02 +4 1.5202 253.707 -423.037 396.9 +5 1.202 851.14 -1918.49 2160.77 +6 0.965 532.506 -1282.9 2004.77 +7 1.457 365.805 -699.637 998.484 +8 1.006 466.74 -1073.6 1251.11 +9 1.452 327.166 -547.899 526.5 +10 1.01 462.75 -1053.63 1545.76 +11 1.416 359.159 -558.473 1146.38 +12 1.457 365.805 -699.637 998.484 +13 0.97 563.28 -1428.22 1902.12 + +Angle Coeffs # class2 + +1 107.66 39.641 -12.921 -2.4318 +2 110.77 41.453 -10.604 5.129 +3 112.67 39.516 -7.443 -9.5583 +4 123.145 55.5431 -17.2123 0.1348 +5 118.986 98.6813 -22.2485 10.3673 +6 123.145 55.5431 -17.2123 0.1348 +7 111.254 53.5303 -11.8454 -11.5405 +8 108.53 51.9747 -9.4851 -10.9985 +9 107.734 40.6099 -28.8121 0 +10 111.91 60.7147 -13.3366 -13.0785 +11 110.62 51.3137 -6.7198 -2.6003 +12 110.954 50.8652 -4.4522 -10.0298 +13 107.067 45.252 -7.5558 -9.512 +14 113.868 45.9271 -20.0824 0 +15 111.037 31.8958 -6.6942 -6.837 +16 116.94 37.5749 -8.6676 0 +17 117.961 37.4964 -8.1837 0 +18 114.302 42.6589 -10.5464 -9.3243 +19 108.937 57.401 2.9374 0 +20 107.734 40.6099 -28.8121 0 +21 116.926 39.4193 -10.9945 -8.7733 +22 118.986 98.6813 -22.2485 10.3673 +23 125.542 92.572 -34.48 -11.1871 +24 125.542 92.572 -34.48 -11.1871 +25 111.91 60.7147 -13.3366 -13.0785 +26 103.7 49.84 -11.6 -8 + +BondBond Coeffs + +1 5.3316 1.101 1.101 +2 3.3872 1.53 1.101 +3 0 1.53 1.53 +4 0 1.5202 1.3649 +5 0 1.3649 1.202 +6 46.0685 1.5202 1.202 +7 0 1.3649 0.965 +8 5.4199 1.53 1.5202 +9 0.7115 1.5202 1.101 +10 4.6217 1.53 1.457 +11 12.426 1.457 1.101 +12 -6.4168 1.457 1.006 +13 -1.8749 1.006 1.006 +14 -3.471 1.452 1.01 +15 12.1186 1.452 1.416 +16 -0.5655 1.01 1.01 +17 -4.3126 1.01 1.416 +18 3.5446 1.452 1.53 +19 15.2994 1.452 1.101 +20 0.7115 1.101 1.5202 +21 0 1.416 1.5202 +22 0 1.202 1.3649 +23 138.495 1.416 1.202 +24 0 1.416 1.3649 +25 4.6217 1.457 1.53 +26 -9.5 0.97 0.97 + +BondAngle Coeffs + +1 18.103 18.103 1.101 1.101 +2 20.754 11.421 1.53 1.101 +3 8.016 8.016 1.53 1.53 +4 0 0 1.5202 1.3649 +5 0 0 1.3649 1.202 +6 34.9982 37.1298 1.5202 1.202 +7 0 0 1.3649 0.965 +8 18.1678 15.8758 1.53 1.5202 +9 12.4632 9.1765 1.5202 1.101 +10 6.0876 16.5702 1.53 1.457 +11 42.4332 13.4582 1.457 1.101 +12 31.8096 20.5799 1.457 1.006 +13 28.0322 28.0322 1.006 1.006 +14 11.8828 5.9339 1.452 1.01 +15 3.7812 14.8633 1.452 1.416 +16 19.8125 19.8125 1.01 1.01 +17 10.8422 29.5743 1.01 1.416 +18 4.6031 -5.479 1.452 1.53 +19 34.8907 10.6917 1.452 1.101 +20 9.1765 12.4632 1.101 1.5202 +21 0 0 1.416 1.5202 +22 0 0 1.202 1.3649 +23 62.7124 52.4045 1.416 1.202 +24 0 0 1.416 1.3649 +25 16.5702 6.0876 1.457 1.53 +26 22.35 22.35 0.97 0.97 + +Dihedral Coeffs # class2 + +1 -0.0228 0 0.028 0 -0.1863 0 +2 -0.1432 0 0.0617 0 -0.1083 0 +3 0.0972 0 0.0722 0 -0.2581 0 +4 0 0 0.0316 0 -0.1681 0 +5 0 0 0.0514 0 -0.143 0 +6 0 0 0 0 0 0 +7 -2.7332 0 2.9646 0 -0.0155 0 +8 0 0 0 0 0 0 +9 0 0 0 0 0 0 +10 0.0442 0 0.0292 0 0.0562 0 +11 -0.1804 0 0.0012 0 0.0371 0 +12 -0.2428 0 0.4065 0 -0.3079 0 +13 0.1764 0 0.1766 0 -0.5206 0 +14 -1.1506 0 -0.6344 0 -0.1845 0 +15 -0.5187 0 -0.4837 0 -0.1692 0 +16 -0.0483 0 -0.0077 0 -0.0014 0 +17 -0.0148 0 -0.0791 0 -0.0148 0 +18 0.0143 0 -0.0132 0 0.0091 0 +19 0.0219 0 -0.026 0 0.0714 0 +20 -0.7532 0 2.7392 0 0.0907 0 +21 0.8297 0 3.7234 0 -0.0495 0 +22 0 0 0 0 0 0 +23 0 0 0 0 0 0 +24 -1.6938 0 2.7386 0 -0.336 0 +25 0 0 0 0 0 0 +26 0.0972 0 0.0722 0 -0.2581 0 +27 -0.0228 0 0.028 0 -0.1863 0 +28 0.1693 0 -0.009 0 -0.0687 0 +29 0.1693 0 -0.009 0 -0.0687 0 +30 0 0 0 0 0 0 +31 -1.1506 0 -0.6344 0 -0.1845 0 +32 -0.5187 0 -0.4837 0 -0.1692 0 +33 0.1764 0 0.1766 0 -0.5206 0 + +AngleAngleTorsion Coeffs + +1 -5.3624 108.53 110.77 +2 -12.564 110.77 110.77 +3 -0.3801 112.67 108.53 +4 -16.164 112.67 110.77 +5 -22.045 112.67 112.67 +6 0 0 111.254 +7 0 118.985 111.254 +8 0 108.53 0 +9 0 107.734 0 +10 -8.019 108.53 123.145 +11 -15.3496 107.734 123.145 +12 -15.7572 111.91 110.77 +13 -27.3953 112.67 111.91 +14 -7.5499 111.91 110.954 +15 -10.4258 110.62 110.954 +16 -4.6337 113.868 114.302 +17 -6.659 113.868 108.937 +18 -7.4314 111.037 114.302 +19 -8.1335 111.037 108.937 +20 -6.5335 111.037 116.926 +21 -15.5547 111.037 125.542 +22 0 111.037 0 +23 -1.3234 117.961 116.926 +24 -7.3186 117.961 125.542 +25 0 117.961 0 +26 -1.0631 114.302 112.67 +27 -12.7974 114.302 110.77 +28 -5.4514 108.53 116.926 +29 -12.2417 107.734 116.926 +30 0 0 111.254 +31 -7.5499 110.954 111.91 +32 -10.4258 110.954 110.62 +33 -27.3953 111.91 112.67 + +EndBondTorsion Coeffs + +1 -0.0204 0.3628 -0.4426 -0.0097 -0.0315 -0.0755 1.5202 1.101 +2 0.213 0.312 0.0777 0.213 0.312 0.0777 1.101 1.101 +3 0.0062 -0.0002 0.0036 0.0055 0.006 -0.0009 1.53 1.5202 +4 0.2486 0.2422 -0.0925 0.0814 0.0591 0.2219 1.53 1.101 +5 -0.0732 0 0 -0.0732 0 0 1.53 1.53 +6 0 0 0 0 0 0 1.5202 0.965 +7 0 0 0 0 0 0 1.202 0.965 +8 0 0 0 0 0 0 1.53 1.3649 +9 0 0 0 0 0 0 1.101 1.3649 +10 0.2654 0.0503 0.1046 -0.281 0.0816 -0.1522 1.53 1.202 +11 1.2143 0.2831 0.3916 -0.2298 0.0354 0.3853 1.101 1.202 +12 0.1022 0.209 0.6433 0.196 0.7056 0.112 1.457 1.101 +13 0.1032 0.5896 -0.4836 0.0579 -0.0043 -0.1906 1.53 1.457 +14 -0.9466 0.9356 -0.5542 0.057 0.0625 0.4112 1.53 1.006 +15 -1.1685 0.9266 -0.0993 0.085 0.3061 0.2104 1.101 1.006 +16 -0.0992 -0.0727 -0.4139 0.132 0.0015 0.1324 1.01 1.53 +17 -0.4894 0.1644 0.3105 -0.8983 0.2826 0.0881 1.01 1.101 +18 -0.1245 -0.9369 0.7781 -0.2033 0.0035 0.056 1.416 1.53 +19 0.2292 1.1732 -0.058 -0.3667 0.8197 0.1335 1.416 1.101 +20 0.2299 -0.1141 -0.1424 0.0933 -0.4631 0.2883 1.452 1.5202 +21 0.1598 0.7253 -0.1007 0.1226 -2.1326 0.5581 1.452 1.202 +22 0 0 0 0 0 0 1.452 1.3649 +23 0.6413 0.1676 0.144 -0.6979 0.5619 0.4212 1.01 1.5202 +24 0.1214 0.1936 0.0816 -0.7604 -2.6431 1.2467 1.01 1.202 +25 0 0 0 0 0 0 1.01 1.3649 +26 -0.0797 -0.0406 0.0255 0.0742 0.0105 0.0518 1.452 1.53 +27 0.3022 0.2513 0.4641 -0.0601 -0.3763 -0.1876 1.452 1.101 +28 -0.2631 -0.0076 -0.1145 -0.2751 -0.3058 -0.1767 1.53 1.416 +29 -0.0268 0.7836 0.0035 0.3552 -0.2685 0.5834 1.101 1.416 +30 0 0 0 0 0 0 1.416 0.965 +31 0.057 0.0625 0.4112 -0.9466 0.9356 -0.5542 1.006 1.53 +32 0.085 0.3061 0.2104 -1.1685 0.9266 -0.0993 1.006 1.101 +33 0.0579 -0.0043 -0.1906 0.1032 0.5896 -0.4836 1.457 1.53 + +MiddleBondTorsion Coeffs + +1 -3.5039 1.2458 -0.761 1.53 +2 -14.261 -0.5322 -0.4864 1.53 +3 -1.5945 0.2267 -0.6911 1.53 +4 -14.879 -3.6581 -0.3138 1.53 +5 -17.787 -7.1877 0 1.53 +6 0 0 0 1.3649 +7 0 0 0 1.3649 +8 0 0 0 1.5202 +9 0 0 0 1.5202 +10 0.3388 -0.1096 0.1219 1.5202 +11 0.2359 0.9139 0.9594 1.5202 +12 -10.4959 -0.7647 -0.0545 1.53 +13 -15.4174 -7.3055 -1.0749 1.53 +14 -2.2208 0.5479 -0.3527 1.457 +15 -3.4611 1.6996 -0.6007 1.457 +16 -3.5406 -3.3866 0.0352 1.452 +17 -1.1752 2.8058 0.8083 1.452 +18 -3.9501 -0.4002 -0.6798 1.452 +19 -0.6899 -2.2646 1.1579 1.452 +20 0 0 0 1.416 +21 -8.8301 14.3079 -1.7716 1.416 +22 0 0 0 1.416 +23 0 0 0 1.416 +24 -0.9084 6.1447 -0.4852 1.416 +25 0 0 0 1.416 +26 -4.2324 -3.3023 -1.3244 1.53 +27 -4.1028 -0.5941 -0.047 1.53 +28 0 0 0 1.5202 +29 0 0 0 1.5202 +30 0 0 0 1.3649 +31 -2.2208 0.5479 -0.3527 1.457 +32 -3.4611 1.6996 -0.6007 1.457 +33 -15.4174 -7.3055 -1.0749 1.53 + +BondBond13 Coeffs + +1 0 1.5202 1.101 +2 0 1.101 1.101 +3 0 1.53 1.5202 +4 0 1.53 1.101 +5 0 1.53 1.53 +6 0 1.5202 0.965 +7 0 1.202 0.965 +8 0 1.53 1.3649 +9 0 1.101 1.3649 +10 0 1.53 1.202 +11 0 1.101 1.202 +12 0 1.457 1.101 +13 0 1.53 1.457 +14 0 1.53 1.006 +15 0 1.101 1.006 +16 0 1.01 1.53 +17 0 1.01 1.101 +18 0 1.416 1.53 +19 0 1.416 1.101 +20 0 1.452 1.5202 +21 0 1.452 1.202 +22 0 1.452 1.3649 +23 0 1.01 1.5202 +24 0 1.01 1.202 +25 0 1.01 1.3649 +26 0 1.452 1.53 +27 0 1.452 1.101 +28 0 1.53 1.416 +29 0 1.101 1.416 +30 0 1.416 0.965 +31 0 1.006 1.53 +32 0 1.006 1.101 +33 0 1.457 1.53 + +AngleTorsion Coeffs + +1 -0.7466 -0.9448 -0.6321 0.0162 1.4211 -1.4092 108.53 110.77 +2 -0.8085 0.5569 -0.2466 -0.8085 0.5569 -0.2466 110.77 110.77 +3 -0.2607 0.3203 -0.2283 0.0515 -0.0674 -0.0474 112.67 108.53 +4 -0.2454 0 -0.1136 0.3113 0.4516 -0.1988 112.67 110.77 +5 0.3886 -0.3139 0.1389 0.3886 -0.3139 0.1389 112.67 112.67 +6 0 0 0 0 0 0 0 111.254 +7 0 0 0 0 0 0 118.985 111.254 +8 0 0 0 0 0 0 108.53 0 +9 0 0 0 0 0 0 107.734 0 +10 0.0885 -1.3703 -0.5452 0.675 0.5965 0.6725 108.53 123.145 +11 9.1299 -0.4847 0.3582 -1.4946 0.7308 -0.2083 107.734 123.145 +12 -1.1075 0.282 0.8318 0.5111 1.6328 -1.0155 111.91 110.77 +13 -1.9225 -1.345 0.221 2.0125 0.944 -2.7612 112.67 111.91 +14 -3.343 4.4558 -0.0346 0.2873 -0.8072 -0.096 111.91 110.954 +15 -3.9582 2.0063 0.3213 -0.4294 -0.4442 -0.6141 110.62 110.954 +16 -0.5807 0.2041 -0.1384 -2.8967 2.7084 -0.0375 113.868 114.302 +17 -0.3868 0.2041 0.0445 -3.7022 1.3876 0.2393 113.868 108.937 +18 -1.523 1.1296 0.7167 -0.7555 0.0564 1.2177 111.037 114.302 +19 0.0372 -0.3418 -0.0775 -1.5157 2.0781 0.5364 111.037 108.937 +20 5.916 1.7856 0.4052 4.2133 2.9302 3.2903 111.037 116.926 +21 7.4427 2.1505 -0.2206 4.4466 4.0317 1.7129 111.037 125.542 +22 0 0 0 0 0 0 111.037 0 +23 1.9306 0.2105 0.0557 -2.2134 1.2909 0.9726 117.961 116.926 +24 2.3848 0.703 0.1399 -2.6238 0.3606 0.5474 117.961 125.542 +25 0 0 0 0 0 0 117.961 0 +26 0.2039 0.1602 -0.7946 -0.5501 -1.6982 0.2485 114.302 112.67 +27 -1.982 0.2325 -0.3928 -1.2469 1.6933 -1.2081 114.302 110.77 +28 2.1802 -0.0335 -1.3816 2.1221 0.5032 -0.0767 108.53 116.926 +29 7.095 0.0075 0.691 2.0013 0.5068 0.8406 107.734 116.926 +30 0 0 0 0 0 0 0 111.254 +31 0.2873 -0.8072 -0.096 -3.343 4.4558 -0.0346 110.954 111.91 +32 -0.4294 -0.4442 -0.6141 -3.9582 2.0063 0.3213 110.954 110.62 +33 2.0125 0.944 -2.7612 -1.9225 -1.345 0.221 111.91 112.67 + +Improper Coeffs # class2 + +1 0 0 +2 0 0 +3 0 0 +4 0 0 +5 24.3329 0 + +AngleAngle Coeffs + +1 0 0 0 0 118.985 123.145 +2 0.2738 -0.4825 0.2738 110.77 107.66 110.77 +3 -1.3199 -1.3199 0.1184 112.67 110.77 110.77 +4 2.0403 -1.8202 1.0827 108.53 107.734 110.77 +5 0 0 0 116.926 123.145 125.542 + +Atoms # full + +1 1 c2 -0.37128 12.288168 0.738732 4.37428 +2 1 c_1 0.77363 13.959928 -0.883144 5.090597 +3 1 o -0.68333 14.411288 -1.994419 5.68216 +4 1 hc 0.20049 12.881083 0.872503 3.506176 +5 1 hc 0.19609 11.232775 0.801641 3.998777 +6 1 ho 0.48328 13.704366 -2.470396 6.130105 +7 1 c2 -0.46692 12.489752 -0.793693 4.710639 +8 1 c2 -0.38182 12.455071 1.866388 5.38587 +9 1 c2 -0.47583 11.248961 1.901849 6.347664 +10 1 c_1 0.78608 10.005971 2.46671 5.77284 +11 1 o_1 -0.56991 14.79536 -0.034436 4.807367 +12 1 o_1 -0.58244 9.115239 1.654547 5.617002 +13 1 o -0.68598 9.745096 3.807654 5.573585 +14 1 hc 0.20664 12.248215 -1.371492 3.808598 +15 1 hc 0.21572 11.715755 -1.036825 5.500449 +16 1 hc 0.18182 12.559724 2.807687 4.858452 +17 1 hc 0.21541 13.299968 1.61657 6.123781 +18 1 hc 0.20587 11.650505 2.330454 7.28241 +19 1 hc 0.22795 10.88842 0.913219 6.637162 +20 1 ho 0.48600 10.550073 4.294209 5.758192 +21 2 c2 -0.38667 5.851425 1.929552 6.038335 +22 2 c2 -0.17544 6.741509 3.160751 6.233074 +23 2 na -0.79903 7.957761 3.12178 5.252257 +24 2 na -0.79111 2.599653 -2.25894 5.985863 +25 2 c2 -0.17576 3.834337 -1.907078 5.441528 +26 2 c2 -0.38091 4.810793 -1.083699 6.310184 +27 2 hc 0.19062 6.505912 1.182799 5.449104 +28 2 hc 0.18951 5.156429 2.256468 5.348423 +29 2 hc 0.17321 7.232782 3.178785 7.181911 +30 2 hc 0.16772 6.251671 4.103621 6.222913 +31 2 hn 0.33245 8.249909 4.070668 4.881297 +32 2 hn 0.34480 7.813025 2.623184 4.400744 +33 2 hn 0.33793 2.626695 -2.857547 6.817247 +34 2 hn 0.33306 1.955281 -2.684319 5.32846 +35 2 hc 0.17708 3.637708 -1.322842 4.469265 +36 2 hc 0.15806 4.41557 -2.739689 4.997336 +37 2 hc 0.18117 5.710714 -1.010014 5.642798 +38 2 hc 0.18224 5.103831 -1.696423 7.160345 +39 2 c2 -0.38394 5.270763 1.286629 7.308822 +40 2 hc 0.17720 4.834381 2.168531 7.931687 +41 2 hc 0.18425 6.118354 0.786724 7.794709 +42 2 c2 -0.37058 4.273849 0.167695 6.957862 +43 2 hc 0.18777 3.792544 -0.081782 7.904418 +44 2 hc 0.18490 3.527495 0.674238 6.348869 + +Velocities + +1 -0.002462698962621882 -0.0015920230003311222 -0.003062192778611524 +2 0.009508241670438584 -0.006990316616750725 0.013702671335945608 +3 0.0023431518493187576 -0.0029261683108242173 0.0014269399726982105 +4 -0.018184451408256214 0.03110380369168796 -0.013358827768357973 +5 0.026084132471017967 -0.010819576493517332 0.03040338445479488 +6 -0.004731211595821874 -0.01911146239947834 -0.03679335415649756 +7 -0.007506879759594987 0.006566142205596249 0.0013226575122695422 +8 0.003380788138016128 0.003045873266355709 0.0022368826795446284 +9 -0.0031113905793879316 0.008290886772075477 -0.001756123803949653 +10 0.0024685206571693056 0.001319477620984103 -0.002804187703280044 +11 -0.0034945605770565296 0.0032323777135621814 0.0016223017668450866 +12 -0.006115348361284778 -0.0051534857074262185 0.0017735747357354274 +13 0.0002138429678185901 -0.004539890294272967 0.006164976989441376 +14 0.00250046198643734 -0.0015709184283264888 0.020837548254667757 +15 0.006054793920564353 -0.012650704436910937 -0.005443075326696219 +16 -0.010374605775698001 0.009140865846388924 -0.011306875858287088 +17 -0.01273649912898741 -0.00917268118525065 0.005113650268546125 +18 0.007674177860704811 0.01862985663545928 -0.011300096447670932 +19 -0.01861613877528112 0.0010848388547730185 -0.005711843368779858 +20 0.005413757224147906 -0.014564578166395727 -0.01261842044190954 +21 0.005847352145231226 -0.004059528600033209 -0.0062517801580146415 +22 0.0036402033824753104 -0.0014629540504663154 -0.004003071231889805 +23 0.009026630501910769 -0.0027511425384659687 0.004557640256543714 +24 -0.013102302415548614 -0.004728670396530579 -0.0018966887841189517 +25 0.007862168262110317 -0.004204631354094957 0.000968879573747513 +26 -0.004738017643833797 0.009609044194077583 -0.008759243138703934 +27 0.005431165881163252 0.02003222466349599 -0.00949520764898085 +28 -0.0029056381493904374 0.0033317109723156875 0.016650350064426677 +29 -0.006456994403348912 0.002842398354195954 -0.026066912906505167 +30 -0.022173867823429387 0.014628839880961319 -0.02333083396140238 +31 0.009192571338198311 -0.0025697556639281928 -0.012822203161488303 +32 -0.00832069750519279 -0.0022538429924858707 0.007762024411858031 +33 0.019920685674825727 0.00503177648484941 -0.021106672824976403 +34 0.014118463330250982 0.017455545466840316 -0.012482101375598437 +35 -0.006111650564043797 0.013353021777303568 -0.025492434283827668 +36 0.009100152156585965 0.00557377745052224 0.014573768978939985 +37 0.0016523593470528035 -0.022107518020000917 0.020311423445130115 +38 -0.01034627539347186 0.01605585658635179 0.005548912701926242 +39 -0.0032054811383248638 0.0016779208962376315 0.002939050953753566 +40 0.019649219364916443 0.004081577652322286 -0.009842244116604127 +41 0.0005696169758816036 0.0007136113223474148 0.004633576422025626 +42 0.0022221300208006252 0.0036217319632558197 -0.006329939850345515 +43 0.002571017273484117 0.008002917981448292 0.01999298692846819 +44 -0.006082758182267466 -0.011834273655641976 0.020526923045885208 + +Bonds + +1 c2-hc 1 5 +2 c2-hc 1 4 +3 c2-c2 1 7 +4 c2-c2 1 8 +5 c_1-o 2 3 +6 c_1-o_1 2 11 +7 o-ho 3 6 +8 c2-c_1 7 2 +9 c2-hc 7 14 +10 c2-hc 7 15 +11 c2-c2 8 9 +12 c2-hc 8 16 +13 c2-hc 8 17 +14 c2-c_1 9 10 +15 c2-hc 9 18 +16 c2-hc 9 19 +17 c_1-o_1 10 12 +18 c_1-o 10 13 +19 o-ho 13 20 +20 c2-c2 21 22 +21 c2-hc 21 27 +22 c2-hc 21 28 +23 c2-c2 21 39 +24 na-c2 22 23 +25 c2-hc 22 29 +26 c2-hc 22 30 +27 na-hn 23 31 +28 na-hn 23 32 +29 na-hn 24 33 +30 na-hn 24 34 +31 na-c2 25 24 +32 c2-c2 25 26 +33 c2-hc 25 35 +34 c2-hc 25 36 +35 c2-hc 26 37 +36 c2-hc 26 38 +37 c2-c2 26 42 +38 c2-hc 39 40 +39 c2-hc 39 41 +40 c2-c2 39 42 +41 c2-hc 42 43 +42 c2-hc 42 44 + +Angles + +1 hc-c2-hc 5 1 4 +2 c2-c2-hc 7 1 5 +3 c2-c2-hc 8 1 5 +4 c2-c2-hc 7 1 4 +5 c2-c2-hc 8 1 4 +6 c2-c2-c2 7 1 8 +7 c2-c_1-o_1 7 2 3 +8 o-c_1-o_1 3 2 11 +9 c2-c_1-o 7 2 11 +10 c_1-o-ho 2 3 6 +11 c2-c2-c_1 1 7 2 +12 c2-c2-hc 1 7 14 +13 c2-c2-hc 1 7 15 +14 c_1-c2-hc 2 7 14 +15 c_1-c2-hc 2 7 15 +16 hc-c2-hc 14 7 15 +17 c2-c2-c2 1 8 9 +18 c2-c2-hc 1 8 16 +19 c2-c2-hc 1 8 17 +20 c2-c2-hc 9 8 16 +21 c2-c2-hc 9 8 17 +22 hc-c2-hc 16 8 17 +23 c2-c2-c_1 8 9 10 +24 c2-c2-hc 8 9 18 +25 c2-c2-hc 8 9 19 +26 c_1-c2-hc 10 9 18 +27 c_1-c2-hc 10 9 19 +28 hc-c2-hc 18 9 19 +29 c2-c_1-o 9 10 12 +30 c2-c_1-o_1 9 10 13 +31 o-c_1-o_1 13 10 12 +32 c_1-o-ho 10 13 20 +33 c2-c2-hc 22 21 27 +34 c2-c2-hc 22 21 28 +35 c2-c2-c2 22 21 39 +36 hc-c2-hc 27 21 28 +37 c2-c2-hc 39 21 27 +38 c2-c2-hc 39 21 28 +39 c2-c2-na 21 22 23 +40 c2-c2-hc 21 22 29 +41 c2-c2-hc 21 22 30 +42 na-c2-hc 23 22 29 +43 na-c2-hc 23 22 30 +44 hc-c2-hc 29 22 30 +45 c2-na-hn 22 23 31 +46 c2-na-hn 22 23 32 +47 hn-na-hn 31 23 32 +48 c2-na-hn 25 24 33 +49 c2-na-hn 25 24 34 +50 hn-na-hn 33 24 34 +51 c2-c2-na 26 25 24 +52 na-c2-hc 24 25 35 +53 na-c2-hc 24 25 36 +54 c2-c2-hc 26 25 35 +55 c2-c2-hc 26 25 36 +56 hc-c2-hc 35 25 36 +57 c2-c2-hc 25 26 37 +58 c2-c2-hc 25 26 38 +59 c2-c2-c2 25 26 42 +60 hc-c2-hc 37 26 38 +61 c2-c2-hc 42 26 37 +62 c2-c2-hc 42 26 38 +63 c2-c2-hc 21 39 40 +64 c2-c2-hc 21 39 41 +65 c2-c2-c2 21 39 42 +66 hc-c2-hc 40 39 41 +67 c2-c2-hc 42 39 40 +68 c2-c2-hc 42 39 41 +69 c2-c2-c2 26 42 39 +70 c2-c2-hc 26 42 43 +71 c2-c2-hc 26 42 44 +72 c2-c2-hc 39 42 43 +73 c2-c2-hc 39 42 44 +74 hc-c2-hc 43 42 44 + +Dihedrals + +1 hc-c2-c2-hc 5 1 7 14 +2 hc-c2-c2-hc 5 1 7 15 +3 hc-c2-c2-hc 4 1 7 14 +4 hc-c2-c2-hc 4 1 7 15 +5 c2-c2-c2-c_1 8 1 7 2 +6 c2-c2-c2-hc 8 1 7 14 +7 c2-c2-c2-hc 8 1 7 15 +8 hc-c2-c2-hc 5 1 8 16 +9 hc-c2-c2-hc 5 1 8 17 +10 hc-c2-c2-hc 4 1 8 16 +11 hc-c2-c2-hc 4 1 8 17 +12 c2-c2-c2-c2 7 1 8 9 +13 c2-c2-c2-hc 7 1 8 16 +14 c2-c2-c2-hc 7 1 8 17 +15 c2-c_1-o-ho 7 2 3 6 +16 o_1-c_1-o-ho 11 2 3 6 +17 c_1-c2-c2-hc 2 7 1 5 +18 c_1-c2-c2-hc 2 7 1 4 +19 c2-c2-c_1-o 1 7 2 3 +20 hc-c2-c_1-o 14 7 2 3 +21 hc-c2-c_1-o 15 7 2 3 +22 c2-c2-c_1-o_1 1 7 2 11 +23 hc-c2-c_1-o_1 14 7 2 11 +24 hc-c2-c_1-o_1 15 7 2 11 +25 c2-c2-c2-hc 9 8 1 5 +26 c2-c2-c2-hc 9 8 1 4 +27 c2-c2-c2-c_1 1 8 9 10 +28 c2-c2-c2-hc 1 8 9 18 +29 c2-c2-c2-hc 1 8 9 19 +30 hc-c2-c2-hc 16 8 9 18 +31 hc-c2-c2-hc 16 8 9 19 +32 hc-c2-c2-hc 17 8 9 18 +33 hc-c2-c2-hc 17 8 9 19 +34 c_1-c2-c2-hc 10 9 8 16 +35 c_1-c2-c2-hc 10 9 8 17 +36 c2-c2-c_1-o_1 8 9 10 12 +37 c2-c2-c_1-o 8 9 10 13 +38 hc-c2-c_1-o_1 18 9 10 12 +39 hc-c2-c_1-o 18 9 10 13 +40 hc-c2-c_1-o_1 19 9 10 12 +41 hc-c2-c_1-o 19 9 10 13 +42 c2-c_1-o-ho 9 10 13 20 +43 o_1-c_1-o-ho 12 10 13 20 +44 hc-c2-c2-hc 27 21 22 29 +45 hc-c2-c2-hc 27 21 22 30 +46 hc-c2-c2-hc 28 21 22 29 +47 hc-c2-c2-hc 28 21 22 30 +48 c2-c2-c2-na 39 21 22 23 +49 c2-c2-c2-hc 39 21 22 29 +50 c2-c2-c2-hc 39 21 22 30 +51 c2-c2-c2-hc 22 21 39 40 +52 c2-c2-c2-hc 22 21 39 41 +53 c2-c2-c2-c2 22 21 39 42 +54 hc-c2-c2-hc 27 21 39 40 +55 hc-c2-c2-hc 27 21 39 41 +56 hc-c2-c2-hc 28 21 39 40 +57 hc-c2-c2-hc 28 21 39 41 +58 na-c2-c2-hc 23 22 21 27 +59 na-c2-c2-hc 23 22 21 28 +60 c2-c2-na-hn 21 22 23 31 +61 c2-c2-na-hn 21 22 23 32 +62 hn-na-c2-hc 29 22 23 31 +63 hn-na-c2-hc 29 22 23 32 +64 hn-na-c2-hc 30 22 23 31 +65 hn-na-c2-hc 30 22 23 32 +66 c2-c2-na-hn 26 25 24 33 +67 hn-na-c2-hc 35 25 24 33 +68 hn-na-c2-hc 36 25 24 33 +69 c2-c2-na-hn 26 25 24 34 +70 hn-na-c2-hc 35 25 24 34 +71 hn-na-c2-hc 36 25 24 34 +72 na-c2-c2-hc 24 25 26 37 +73 na-c2-c2-hc 24 25 26 38 +74 hc-c2-c2-hc 35 25 26 37 +75 hc-c2-c2-hc 35 25 26 38 +76 hc-c2-c2-hc 36 25 26 37 +77 hc-c2-c2-hc 36 25 26 38 +78 c2-c2-c2-na 42 26 25 24 +79 c2-c2-c2-hc 42 26 25 35 +80 c2-c2-c2-hc 42 26 25 36 +81 c2-c2-c2-c2 25 26 42 39 +82 c2-c2-c2-hc 25 26 42 43 +83 c2-c2-c2-hc 25 26 42 44 +84 hc-c2-c2-hc 37 26 42 43 +85 hc-c2-c2-hc 37 26 42 44 +86 hc-c2-c2-hc 38 26 42 43 +87 hc-c2-c2-hc 38 26 42 44 +88 c2-c2-c2-hc 42 39 21 27 +89 c2-c2-c2-hc 42 39 21 28 +90 c2-c2-c2-c2 21 39 42 26 +91 c2-c2-c2-hc 21 39 42 43 +92 c2-c2-c2-hc 21 39 42 44 +93 hc-c2-c2-hc 40 39 42 43 +94 hc-c2-c2-hc 40 39 42 44 +95 hc-c2-c2-hc 41 39 42 43 +96 hc-c2-c2-hc 41 39 42 44 +97 c2-c2-c2-hc 39 42 26 37 +98 c2-c2-c2-hc 39 42 26 38 +99 c2-c2-c2-hc 26 42 39 40 +100 c2-c2-c2-hc 26 42 39 41 + +Impropers + +1 c2-na-hn-hn 7 1 4 5 +2 c2-na-hn-hn 8 1 4 5 +3 c2-c_1-o_1-o 7 1 8 5 +4 c2-c_1-o_1-o 7 1 8 4 +5 c2-c_1-o-o_1 7 2 3 11 +6 c2-n-hn-c_1 1 7 2 14 +7 c2-n-hn-c_1 1 7 2 15 +8 c2-na-hn-hn 1 7 14 15 +9 c2-c_1-o_1-o 1 8 9 16 +10 c2-c_1-o_1-o 1 8 9 17 +11 c2-na-hn-hn 1 8 16 17 +12 c2-na-hn-hn 9 8 16 17 +13 c2-n-hn-c_1 8 9 10 18 +14 c2-n-hn-c_1 8 9 10 19 +15 c2-na-hn-hn 8 9 18 19 +16 c2-c_1-o-o_1 9 10 13 12 diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 60cbd01d73..836e1353b0 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -94,7 +94,13 @@ add_library(zero2plugin MODULE zero2plugin.cpp pair_zero2.cpp bond_zero2.cpp angle_zero2.cpp dihedral_zero2.cpp improper_zero2.cpp) target_link_libraries(zero2plugin PRIVATE lammps) -set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES PREFIX "" SUFFIX ".so") +add_library(kspaceplugin MODULE kspaceplugin.cpp kspace_zero2.cpp) +target_link_libraries(kspaceplugin PRIVATE lammps) + +add_library(runminplugin MODULE runminplugin.cpp min_cg2.cpp verlet2.cpp) +target_link_libraries(runminplugin PRIVATE lammps) + +set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin kspaceplugin runminplugin PROPERTIES PREFIX "" SUFFIX ".so") # MacOS seems to need this if(CMAKE_SYSTEM_NAME STREQUAL Darwin) @@ -105,13 +111,13 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) if(CMAKE_CROSSCOMPILING) - set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin kspaceplugin runminplugin PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") endif() else() - set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin PROPERTIES + set_target_properties(morse2plugin nve2plugin helloplugin zero2plugin kspaceplugin runminplugin PROPERTIES LINK_FLAGS "-rdynamic") endif() add_custom_target(plugins ALL ${CMAKE_COMMAND} -E echo "Building Plugins" - DEPENDS morse2plugin nve2plugin helloplugin zero2plugin morse2plugin) + DEPENDS morse2plugin nve2plugin helloplugin zero2plugin morse2plugin kspaceplugin runminplugin) diff --git a/examples/plugins/kspace_zero2.cpp b/examples/plugins/kspace_zero2.cpp new file mode 100644 index 0000000000..6c2a63339c --- /dev/null +++ b/examples/plugins/kspace_zero2.cpp @@ -0,0 +1,112 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Axel Kohlmeyer (Temple U) +------------------------------------------------------------------------- */ + +#include "kspace_zero2.h" + +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "force.h" +#include "pair.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpaceZero2::KSpaceZero2(LAMMPS *lmp) : KSpace(lmp) +{ + ewaldflag = 1; + pppmflag = 1; + msmflag = 1; + dispersionflag = 1; + tip4pflag = 1; + dipoleflag = 1; + spinflag = 1; +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::settings(int narg, char **arg) +{ + if (narg != 1) error->all(FLERR, "Illegal kspace_style {} command", force->kspace_style); + + accuracy_relative = fabs(utils::numeric(FLERR, arg[0], false, lmp)); + if (accuracy_relative > 1.0) + error->all(FLERR, "Invalid relative accuracy {:g} for kspace_style {}", accuracy_relative, + force->kspace_style); + if ((narg != 0) && (narg != 1)) error->all(FLERR, "Illegal kspace_style command"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::init() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace initialization ...\n"); + + // error checks + + if (force->pair == nullptr) error->all(FLERR, "KSpace solver requires a pair style"); + if (!atom->q_flag) error->all(FLERR, "KSpace style zero2 requires atom attribute q"); + + // compute two charge force + + two_charge(); + + int itmp; + auto p_cutoff = (double *) force->pair->extract("cut_coul", itmp); + if (p_cutoff == nullptr) error->all(FLERR, "KSpace style is incompatible with Pair style"); + double cutoff = *p_cutoff; + + qsum_qsq(); + + accuracy = accuracy_relative * two_charge_force; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) { + if (accuracy <= 0.0) error->all(FLERR, "KSpace accuracy must be > 0"); + if (q2 == 0.0) error->all(FLERR, "Must use 'kspace_modify gewald' for uncharged system"); + g_ewald = accuracy * sqrt(atom->natoms * cutoff * domain->xprd * domain->yprd * domain->zprd) / + (2.0 * q2); + if (g_ewald >= 1.0) + g_ewald = (1.35 - 0.15 * log(accuracy)) / cutoff; + else + g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + if (comm->me == 0) utils::logmesg(lmp, " G vector (1/distance) = {:.8g}\n", g_ewald); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::setup() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace setup\n"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero2::compute(int eflag, int vflag) +{ + ev_init(eflag, vflag); +} diff --git a/examples/plugins/kspace_zero2.h b/examples/plugins/kspace_zero2.h new file mode 100644 index 0000000000..033c2caee5 --- /dev/null +++ b/examples/plugins/kspace_zero2.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/ Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_KSPACE_ZERO2_H +#define LMP_KSPACE_ZERO2_H + +#include "kspace.h" + +namespace LAMMPS_NS { + +class KSpaceZero2 : public KSpace { + public: + KSpaceZero2(class LAMMPS *); + + void init() override; + void setup() override; + void settings(int, char **) override; + + void compute(int, int) override; +}; +} // namespace LAMMPS_NS +#endif diff --git a/examples/plugins/kspaceplugin.cpp b/examples/plugins/kspaceplugin.cpp new file mode 100644 index 0000000000..4b93aa27e7 --- /dev/null +++ b/examples/plugins/kspaceplugin.cpp @@ -0,0 +1,34 @@ + +#include "lammpsplugin.h" + +#include "comm.h" +#include "command.h" +#include "error.h" +#include "version.h" + +#include + +#include "kspace_zero2.h" + +using namespace LAMMPS_NS; + +static KSpace *zero2creator(LAMMPS *lmp) +{ + KSpace *ptr = (KSpace *) new KSpaceZero2(lmp); + return ptr; +} + +extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) +{ + lammpsplugin_t plugin; + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + + plugin.version = LAMMPS_VERSION; + plugin.style = "kspace"; + plugin.name = "zero2"; + plugin.info = "zero2 KSpace style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &zero2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); +} diff --git a/examples/plugins/min_cg2.cpp b/examples/plugins/min_cg2.cpp new file mode 100644 index 0000000000..4b9d519d45 --- /dev/null +++ b/examples/plugins/min_cg2.cpp @@ -0,0 +1,192 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "min_cg2.h" + +#include "error.h" +#include "output.h" +#include "timer.h" +#include "update.h" + +#include + +using namespace LAMMPS_NS; + +// EPS_ENERGY = minimum normalization for energy tolerance + +static constexpr double EPS_ENERGY = 1.0e-8; + +/* ---------------------------------------------------------------------- */ + +MinCG2::MinCG2(LAMMPS *lmp) : MinLineSearch(lmp) {} + +/* ---------------------------------------------------------------------- + minimization via conjugate gradient iterations +------------------------------------------------------------------------- */ + +int MinCG2::iterate(int maxiter) +{ + int i,m,n,fail,ntimestep; + double beta,gg,dot[2],dotall[2],fdotf; + double *fatom,*gatom,*hatom; + + // nlimit = max # of CG iterations before restarting + // set to ndoftotal unless too big + + int nlimit = static_cast (MIN(MAXSMALLINT,ndoftotal)); + + // initialize working vectors + + for (i = 0; i < nvec; i++) h[i] = g[i] = fvec[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) hatom[i] = gatom[i] = fatom[i]; + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) hextra[i] = gextra[i] = fextra[i]; + + gg = fnorm_sqr(); + + for (int iter = 0; iter < maxiter; iter++) { + + if (timer->check_timeout(niter)) + return TIMEOUT; + + ntimestep = ++update->ntimestep; + niter++; + + // line minimization along direction h from current atom->x + + eprevious = ecurrent; + fail = (this->*linemin)(ecurrent,alpha_final); + if (fail) return fail; + + // function evaluation criterion + + if (neval >= update->max_eval) return MAXEVAL; + + // energy tolerance criterion + + if (fabs(ecurrent-eprevious) < + update->etol * 0.5*(fabs(ecurrent) + fabs(eprevious) + EPS_ENERGY)) + return ETOL; + + // force tolerance criterion + + dot[0] = dot[1] = 0.0; + for (i = 0; i < nvec; i++) { + dot[0] += fvec[i]*fvec[i]; + dot[1] += fvec[i]*g[i]; + } + + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) { + dot[0] += fatom[i]*fatom[i]; + dot[1] += fatom[i]*gatom[i]; + } + } + MPI_Allreduce(dot,dotall,2,MPI_DOUBLE,MPI_SUM,world); + if (nextra_global) + for (i = 0; i < nextra_global; i++) { + dotall[0] += fextra[i]*fextra[i]; + dotall[1] += fextra[i]*gextra[i]; + } + + fdotf = 0.0; + if (update->ftol > 0.0) { + if (normstyle == MAX) fdotf = fnorm_max(); // max force norm + else if (normstyle == INF) fdotf = fnorm_inf(); // infinite force norm + else if (normstyle == TWO) fdotf = dotall[0]; // same as fnorm_sqr(), Euclidean force 2-norm + else error->all(FLERR,"Illegal min_modify command"); + if (fdotf < update->ftol*update->ftol) return FTOL; + } + + // update new search direction h from new f = -Grad(x) and old g + // this is Polak-Ribieri formulation + // beta = dotall[0]/gg would be Fletcher-Reeves + // reinitialize CG every ndof iterations by setting beta = 0.0 + + beta = MAX(0.0,(dotall[0] - dotall[1])/gg); + if ((niter+1) % nlimit == 0) beta = 0.0; + gg = dotall[0]; + + for (i = 0; i < nvec; i++) { + g[i] = fvec[i]; + h[i] = g[i] + beta*h[i]; + } + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + fatom = fextra_atom[m]; + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) { + gatom[i] = fatom[i]; + hatom[i] = gatom[i] + beta*hatom[i]; + } + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) { + gextra[i] = fextra[i]; + hextra[i] = gextra[i] + beta*hextra[i]; + } + + // reinitialize CG if new search direction h is not downhill + + dot[0] = 0.0; + for (i = 0; i < nvec; i++) dot[0] += g[i]*h[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) dot[0] += gatom[i]*hatom[i]; + } + MPI_Allreduce(dot,dotall,1,MPI_DOUBLE,MPI_SUM,world); + if (nextra_global) + for (i = 0; i < nextra_global; i++) + dotall[0] += gextra[i]*hextra[i]; + + if (dotall[0] <= 0.0) { + for (i = 0; i < nvec; i++) h[i] = g[i]; + if (nextra_atom) + for (m = 0; m < nextra_atom; m++) { + gatom = gextra_atom[m]; + hatom = hextra_atom[m]; + n = extra_nlen[m]; + for (i = 0; i < n; i++) hatom[i] = gatom[i]; + } + if (nextra_global) + for (i = 0; i < nextra_global; i++) hextra[i] = gextra[i]; + } + + // output for thermo, dump, restart files + + if (output->next == ntimestep) { + timer->stamp(); + output->write(ntimestep); + timer->stamp(Timer::OUTPUT); + } + } + + return MAXITER; +} diff --git a/examples/plugins/min_cg2.h b/examples/plugins/min_cg2.h new file mode 100644 index 0000000000..f8c083b971 --- /dev/null +++ b/examples/plugins/min_cg2.h @@ -0,0 +1,29 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_MIN_CG2_H +#define LMP_MIN_CG2_H + +#include "min_linesearch.h" + +namespace LAMMPS_NS { + +class MinCG2 : public MinLineSearch { + public: + MinCG2(class LAMMPS *); + int iterate(int) override; +}; + +} // namespace LAMMPS_NS + +#endif diff --git a/examples/plugins/runminplugin.cpp b/examples/plugins/runminplugin.cpp new file mode 100644 index 0000000000..9f098d6aab --- /dev/null +++ b/examples/plugins/runminplugin.cpp @@ -0,0 +1,45 @@ + +#include "lammpsplugin.h" + +#include "version.h" + +#include + +#include "min_cg2.h" +#include "verlet2.h" + +using namespace LAMMPS_NS; + +static Min *min_cg2creator(LAMMPS *lmp) +{ + return new MinCG2(lmp); +} + +static Integrate *verlet2creator(LAMMPS *lmp, int argc, char **argv) +{ + return new Verlet2(lmp, argc, argv); +} + +extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) +{ + lammpsplugin_t plugin; + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + + plugin.version = LAMMPS_VERSION; + + plugin.style = "min"; + plugin.name = "cg2"; + plugin.info = "CG2 minimize style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &min_cg2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); + + plugin.style = "run"; + plugin.name = "verlet2"; + plugin.info = "Verlet2 run style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v2 = (lammpsplugin_factory2 *) &verlet2creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); +} diff --git a/examples/plugins/verlet2.cpp b/examples/plugins/verlet2.cpp new file mode 100644 index 0000000000..b88857645f --- /dev/null +++ b/examples/plugins/verlet2.cpp @@ -0,0 +1,426 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "verlet2.h" + +#include "angle.h" +#include "atom.h" +#include "atom_vec.h" +#include "bond.h" +#include "comm.h" +#include "dihedral.h" +#include "domain.h" +#include "error.h" +#include "fix.h" +#include "force.h" +#include "improper.h" +#include "kspace.h" +#include "modify.h" +#include "neighbor.h" +#include "output.h" +#include "pair.h" +#include "timer.h" +#include "update.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +Verlet2::Verlet2(LAMMPS *lmp, int narg, char **arg) : + Integrate(lmp, narg, arg) {} + +Verlet2::~Verlet2() +{ + // do nothing +} + +/* ---------------------------------------------------------------------- + initialization before run +------------------------------------------------------------------------- */ + +void Verlet2::init() +{ + Integrate::init(); + + // warn if no fixes doing time integration + + bool do_time_integrate = false; + for (const auto &fix : modify->get_fix_list()) + if (fix->time_integrate) do_time_integrate = true; + + if (!do_time_integrate && (comm->me == 0)) + error->warning(FLERR,"No fixes with time integration, atoms won't move" + utils::errorurl(28)); + + // virial_style: + // VIRIAL_PAIR if computed explicitly in pair via sum over pair interactions + // VIRIAL_FDOTR if computed implicitly in pair by + // virial_fdotr_compute() via sum over ghosts + + if (force->newton_pair) virial_style = VIRIAL_FDOTR; + else virial_style = VIRIAL_PAIR; + + // setup lists of computes for global and per-atom PE and pressure + + ev_setup(); + + // detect if fix omp is present for clearing force arrays + + if (modify->get_fix_by_id("package_omp")) external_force_clear = 1; + + // set flags for arrays to clear in force_clear() + + torqueflag = extraflag = 0; + if (atom->torque_flag) torqueflag = 1; + if (atom->avec->forceclearflag) extraflag = 1; + + // orthogonal vs triclinic simulation box + + triclinic = domain->triclinic; +} + +/* ---------------------------------------------------------------------- + setup before run +------------------------------------------------------------------------- */ + +void Verlet2::setup(int flag) +{ + if (comm->me == 0 && screen) { + fputs("Setting up Verlet2 run ...\n",screen); + if (flag) { + utils::print(screen," Unit style : {}\n" + " Current step : {}\n" + " Time step : {}\n", + update->unit_style,update->ntimestep,update->dt); + timer->print_timeout(screen); + } + } + + if (lmp->kokkos) + error->all(FLERR,"KOKKOS package requires run_style verlet2/kk"); + + update->setupflag = 1; + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + atom->setup(); + modify->setup_pre_exchange(); + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + if (atom->sortfreq > 0) atom->sort(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + domain->image_check(); + domain->box_too_small_check(); + modify->setup_pre_neighbor(); + neighbor->build(1); + modify->setup_post_neighbor(); + neighbor->ncalls = 0; + + // compute all forces + + force->setup(); + ev_set(update->ntimestep); + force_clear(); + modify->setup_pre_force(vflag); + + if (pair_compute_flag) force->pair->compute(eflag,vflag); + else if (force->pair) force->pair->compute_dummy(eflag,vflag); + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->kspace) { + force->kspace->setup(); + if (kspace_compute_flag) force->kspace->compute(eflag,vflag); + else force->kspace->compute_dummy(eflag,vflag); + } + + modify->setup_pre_reverse(eflag,vflag); + if (force->newton) comm->reverse_comm(); + + modify->setup(vflag); + output->setup(flag); + update->setupflag = 0; +} + +/* ---------------------------------------------------------------------- + setup without output + flag = 0 = just force calculation + flag = 1 = reneighbor and force calculation +------------------------------------------------------------------------- */ + +void Verlet2::setup_minimal(int flag) +{ + update->setupflag = 1; + + // setup domain, communication and neighboring + // acquire ghosts + // build neighbor lists + + if (flag) { + modify->setup_pre_exchange(); + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + comm->exchange(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + domain->image_check(); + domain->box_too_small_check(); + modify->setup_pre_neighbor(); + neighbor->build(1); + modify->setup_post_neighbor(); + neighbor->ncalls = 0; + } + + // compute all forces + + ev_set(update->ntimestep); + force_clear(); + modify->setup_pre_force(vflag); + + if (pair_compute_flag) force->pair->compute(eflag,vflag); + else if (force->pair) force->pair->compute_dummy(eflag,vflag); + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + } + + if (force->kspace) { + force->kspace->setup(); + if (kspace_compute_flag) force->kspace->compute(eflag,vflag); + else force->kspace->compute_dummy(eflag,vflag); + } + + modify->setup_pre_reverse(eflag,vflag); + if (force->newton) comm->reverse_comm(); + + modify->setup(vflag); + update->setupflag = 0; +} + +/* ---------------------------------------------------------------------- + run for N steps +------------------------------------------------------------------------- */ + +void Verlet2::run(int n) +{ + bigint ntimestep; + int nflag,sortflag; + + int n_post_integrate = modify->n_post_integrate; + int n_pre_exchange = modify->n_pre_exchange; + int n_pre_neighbor = modify->n_pre_neighbor; + int n_post_neighbor = modify->n_post_neighbor; + int n_pre_force = modify->n_pre_force; + int n_pre_reverse = modify->n_pre_reverse; + int n_post_force_any = modify->n_post_force_any; + int n_end_of_step = modify->n_end_of_step; + + if (atom->sortfreq > 0) sortflag = 1; + else sortflag = 0; + + for (int i = 0; i < n; i++) { + if (timer->check_timeout(i)) { + update->nsteps = i; + break; + } + + ntimestep = ++update->ntimestep; + ev_set(ntimestep); + + // initial time integration + + timer->stamp(); + modify->initial_integrate(vflag); + if (n_post_integrate) modify->post_integrate(); + timer->stamp(Timer::MODIFY); + + // regular communication vs neighbor list rebuild + + nflag = neighbor->decide(); + + if (nflag == 0) { + timer->stamp(); + comm->forward_comm(); + timer->stamp(Timer::COMM); + } else { + if (n_pre_exchange) { + timer->stamp(); + modify->pre_exchange(); + timer->stamp(Timer::MODIFY); + } + if (triclinic) domain->x2lamda(atom->nlocal); + domain->pbc(); + if (domain->box_change) { + domain->reset_box(); + comm->setup(); + if (neighbor->style) neighbor->setup_bins(); + } + timer->stamp(); + comm->exchange(); + if (sortflag && ntimestep >= atom->nextsort) atom->sort(); + comm->borders(); + if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + timer->stamp(Timer::COMM); + if (n_pre_neighbor) { + modify->pre_neighbor(); + timer->stamp(Timer::MODIFY); + } + neighbor->build(1); + timer->stamp(Timer::NEIGH); + if (n_post_neighbor) { + modify->post_neighbor(); + timer->stamp(Timer::MODIFY); + } + } + + // force computations + // important for pair to come before bonded contributions + // since some bonded potentials tally pairwise energy/virial + // and Pair:ev_tally() needs to be called before any tallying + + force_clear(); + + timer->stamp(); + + if (n_pre_force) { + modify->pre_force(vflag); + timer->stamp(Timer::MODIFY); + } + + if (pair_compute_flag) { + force->pair->compute(eflag,vflag); + timer->stamp(Timer::PAIR); + } + + if (atom->molecular != Atom::ATOMIC) { + if (force->bond) force->bond->compute(eflag,vflag); + if (force->angle) force->angle->compute(eflag,vflag); + if (force->dihedral) force->dihedral->compute(eflag,vflag); + if (force->improper) force->improper->compute(eflag,vflag); + timer->stamp(Timer::BOND); + } + + if (kspace_compute_flag) { + force->kspace->compute(eflag,vflag); + timer->stamp(Timer::KSPACE); + } + + if (n_pre_reverse) { + modify->pre_reverse(eflag,vflag); + timer->stamp(Timer::MODIFY); + } + + // reverse communication of forces + + if (force->newton) { + comm->reverse_comm(); + timer->stamp(Timer::COMM); + } + + // force modifications, final time integration, diagnostics + + if (n_post_force_any) modify->post_force(vflag); + modify->final_integrate(); + if (n_end_of_step) modify->end_of_step(); + timer->stamp(Timer::MODIFY); + + // all output + + if (ntimestep == output->next) { + timer->stamp(); + output->write(ntimestep); + timer->stamp(Timer::OUTPUT); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Verlet2::cleanup() +{ + modify->post_run(); + domain->box_too_small_check(); + update->update_time(); +} + +/* ---------------------------------------------------------------------- + clear force on own & ghost atoms + clear other arrays as needed +------------------------------------------------------------------------- */ + +void Verlet2::force_clear() +{ + size_t nbytes; + + if (external_force_clear) return; + + // clear force on all particles + // if either newton flag is set, also include ghosts + // when using threads always clear all forces. + + int nlocal = atom->nlocal; + + if (neighbor->includegroup == 0) { + nbytes = sizeof(double) * nlocal; + if (force->newton) nbytes += sizeof(double) * atom->nghost; + + if (nbytes) { + memset(&atom->f[0][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(0,nbytes); + } + + // neighbor includegroup flag is set + // clear force only on initial nfirst particles + // if either newton flag is set, also include ghosts + + } else { + nbytes = sizeof(double) * atom->nfirst; + + if (nbytes) { + memset(&atom->f[0][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(0,nbytes); + } + + if (force->newton) { + nbytes = sizeof(double) * atom->nghost; + + if (nbytes) { + memset(&atom->f[nlocal][0],0,3*nbytes); + if (torqueflag) memset(&atom->torque[nlocal][0],0,3*nbytes); + if (extraflag) atom->avec->force_clear(nlocal,nbytes); + } + } + } +} diff --git a/examples/plugins/verlet2.h b/examples/plugins/verlet2.h new file mode 100644 index 0000000000..ece4bd45d9 --- /dev/null +++ b/examples/plugins/verlet2.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_VERLET2_H +#define LMP_VERLET2_H + +#include "integrate.h" + +namespace LAMMPS_NS { + +class Verlet2 : public Integrate { + public: + Verlet2(class LAMMPS *, int, char **); + ~Verlet2() override; + void init() override; + void setup(int flag) override; + void setup_minimal(int) override; + void run(int) override; + void force_clear() override; + void cleanup() override; + + protected: + int triclinic; // 0 if domain is orthog, 1 if triclinic + int torqueflag, extraflag; +}; + +} // namespace LAMMPS_NS + +#endif diff --git a/examples/python/dump.1May25.python.wrap.g++.1 b/examples/python/dump.1May25.python.wrap.g++.1 new file mode 100644 index 0000000000..b294b70d67 --- /dev/null +++ b/examples/python/dump.1May25.python.wrap.g++.1 @@ -0,0 +1,234 @@ +ITEM: TIMESTEP +0 +ITEM: NUMBER OF ATOMS +108 +ITEM: BOX BOUNDS pp pp pp +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc +1 0 0 0 0 0 0 +2 0.839798 0.839798 0 0 0 0 +3 0.839798 0 0.839798 0 0 0 +4 0 0.839798 0.839798 0 0 0 +5 1.6796 0 0 1 0 0 +6 2.51939 0.839798 0 2 0 0 +7 2.51939 0 0.839798 2 0 0 +8 1.6796 0.839798 0.839798 1 0 0 +9 3.35919 0 0 3 0 0 +10 4.19899 0.839798 0 4 0 0 +11 4.19899 0 0.839798 4 0 0 +12 3.35919 0.839798 0.839798 3 0 0 +13 0 1.6796 0 0 1 0 +14 0.839798 2.51939 0 0 2 0 +15 0.839798 1.6796 0.839798 0 1 0 +16 0 2.51939 0.839798 0 2 0 +17 1.6796 1.6796 0 1 1 0 +18 2.51939 2.51939 0 2 2 0 +19 2.51939 1.6796 0.839798 2 1 0 +20 1.6796 2.51939 0.839798 1 2 0 +21 3.35919 1.6796 0 3 1 0 +22 4.19899 2.51939 0 4 2 0 +23 4.19899 1.6796 0.839798 4 1 0 +24 3.35919 2.51939 0.839798 3 2 0 +25 0 3.35919 0 0 3 0 +26 0.839798 4.19899 0 0 4 0 +27 0.839798 3.35919 0.839798 0 3 0 +28 0 4.19899 0.839798 0 4 0 +29 1.6796 3.35919 0 1 3 0 +30 2.51939 4.19899 0 2 4 0 +31 2.51939 3.35919 0.839798 2 3 0 +32 1.6796 4.19899 0.839798 1 4 0 +33 3.35919 3.35919 0 3 3 0 +34 4.19899 4.19899 0 4 4 0 +35 4.19899 3.35919 0.839798 4 3 0 +36 3.35919 4.19899 0.839798 3 4 0 +37 0 0 1.6796 0 0 1 +38 0.839798 0.839798 1.6796 0 0 1 +39 0.839798 0 2.51939 0 0 2 +40 0 0.839798 2.51939 0 0 2 +41 1.6796 0 1.6796 1 0 1 +42 2.51939 0.839798 1.6796 2 0 1 +43 2.51939 0 2.51939 2 0 2 +44 1.6796 0.839798 2.51939 1 0 2 +45 3.35919 0 1.6796 3 0 1 +46 4.19899 0.839798 1.6796 4 0 1 +47 4.19899 0 2.51939 4 0 2 +48 3.35919 0.839798 2.51939 3 0 2 +49 0 1.6796 1.6796 0 1 1 +50 0.839798 2.51939 1.6796 0 2 1 +51 0.839798 1.6796 2.51939 0 1 2 +52 0 2.51939 2.51939 0 2 2 +53 1.6796 1.6796 1.6796 1 1 1 +54 2.51939 2.51939 1.6796 2 2 1 +55 2.51939 1.6796 2.51939 2 1 2 +56 1.6796 2.51939 2.51939 1 2 2 +57 3.35919 1.6796 1.6796 3 1 1 +58 4.19899 2.51939 1.6796 4 2 1 +59 4.19899 1.6796 2.51939 4 1 2 +60 3.35919 2.51939 2.51939 3 2 2 +61 0 3.35919 1.6796 0 3 1 +62 0.839798 4.19899 1.6796 0 4 1 +63 0.839798 3.35919 2.51939 0 3 2 +64 0 4.19899 2.51939 0 4 2 +65 1.6796 3.35919 1.6796 1 3 1 +66 2.51939 4.19899 1.6796 2 4 1 +67 2.51939 3.35919 2.51939 2 3 2 +68 1.6796 4.19899 2.51939 1 4 2 +69 3.35919 3.35919 1.6796 3 3 1 +70 4.19899 4.19899 1.6796 4 4 1 +71 4.19899 3.35919 2.51939 4 3 2 +72 3.35919 4.19899 2.51939 3 4 2 +73 0 0 3.35919 0 0 3 +74 0.839798 0.839798 3.35919 0 0 3 +75 0.839798 0 4.19899 0 0 4 +76 0 0.839798 4.19899 0 0 4 +77 1.6796 0 3.35919 1 0 3 +78 2.51939 0.839798 3.35919 2 0 3 +79 2.51939 0 4.19899 2 0 4 +80 1.6796 0.839798 4.19899 1 0 4 +81 3.35919 0 3.35919 3 0 3 +82 4.19899 0.839798 3.35919 4 0 3 +83 4.19899 0 4.19899 4 0 4 +84 3.35919 0.839798 4.19899 3 0 4 +85 0 1.6796 3.35919 0 1 3 +86 0.839798 2.51939 3.35919 0 2 3 +87 0.839798 1.6796 4.19899 0 1 4 +88 0 2.51939 4.19899 0 2 4 +89 1.6796 1.6796 3.35919 1 1 3 +90 2.51939 2.51939 3.35919 2 2 3 +91 2.51939 1.6796 4.19899 2 1 4 +92 1.6796 2.51939 4.19899 1 2 4 +93 3.35919 1.6796 3.35919 3 1 3 +94 4.19899 2.51939 3.35919 4 2 3 +95 4.19899 1.6796 4.19899 4 1 4 +96 3.35919 2.51939 4.19899 3 2 4 +97 0 3.35919 3.35919 0 3 3 +98 0.839798 4.19899 3.35919 0 4 3 +99 0.839798 3.35919 4.19899 0 3 4 +100 0 4.19899 4.19899 0 4 4 +101 1.6796 3.35919 3.35919 1 3 3 +102 2.51939 4.19899 3.35919 2 4 3 +103 2.51939 3.35919 4.19899 2 3 4 +104 1.6796 4.19899 4.19899 1 4 4 +105 3.35919 3.35919 3.35919 3 3 3 +106 4.19899 4.19899 3.35919 4 4 3 +107 4.19899 3.35919 4.19899 4 3 4 +108 3.35919 4.19899 4.19899 3 4 4 +ITEM: TIMESTEP +100 +ITEM: NUMBER OF ATOMS +108 +ITEM: BOX BOUNDS pp pp pp +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc +1 4.97801 0.0605812 4.88474 4 0 4 +2 0.800305 0.813213 0.0576603 0 0 0 +3 0.6485 5.00484 0.836947 0 5 0 +4 5.01236 0.786108 0.77507 5 0 0 +5 1.50261 4.9277 4.9755 1 4 4 +6 2.46756 0.653742 5.0003 2 0 5 +7 2.55699 4.96491 0.926671 2 4 0 +8 1.62464 0.676882 0.777822 1 0 0 +9 3.57018 0.00324557 4.76943 3 0 4 +10 4.24909 0.985667 5.01613 4 0 5 +11 4.13124 0.178555 0.700886 4 0 0 +12 3.22537 0.963341 0.639686 3 0 0 +13 4.90815 1.75715 0.0826318 4 1 0 +14 1.00239 2.45421 0.0111309 1 2 0 +15 0.88902 1.62097 0.874547 0 1 0 +16 0.269512 2.41383 0.844638 0 2 0 +17 1.54513 1.51174 0.0869798 1 1 0 +18 2.0637 2.5991 0.0826392 2 2 0 +19 2.44634 1.71869 0.918144 2 1 0 +20 1.57997 2.52559 1.04361 1 2 1 +21 3.26063 2.10027 0.0160498 3 2 0 +22 4.21774 2.61172 0.0367453 4 2 0 +23 4.32215 1.8333 0.925663 4 1 0 +24 3.38151 2.6788 0.906258 3 2 0 +25 0.275459 3.26838 0.21932 0 3 0 +26 0.675354 4.14887 4.94054 0 4 4 +27 0.867201 3.35405 1.17655 0 3 1 +28 4.78788 4.34273 0.478103 4 4 0 +29 1.42014 3.50181 0.191388 1 3 0 +30 2.39935 4.14279 0.0726977 2 4 0 +31 2.68157 3.57418 0.921633 2 3 0 +32 1.77965 4.13041 0.925765 1 4 0 +33 3.10555 3.22911 5.00703 3 3 5 +34 4.24908 3.67507 4.83053 4 3 4 +35 4.2444 3.59428 0.837193 4 3 0 +36 3.52155 4.21032 0.423884 3 4 0 +37 0.00881412 4.90616 1.74242 0 4 1 +38 0.800033 0.812556 1.72553 0 0 1 +39 0.929754 0.0251285 2.53734 0 0 2 +40 0.0189933 0.897729 2.4272 0 0 2 +41 1.77025 0.0864618 1.69637 1 0 1 +42 2.6077 0.984882 1.7897 2 0 1 +43 2.55819 4.95626 2.45373 2 4 2 +44 1.77963 0.875522 2.5056 1 0 2 +45 3.50499 0.0189748 1.5186 3 0 1 +46 4.47856 0.849521 1.59138 4 0 1 +47 3.99 0.309367 2.42507 3 0 2 +48 3.06373 0.748905 2.87477 3 0 2 +49 0.149175 1.72252 1.81302 0 1 1 +50 0.963279 2.42412 1.76899 0 2 1 +51 0.879984 1.74899 2.55389 0 1 2 +52 0.211736 2.57456 2.48446 0 2 2 +53 1.75238 1.7433 1.79028 1 1 1 +54 2.61023 2.6802 1.55816 2 2 1 +55 2.55259 1.72904 2.60295 2 1 2 +56 1.7903 2.56204 2.41895 1 2 2 +57 3.43857 1.71819 1.54054 3 1 1 +58 4.20455 2.5331 1.87607 4 2 1 +59 4.12961 1.48438 2.40107 4 1 2 +60 3.36606 2.52455 2.67203 3 2 2 +61 0.0138563 3.23504 1.76147 0 3 1 +62 0.968224 4.42746 1.67526 0 4 1 +63 0.965745 3.43479 2.52674 0 3 2 +64 0.144542 4.27264 2.61793 0 4 2 +65 1.7741 3.32184 1.66819 1 3 1 +66 2.4878 4.29352 1.69069 2 4 1 +67 2.56992 3.44579 2.4534 2 3 2 +68 1.72356 4.2344 2.49376 1 4 2 +69 3.43363 3.36417 1.87451 3 3 1 +70 4.32623 4.1046 1.69102 4 4 1 +71 4.22927 3.23505 2.64208 4 3 2 +72 3.41086 4.28362 2.42553 3 4 2 +73 4.85349 0.0953155 3.14546 4 0 3 +74 0.833593 0.840282 3.27238 0 0 3 +75 0.578712 4.99093 3.85234 0 4 3 +76 0.119704 1.00404 4.09942 0 1 4 +77 1.78347 4.95586 3.31184 1 4 3 +78 2.34318 0.736887 3.54475 2 0 3 +79 2.34858 4.82316 4.28272 2 4 4 +80 1.55893 0.765128 4.31112 1 0 4 +81 3.13128 0.0257347 3.70936 3 0 3 +82 4.10851 0.877375 3.36388 4 0 3 +83 4.30691 4.69041 4.14081 4 4 4 +84 3.48471 1.07347 4.35888 3 1 4 +85 0.0668209 1.77975 3.29313 0 1 3 +86 0.894798 2.5043 3.34172 0 2 3 +87 0.743432 1.71961 4.3483 0 1 4 +88 0.135168 2.69084 4.12098 0 2 4 +89 1.73749 1.5259 3.46692 1 1 3 +90 2.47391 2.57508 3.27574 2 2 3 +91 2.4825 1.46064 4.38907 2 1 4 +92 1.67786 2.47309 4.03082 1 2 4 +93 3.34694 1.70248 3.44784 3 1 3 +94 4.20676 2.29697 3.23228 4 2 3 +95 4.31748 1.76411 4.20898 4 1 4 +96 3.1135 2.59262 4.08996 3 2 4 +97 0.0727406 3.44797 3.30247 0 3 3 +98 1.0752 4.05296 3.38036 1 4 3 +99 1.0085 3.35058 4.3598 1 3 4 +100 0.054939 4.00087 4.11831 0 4 4 +101 1.72259 3.24691 3.19685 1 3 3 +102 2.54324 4.21582 3.29642 2 4 3 +103 2.33008 3.38603 4.27885 2 3 4 +104 1.65617 4.10852 4.3491 1 4 4 +105 3.14646 3.43756 3.28105 3 3 3 +106 4.09781 4.10691 3.24439 4 4 3 +107 4.1529 3.22538 3.79605 4 3 3 +108 3.16394 3.96553 4.31023 3 3 4 diff --git a/examples/python/dump.1May25.python.wrap.g++.4 b/examples/python/dump.1May25.python.wrap.g++.4 new file mode 100644 index 0000000000..4f1ad1f2c8 --- /dev/null +++ b/examples/python/dump.1May25.python.wrap.g++.4 @@ -0,0 +1,234 @@ +ITEM: TIMESTEP +0 +ITEM: NUMBER OF ATOMS +108 +ITEM: BOX BOUNDS pp pp pp +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc +1 0 0 0 0 0 0 +2 0.839798 0.839798 0 0 0 0 +3 0.839798 0 0.839798 0 0 0 +4 0 0.839798 0.839798 0 0 0 +13 0 1.6796 0 0 1 0 +14 0.839798 1.6796 0.839798 0 1 0 +19 0 0 1.6796 0 0 1 +20 0.839798 0.839798 1.6796 0 0 1 +25 0 1.6796 1.6796 0 1 1 +5 1.6796 0 0 1 0 0 +6 2.51939 0.839798 0 2 0 0 +7 2.51939 0 0.839798 2 0 0 +8 1.6796 0.839798 0.839798 1 0 0 +15 1.6796 1.6796 0 1 1 0 +16 2.51939 1.6796 0.839798 2 1 0 +21 1.6796 0 1.6796 1 0 1 +22 2.51939 0.839798 1.6796 2 0 1 +26 1.6796 1.6796 1.6796 1 1 1 +9 3.35919 0 0 3 0 0 +10 4.19899 0.839798 0 4 0 0 +11 4.19899 0 0.839798 4 0 0 +12 3.35919 0.839798 0.839798 3 0 0 +17 3.35919 1.6796 0 3 1 0 +18 4.19899 1.6796 0.839798 4 1 0 +23 3.35919 0 1.6796 3 0 1 +24 4.19899 0.839798 1.6796 4 0 1 +27 3.35919 1.6796 1.6796 3 1 1 +28 0.839798 0 2.51939 0 0 2 +29 0 0.839798 2.51939 0 0 2 +34 0.839798 1.6796 2.51939 0 1 2 +37 0 0 3.35919 0 0 3 +38 0.839798 0.839798 3.35919 0 0 3 +39 0.839798 0 4.19899 0 0 4 +40 0 0.839798 4.19899 0 0 4 +49 0 1.6796 3.35919 0 1 3 +50 0.839798 1.6796 4.19899 0 1 4 +30 2.51939 0 2.51939 2 0 2 +31 1.6796 0.839798 2.51939 1 0 2 +35 2.51939 1.6796 2.51939 2 1 2 +41 1.6796 0 3.35919 1 0 3 +42 2.51939 0.839798 3.35919 2 0 3 +43 2.51939 0 4.19899 2 0 4 +44 1.6796 0.839798 4.19899 1 0 4 +51 1.6796 1.6796 3.35919 1 1 3 +52 2.51939 1.6796 4.19899 2 1 4 +32 4.19899 0 2.51939 4 0 2 +33 3.35919 0.839798 2.51939 3 0 2 +36 4.19899 1.6796 2.51939 4 1 2 +45 3.35919 0 3.35919 3 0 3 +46 4.19899 0.839798 3.35919 4 0 3 +47 4.19899 0 4.19899 4 0 4 +48 3.35919 0.839798 4.19899 3 0 4 +53 3.35919 1.6796 3.35919 3 1 3 +54 4.19899 1.6796 4.19899 4 1 4 +55 0.839798 2.51939 0 0 2 0 +56 0 2.51939 0.839798 0 2 0 +61 0 3.35919 0 0 3 0 +62 0.839798 4.19899 0 0 4 0 +63 0.839798 3.35919 0.839798 0 3 0 +64 0 4.19899 0.839798 0 4 0 +73 0.839798 2.51939 1.6796 0 2 1 +76 0 3.35919 1.6796 0 3 1 +77 0.839798 4.19899 1.6796 0 4 1 +57 2.51939 2.51939 0 2 2 0 +58 1.6796 2.51939 0.839798 1 2 0 +65 1.6796 3.35919 0 1 3 0 +66 2.51939 4.19899 0 2 4 0 +67 2.51939 3.35919 0.839798 2 3 0 +68 1.6796 4.19899 0.839798 1 4 0 +74 2.51939 2.51939 1.6796 2 2 1 +78 1.6796 3.35919 1.6796 1 3 1 +79 2.51939 4.19899 1.6796 2 4 1 +59 4.19899 2.51939 0 4 2 0 +60 3.35919 2.51939 0.839798 3 2 0 +69 3.35919 3.35919 0 3 3 0 +70 4.19899 4.19899 0 4 4 0 +71 4.19899 3.35919 0.839798 4 3 0 +72 3.35919 4.19899 0.839798 3 4 0 +75 4.19899 2.51939 1.6796 4 2 1 +80 3.35919 3.35919 1.6796 3 3 1 +81 4.19899 4.19899 1.6796 4 4 1 +82 0 2.51939 2.51939 0 2 2 +85 0.839798 3.35919 2.51939 0 3 2 +86 0 4.19899 2.51939 0 4 2 +91 0.839798 2.51939 3.35919 0 2 3 +92 0 2.51939 4.19899 0 2 4 +97 0 3.35919 3.35919 0 3 3 +98 0.839798 4.19899 3.35919 0 4 3 +99 0.839798 3.35919 4.19899 0 3 4 +100 0 4.19899 4.19899 0 4 4 +83 1.6796 2.51939 2.51939 1 2 2 +87 2.51939 3.35919 2.51939 2 3 2 +88 1.6796 4.19899 2.51939 1 4 2 +93 2.51939 2.51939 3.35919 2 2 3 +94 1.6796 2.51939 4.19899 1 2 4 +101 1.6796 3.35919 3.35919 1 3 3 +102 2.51939 4.19899 3.35919 2 4 3 +103 2.51939 3.35919 4.19899 2 3 4 +104 1.6796 4.19899 4.19899 1 4 4 +84 3.35919 2.51939 2.51939 3 2 2 +89 4.19899 3.35919 2.51939 4 3 2 +90 3.35919 4.19899 2.51939 3 4 2 +95 4.19899 2.51939 3.35919 4 2 3 +96 3.35919 2.51939 4.19899 3 2 4 +105 3.35919 3.35919 3.35919 3 3 3 +106 4.19899 4.19899 3.35919 4 4 3 +107 4.19899 3.35919 4.19899 4 3 4 +108 3.35919 4.19899 4.19899 3 4 4 +ITEM: TIMESTEP +100 +ITEM: NUMBER OF ATOMS +108 +ITEM: BOX BOUNDS pp pp pp +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +0.0000000000000000e+00 5.0387885741475218e+00 +ITEM: ATOMS id x y z v_xtrunc v_ytrunc v_ztrunc +27 3.43857 1.71819 1.54054 3 1 1 +55 1.00239 2.45421 0.0111309 1 2 0 +24 4.47856 0.849521 1.59138 4 0 1 +4 5.01236 0.786108 0.77507 5 0 0 +13 4.90815 1.75715 0.0826318 4 1 0 +14 0.88902 1.62097 0.874547 0 1 0 +23 3.50499 0.0189748 1.5186 3 0 1 +20 0.800033 0.812556 1.72553 0 0 1 +25 0.149175 1.72252 1.81302 0 1 1 +18 4.32215 1.8333 0.925663 4 1 0 +2 0.800305 0.813213 0.0576603 0 0 0 +11 4.13124 0.178555 0.700886 4 0 0 +8 1.62464 0.676882 0.777822 1 0 0 +15 1.54513 1.51174 0.0869798 1 1 0 +16 2.44634 1.71869 0.918144 2 1 0 +21 1.77025 0.0864618 1.69637 1 0 1 +22 2.6077 0.984882 1.7897 2 0 1 +26 1.75238 1.7433 1.79028 1 1 1 +56 0.269512 2.41383 0.844638 0 2 0 +73 0.963279 2.42412 1.76899 0 2 1 +12 3.22537 0.963341 0.639686 3 0 0 +29 0.0189933 0.897729 2.4272 0 0 2 +36 4.12961 1.48438 2.40107 4 1 2 +31 1.77963 0.875522 2.5056 1 0 2 +17 3.26063 2.10027 0.0160498 3 2 0 +32 3.99 0.309367 2.42507 3 0 2 +10 4.24909 0.985667 5.01613 4 0 5 +9 3.57018 0.00324557 4.76943 3 0 4 +34 0.879984 1.74899 2.55389 0 1 2 +54 4.31748 1.76411 4.20898 4 1 4 +38 0.833593 0.840282 3.27238 0 0 3 +53 3.34694 1.70248 3.44784 3 1 3 +40 0.119704 1.00404 4.09942 0 1 4 +49 0.0668209 1.77975 3.29313 0 1 3 +50 0.743432 1.71961 4.3483 0 1 4 +1 4.97801 0.0605812 4.88474 4 0 4 +28 0.929754 0.0251285 2.53734 0 0 2 +94 1.67786 2.47309 4.03082 1 2 4 +48 3.48471 1.07347 4.35888 3 1 4 +42 2.34318 0.736887 3.54475 2 0 3 +46 4.10851 0.877375 3.36388 4 0 3 +44 1.55893 0.765128 4.31112 1 0 4 +51 1.73749 1.5259 3.46692 1 1 3 +52 2.4825 1.46064 4.38907 2 1 4 +45 3.13128 0.0257347 3.70936 3 0 3 +33 3.06373 0.748905 2.87477 3 0 2 +37 4.85349 0.0953155 3.14546 4 0 3 +95 4.20676 2.29697 3.23228 4 2 3 +91 0.894798 2.5043 3.34172 0 2 3 +6 2.46756 0.653742 5.0003 2 0 5 +35 2.55259 1.72904 2.60295 2 1 2 +75 4.20455 2.5331 1.87607 4 2 1 +83 1.7903 2.56204 2.41895 1 2 2 +90 3.41086 4.28362 2.42553 3 4 2 +7 2.55699 4.96491 0.926671 2 4 0 +63 0.867201 3.35405 1.17655 0 3 1 +64 4.78788 4.34273 0.478103 4 4 0 +81 4.32623 4.1046 1.69102 4 4 1 +76 0.0138563 3.23504 1.76147 0 3 1 +77 0.968224 4.42746 1.67526 0 4 1 +57 2.0637 2.5991 0.0826392 2 2 0 +80 3.43363 3.36417 1.87451 3 3 1 +65 1.42014 3.50181 0.191388 1 3 0 +58 1.57997 2.52559 1.04361 1 2 1 +67 2.68157 3.57418 0.921633 2 3 0 +68 1.77965 4.13041 0.925765 1 4 0 +74 2.61023 2.6802 1.55816 2 2 1 +78 1.7741 3.32184 1.66819 1 3 1 +79 2.4878 4.29352 1.69069 2 4 1 +59 4.21774 2.61172 0.0367453 4 2 0 +72 3.52155 4.21032 0.423884 3 4 0 +19 0.00881412 4.90616 1.74242 0 4 1 +71 4.2444 3.59428 0.837193 4 3 0 +60 3.38151 2.6788 0.906258 3 2 0 +88 1.72356 4.2344 2.49376 1 4 2 +87 2.56992 3.44579 2.4534 2 3 2 +61 0.275459 3.26838 0.21932 0 3 0 +3 0.6485 5.00484 0.836947 0 5 0 +66 2.39935 4.14279 0.0726977 2 4 0 +82 0.211736 2.57456 2.48446 0 2 2 +30 2.55819 4.95626 2.45373 2 4 2 +108 3.16394 3.96553 4.31023 3 3 4 +62 0.675354 4.14887 4.94054 0 4 4 +47 4.30691 4.69041 4.14081 4 4 4 +107 4.1529 3.22538 3.79605 4 3 3 +92 0.135168 2.69084 4.12098 0 2 4 +97 0.0727406 3.44797 3.30247 0 3 3 +98 1.0752 4.05296 3.38036 1 4 3 +99 1.0085 3.35058 4.3598 1 3 4 +100 0.054939 4.00087 4.11831 0 4 4 +106 4.09781 4.10691 3.24439 4 4 3 +70 4.24908 3.67507 4.83053 4 3 4 +43 2.34858 4.82316 4.28272 2 4 4 +105 3.14646 3.43756 3.28105 3 3 3 +96 3.1135 2.59262 4.08996 3 2 4 +101 1.72259 3.24691 3.19685 1 3 3 +102 2.54324 4.21582 3.29642 2 4 3 +103 2.33008 3.38603 4.27885 2 3 4 +104 1.65617 4.10852 4.3491 1 4 4 +84 3.36606 2.52455 2.67203 3 2 2 +89 4.22927 3.23505 2.64208 4 3 2 +41 1.78347 4.95586 3.31184 1 4 3 +93 2.47391 2.57508 3.27574 2 2 3 +39 0.578712 4.99093 3.85234 0 4 3 +86 0.144542 4.27264 2.61793 0 4 2 +5 1.50261 4.9277 4.9755 1 4 4 +69 3.10555 3.22911 5.00703 3 3 5 +85 0.965745 3.43479 2.52674 0 3 2 diff --git a/examples/python/in.python.wrap b/examples/python/in.python.wrap new file mode 100644 index 0000000000..47a2d3a572 --- /dev/null +++ b/examples/python/in.python.wrap @@ -0,0 +1,60 @@ +# 3d Lennard-Jones melt with equal- and atom-style variables which +# use a Python function wrapper in their formulas + +variable x index 3 +variable y index 3 +variable z index 3 + +units lj +atom_style atomic + +lattice fcc 0.8442 +region box block 0 $x 0 $y 0 $z +create_box 1 box +create_atoms 1 box +mass 1 1.0 + +velocity all create 1.44 87287 loop geom + +pair_style lj/cut 2.5 +pair_coeff 1 1 1.0 1.0 2.5 + +neighbor 0.3 bin +neigh_modify delay 0 every 20 check no + +fix 1 all nve + +# define Python truncate() function + +variable foo python truncate +python truncate return v_foo input 1 iv_arg format fi here """ +def truncate(x): + return int(x) +""" + +# use in equal-style variable + +variable scalar equal py_foo(4.5) +print "TRUNCATE ${scalar}" + +# use in atom-style variable +# examine dump file to see truncated xyz coords of each atom + +variable xtrunc atom py_foo(x) +variable ytrunc atom py_foo(y) +variable ztrunc atom py_foo(z) + +dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc + +# use in vector-style variable + +compute ke all temp +variable ke vector c_ke +variable ketrunc vector py_foo(v_ke) +thermo_style custom step temp epair v_ketrunc[*6] + +run 100 + +print "KE TENSOR ${ketrunc}" + + diff --git a/examples/python/log.1May25.python.wrap.g++.1 b/examples/python/log.1May25.python.wrap.g++.1 new file mode 100644 index 0000000000..3a7dd1c7b2 --- /dev/null +++ b/examples/python/log.1May25.python.wrap.g++.1 @@ -0,0 +1,116 @@ +LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-270-g2351418c94-modified) +# 3d Lennard-Jones melt with equal- and atom-style variables which +# use a Python function wrapper in their formulas + +variable x index 3 +variable y index 3 +variable z index 3 + +units lj +atom_style atomic + +lattice fcc 0.8442 +Lattice spacing in x,y,z = 1.6795962 1.6795962 1.6795962 +region box block 0 $x 0 $y 0 $z +region box block 0 3 0 $y 0 $z +region box block 0 3 0 3 0 $z +region box block 0 3 0 3 0 3 +create_box 1 box +Created orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886) + 1 by 1 by 1 MPI processor grid +create_atoms 1 box +Created 108 atoms + using lattice units in orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886) + create_atoms CPU = 0.000 seconds +mass 1 1.0 + +velocity all create 1.44 87287 loop geom + +pair_style lj/cut 2.5 +pair_coeff 1 1 1.0 1.0 2.5 + +neighbor 0.3 bin +neigh_modify delay 0 every 20 check no + +fix 1 all nve + +# define Python truncate() function + +variable foo python truncate +python truncate return v_foo input 1 iv_arg format fi here """ +def truncate(x): + return int(x) +""" + +# use in equal-style variable + +variable scalar equal py_foo(4.5) +print "TRUNCATE ${scalar}" +TRUNCATE 4 + +# use in atom-style variable +# examine dump file to see truncated xyz coords of each atom + +variable xtrunc atom py_foo(x) +variable ytrunc atom py_foo(y) +variable ztrunc atom py_foo(z) + +dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc + +# use in vector-style variable + +compute ke all temp +variable ke vector c_ke +variable ketrunc vector py_foo(v_ke) +thermo_style custom step temp epair v_ketrunc[*6] + +run 100 +Generated 0 of 0 mixed pair_coeff terms from geometric mixing rule +Neighbor list info ... + update: every = 20 steps, delay = 0 steps, check = no + max neighbors/atom: 2000, page size: 100000 + master list distance cutoff = 2.8 + ghost atom cutoff = 2.8 + binsize = 1.4, bins = 4 4 4 + 1 neighbor lists, perpetual/occasional/extra = 1 0 0 + (1) pair lj/cut, perpetual + attributes: half, newton on + pair build: half/bin/atomonly/newton + stencil: half/bin/3d + bin: standard +Per MPI rank memory allocation (min/avg/max) = 2.598 | 2.598 | 2.598 Mbytes + Step Temp E_pair v_ketrunc[1] v_ketrunc[2] v_ketrunc[3] v_ketrunc[4] v_ketrunc[5] v_ketrunc[6] + 0 1.44 -6.7733681 155 152 154 -10 -4 -6 + 100 0.82217015 -5.8614684 113 65 84 7 -1 -12 +Loop time of 0.00278186 on 1 procs for 100 steps with 108 atoms + +Performance: 15529161.573 tau/day, 35947.133 timesteps/s, 3.882 Matom-step/s +100.0% CPU use with 1 MPI tasks x no OpenMP threads + +MPI task timing breakdown: +Section | min time | avg time | max time |%varavg| %total +--------------------------------------------------------------- +Pair | 0.0018161 | 0.0018161 | 0.0018161 | 0.0 | 65.29 +Neigh | 0.00057543 | 0.00057543 | 0.00057543 | 0.0 | 20.68 +Comm | 0.00019634 | 0.00019634 | 0.00019634 | 0.0 | 7.06 +Output | 0.00012056 | 0.00012056 | 0.00012056 | 0.0 | 4.33 +Modify | 4.8221e-05 | 4.8221e-05 | 4.8221e-05 | 0.0 | 1.73 +Other | | 2.516e-05 | | | 0.90 + +Nlocal: 108 ave 108 max 108 min +Histogram: 1 0 0 0 0 0 0 0 0 0 +Nghost: 980 ave 980 max 980 min +Histogram: 1 0 0 0 0 0 0 0 0 0 +Neighs: 4071 ave 4071 max 4071 min +Histogram: 1 0 0 0 0 0 0 0 0 0 + +Total # of neighbors = 4071 +Ave neighs/atom = 37.694444 +Neighbor list builds = 5 +Dangerous builds not checked + +print "KE TENSOR ${ketrunc}" +KE TENSOR [113,65,84,7,-1,-12] + + +Total wall time: 0:00:00 diff --git a/examples/python/log.1May25.python.wrap.g++.4 b/examples/python/log.1May25.python.wrap.g++.4 new file mode 100644 index 0000000000..5f28f5bc34 --- /dev/null +++ b/examples/python/log.1May25.python.wrap.g++.4 @@ -0,0 +1,116 @@ +LAMMPS (2 Apr 2025 - Development - patch_2Apr2025-270-g2351418c94-modified) +# 3d Lennard-Jones melt with equal- and atom-style variables which +# use a Python function wrapper in their formulas + +variable x index 3 +variable y index 3 +variable z index 3 + +units lj +atom_style atomic + +lattice fcc 0.8442 +Lattice spacing in x,y,z = 1.6795962 1.6795962 1.6795962 +region box block 0 $x 0 $y 0 $z +region box block 0 3 0 $y 0 $z +region box block 0 3 0 3 0 $z +region box block 0 3 0 3 0 3 +create_box 1 box +Created orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886) + 1 by 2 by 2 MPI processor grid +create_atoms 1 box +Created 108 atoms + using lattice units in orthogonal box = (0 0 0) to (5.0387886 5.0387886 5.0387886) + create_atoms CPU = 0.000 seconds +mass 1 1.0 + +velocity all create 1.44 87287 loop geom + +pair_style lj/cut 2.5 +pair_coeff 1 1 1.0 1.0 2.5 + +neighbor 0.3 bin +neigh_modify delay 0 every 20 check no + +fix 1 all nve + +# define Python truncate() function + +variable foo python truncate +python truncate return v_foo input 1 iv_arg format fi here """ +def truncate(x): + return int(x) +""" + +# use in equal-style variable + +variable scalar equal py_foo(4.5) +print "TRUNCATE ${scalar}" +TRUNCATE 4 + +# use in atom-style variable +# examine dump file to see truncated xyz coords of each atom + +variable xtrunc atom py_foo(x) +variable ytrunc atom py_foo(y) +variable ztrunc atom py_foo(z) + +dump 1 all custom 100 tmp.dump id x y z v_xtrunc v_ytrunc v_ztrunc + +# use in vector-style variable + +compute ke all temp +variable ke vector c_ke +variable ketrunc vector py_foo(v_ke) +thermo_style custom step temp epair v_ketrunc[*6] + +run 100 +Generated 0 of 0 mixed pair_coeff terms from geometric mixing rule +Neighbor list info ... + update: every = 20 steps, delay = 0 steps, check = no + max neighbors/atom: 2000, page size: 100000 + master list distance cutoff = 2.8 + ghost atom cutoff = 2.8 + binsize = 1.4, bins = 4 4 4 + 1 neighbor lists, perpetual/occasional/extra = 1 0 0 + (1) pair lj/cut, perpetual + attributes: half, newton on + pair build: half/bin/atomonly/newton + stencil: half/bin/3d + bin: standard +Per MPI rank memory allocation (min/avg/max) = 2.59 | 2.59 | 2.59 Mbytes + Step Temp E_pair v_ketrunc[1] v_ketrunc[2] v_ketrunc[3] v_ketrunc[4] v_ketrunc[5] v_ketrunc[6] + 0 1.44 -6.7733681 155 152 154 -10 -4 -6 + 100 0.82217015 -5.8614684 113 65 84 7 -1 -12 +Loop time of 0.00268845 on 4 procs for 100 steps with 108 atoms + +Performance: 16068745.964 tau/day, 37196.171 timesteps/s, 4.017 Matom-step/s +66.7% CPU use with 4 MPI tasks x no OpenMP threads + +MPI task timing breakdown: +Section | min time | avg time | max time |%varavg| %total +--------------------------------------------------------------- +Pair | 0.00043389 | 0.00051695 | 0.00061255 | 0.0 | 19.23 +Neigh | 0.00017121 | 0.00018976 | 0.00019891 | 0.0 | 7.06 +Comm | 0.0017423 | 0.0018487 | 0.0019509 | 0.2 | 68.76 +Output | 6.7449e-05 | 6.9998e-05 | 7.5195e-05 | 0.0 | 2.60 +Modify | 2.1329e-05 | 2.2855e-05 | 2.4821e-05 | 0.0 | 0.85 +Other | | 4.018e-05 | | | 1.49 + +Nlocal: 27 ave 30 max 25 min +Histogram: 1 0 1 0 1 0 0 0 0 1 +Nghost: 614 ave 616 max 612 min +Histogram: 1 0 1 0 0 0 0 1 0 1 +Neighs: 1017.75 ave 1149 max 894 min +Histogram: 1 1 0 0 0 0 0 1 0 1 + +Total # of neighbors = 4071 +Ave neighs/atom = 37.694444 +Neighbor list builds = 5 +Dangerous builds not checked + +print "KE TENSOR ${ketrunc}" +KE TENSOR [113,65,84,7,-1,-12] + + +Total wall time: 0:00:00 diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 4baff4d03a..ed9e7b2b6b 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -408,6 +408,12 @@ MODULE LIBLAMMPS SUBROUTINE lammps_kokkos_finalize() BIND(C) END SUBROUTINE lammps_kokkos_finalize + SUBROUTINE lammps_python_finalize() BIND(C) + END SUBROUTINE lammps_python_finalize + + SUBROUTINE lammps_plugin_finalize() BIND(C) + END SUBROUTINE lammps_plugin_finalize + SUBROUTINE lammps_error(handle, error_type, error_text) BIND(C) IMPORT :: c_ptr, c_int IMPLICIT NONE @@ -1135,7 +1141,7 @@ CONTAINS SIZE_IMAGEINT = lmp_extract_setting(lmp_open, 'imageint') END FUNCTION lmp_open - ! Combined Fortran wrapper around lammps_close() and lammps_mpi_finalize() + ! Combined Fortran wrapper around lammps_close() and lammps_*_finalize() SUBROUTINE lmp_close(self, finalize) CLASS(lammps), INTENT(IN) :: self LOGICAL, INTENT(IN), OPTIONAL :: finalize @@ -1146,6 +1152,8 @@ CONTAINS IF (finalize) THEN CALL lammps_kokkos_finalize() CALL lammps_mpi_finalize() + CALL lammps_python_finalize() + CALL lammps_plugin_finalize() END IF END IF END SUBROUTINE lmp_close diff --git a/lib/gpu/lal_eam.cpp b/lib/gpu/lal_eam.cpp index 0a2ed21ab3..08b5f7e9b7 100644 --- a/lib/gpu/lal_eam.cpp +++ b/lib/gpu/lal_eam.cpp @@ -61,7 +61,7 @@ int EAMT::init(const int ntypes, double host_cutforcesq, int **host_type2rhor, if (onetype>0) onetype=-1; else if (onetype==0) - onetype=i*max_shared_types+i; + onetype=i; } if (onetype<0) onetype=0; #endif @@ -109,7 +109,7 @@ int EAMT::init(const int ntypes, double host_cutforcesq, int **host_type2rhor, int lj_types=ntypes; shared_types=false; - if (lj_types<=max_shared_types && this->_block_size>=max_shared_types) { + if (lj_types<=max_shared_types && this->_block_size>=max_shared_types*max_shared_types) { lj_types=max_shared_types; shared_types=true; } diff --git a/lib/voronoi/voro-make.patch b/lib/voronoi/voro-make.patch index f51bd08614..f2811e3adb 100644 --- a/lib/voronoi/voro-make.patch +++ b/lib/voronoi/voro-make.patch @@ -1,5 +1,35 @@ ---- src/Makefile.orig 2020-05-03 03:50:23.501557199 -0400 -+++ src/Makefile 2020-05-03 03:53:32.147681674 -0400 +--- Makefile.orig 2025-06-04 12:16:01.056286325 -0400 ++++ Makefile 2025-06-04 12:18:47.454879006 -0400 +@@ -11,8 +11,7 @@ + + # Build all of the executable files + all: +- $(MAKE) -C src +- $(MAKE) -C examples ++ $(MAKE) -C src depend libvoro++.a + + # Build the help files (with Doxygen) + help: +@@ -24,16 +23,12 @@ + $(MAKE) -C examples clean + + # Install the executable, man page, and shared library +-install: +- $(MAKE) -C src +- $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/bin ++install: all + $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/lib + $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/man + $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/man/man1 + $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/include + $(INSTALL) -d $(IFLAGS_EXEC) $(PREFIX)/include/voro++ +- $(INSTALL) $(IFLAGS_EXEC) src/voro++ $(PREFIX)/bin +- $(INSTALL) $(IFLAGS) man/voro++.1 $(PREFIX)/man/man1 + $(INSTALL) $(IFLAGS) src/libvoro++.a $(PREFIX)/lib + $(INSTALL) $(IFLAGS) src/voro++.hh $(PREFIX)/include/voro++ + $(INSTALL) $(IFLAGS) src/c_loops.hh $(PREFIX)/include/voro++ +--- src/Makefile.orig 2013-10-17 13:54:13.000000000 -0400 ++++ src/Makefile 2025-06-04 12:16:47.293104880 -0400 @@ -10,10 +10,10 @@ # List of the common source files objs=cell.o common.o container.o unitcell.o v_compute.o c_loops.o \ diff --git a/python/lammps/core.py b/python/lammps/core.py index d808fc713f..cab3171cd3 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -653,8 +653,9 @@ class lammps(object): def finalize(self): """Shut down the MPI communication and Kokkos environment (if active) through the - library interface by calling :cpp:func:`lammps_mpi_finalize` and - :cpp:func:`lammps_kokkos_finalize`. + library interface by calling :cpp:func:`lammps_mpi_finalize`, + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize`, and + :cpp:func:`lammps_plugin_finalize` You cannot create or use any LAMMPS instances after this function is called unless LAMMPS was compiled without MPI and without Kokkos support. @@ -662,6 +663,8 @@ class lammps(object): self.close() self.lib.lammps_kokkos_finalize() self.lib.lammps_mpi_finalize() + self.lib.lammps_python_finalize() + self.lib.lammps_plugin_finalize() # ------------------------------------------------------------------------- diff --git a/src/EXTRA-FIX/fix_ave_moments.cpp b/src/EXTRA-FIX/fix_ave_moments.cpp index c0d4b6c9d0..882aaa7f28 100644 --- a/src/EXTRA-FIX/fix_ave_moments.cpp +++ b/src/EXTRA-FIX/fix_ave_moments.cpp @@ -29,6 +29,7 @@ #include "variable.h" #include +#include using namespace LAMMPS_NS; using namespace FixConst; @@ -113,7 +114,7 @@ FixAveMoments::FixAveMoments(LAMMPS *lmp, int narg, char **arg) : if ((val.which == ArgInfo::NONE) || (val.which == ArgInfo::UNKNOWN) || (argi.get_dim() > 1)) error->all(FLERR, val.iarg, "Invalid fix ave/moments argument: {}", arg[i]); - values.push_back(val); + values.push_back(std::move(val)); } if (nvalues != (int)values.size()) error->all(FLERR, Error::NOPOINTER, diff --git a/src/KOKKOS/mliap_model_python_kokkos.h b/src/KOKKOS/mliap_model_python_kokkos.h index 4f56ecb0ab..76b4a9d42a 100644 --- a/src/KOKKOS/mliap_model_python_kokkos.h +++ b/src/KOKKOS/mliap_model_python_kokkos.h @@ -29,7 +29,7 @@ class MLIAPModelPythonKokkos : public MLIAPModelPython, public MLIAPModelKokkos< public: MLIAPModelPythonKokkos(LAMMPS *, char * = nullptr); ~MLIAPModelPythonKokkos(); - void read_coeffs(char *fname); + void read_coeffs(char *fname) override; void compute_gradients(class MLIAPData *) override; void compute_gradgrads(class MLIAPData *) override; diff --git a/src/KOKKOS/pair_mliap_kokkos.cpp b/src/KOKKOS/pair_mliap_kokkos.cpp index e91d1bea9d..e0e109a69e 100644 --- a/src/KOKKOS/pair_mliap_kokkos.cpp +++ b/src/KOKKOS/pair_mliap_kokkos.cpp @@ -85,14 +85,24 @@ void PairMLIAPKokkos::compute(int eflag, int vflag) error->all(FLERR, "Incompatible model and descriptor element count"); ev_init(eflag, vflag, 0); - if (eflag_atom && (int)k_eatom.h_view.extent(0) < maxeatom) { - memoryKK->destroy_kokkos(k_eatom,eatom); - memoryKK->create_kokkos(k_eatom,eatom,maxeatom,"pair:eatom"); + if (eflag_atom) { + if ((int)k_eatom.h_view.extent(0) < maxeatom) { + memoryKK->destroy_kokkos(k_eatom,eatom); + memoryKK->create_kokkos(k_eatom,eatom,maxeatom,"pair:eatom"); + } else { + Kokkos::deep_copy(k_eatom.d_view,0); + k_eatom.modify(); + } } - if (vflag_atom && (int)k_vatom.h_view.extent(0) < maxeatom) { - memoryKK->destroy_kokkos(k_vatom,vatom); - memoryKK->create_kokkos(k_vatom,vatom,maxeatom,6,"pair:eatom"); + if (vflag_atom) { + if ((int)k_vatom.h_view.extent(0) < maxeatom) { + memoryKK->destroy_kokkos(k_vatom,vatom); + memoryKK->create_kokkos(k_vatom,vatom,maxeatom,6,"pair:eatom"); + } else { + Kokkos::deep_copy(k_vatom.d_view,0); + k_vatom.modify(); + } } data->generate_neighdata(list, eflag, vflag); diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 746055f28c..4595843477 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -37,15 +37,9 @@ #include +// prototype repeated from base class implementation namespace LAMMPS_NS { struct ACEALImpl { - ACEALImpl() : basis_set(nullptr), ace(nullptr) {} - - ~ACEALImpl() { - delete basis_set; - delete ace; - } - ACEBBasisSet *basis_set; ACEBEvaluator *ace; }; diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 0afbb7540e..577f77ba8b 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -38,14 +38,9 @@ #include +// prototype repeated from base class implementation namespace LAMMPS_NS { struct ACEImpl { - ACEImpl() : basis_set(nullptr), ace(nullptr) {} - ~ACEImpl() - { - delete basis_set; - delete ace; - } ACECTildeBasisSet *basis_set; ACERecursiveEvaluator *ace; }; diff --git a/src/MC/fix_neighbor_swap.cpp b/src/MC/fix_neighbor_swap.cpp deleted file mode 100644 index d44181a981..0000000000 --- a/src/MC/fix_neighbor_swap.cpp +++ /dev/null @@ -1,901 +0,0 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - LAMMPS development team: developers@lammps.org - - Copyright (2003) Sandia Corporation. Under the terms of Contract - DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains - certain rights in this software. This software is distributed under - the GNU General Public License. - - See the README file in the top-level LAMMPS directory. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing authors: Jacob Tavenner -------------------------------------------------------------------------- */ - -#include "fix_neighbor_swap.h" - -#include "angle.h" -#include "atom.h" -#include "bond.h" -#include "comm.h" -#include "compute.h" -#include "compute_voronoi_atom.h" -#include "dihedral.h" -#include "domain.h" -#include "error.h" -#include "fix.h" -#include "force.h" -#include "group.h" -#include "improper.h" -#include "kspace.h" -#include "math_special.h" -#include "memory.h" -#include "modify.h" -#include "neighbor.h" -#include "pair.h" -#include "random_park.h" -#include "region.h" -#include "update.h" - -#include -#include -#include -#include - -using namespace LAMMPS_NS; -using namespace FixConst; - -static const char cite_fix_neighbor_swap_c[] = - "fix neighbor/swap command: doi:10.1016/j.commatsci.2022.111929\n\n" - "@Article{Tavenner2023111929,\n" - " author = {Jacob P. Tavenner and Mikhail I. Mendelev and John W. Lawson},\n" - " title = {Molecular dynamics based kinetic Monte Carlo simulation for accelerated " - "diffusion},\n" - " journal = {Computational Materials Science},\n" - " year = {2023},\n" - " volume = {218},\n" - " pages = {111929}\n" - " url = {https://www.sciencedirect.com/science/article/pii/S0927025622006401}\n" - "}\n\n"; - -/* ---------------------------------------------------------------------- */ - -FixNeighborSwap::FixNeighborSwap(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg), region(nullptr), idregion(nullptr), type_list(nullptr), qtype(nullptr), - c_voro(nullptr), voro_neighbor_list(nullptr), sqrt_mass_ratio(nullptr), - local_swap_iatom_list(nullptr), random_equal(nullptr), c_pe(nullptr) -{ - if (narg < 10) utils::missing_cmd_args(FLERR, "fix neighbor/swap", error); - - dynamic_group_allow = 1; - - vector_flag = 1; - size_vector = 2; - global_freq = 1; - extvector = 0; - restart_global = 1; - time_depend = 1; - - // required args - - nevery = utils::inumeric(FLERR, arg[3], false, lmp); - ncycles = utils::inumeric(FLERR, arg[4], false, lmp); - seed = utils::inumeric(FLERR, arg[5], false, lmp); - double temperature = utils::numeric(FLERR, arg[6], false, lmp); - r_0 = utils::inumeric(FLERR, arg[7], false, lmp); - - // Voro compute check - - int icompute = modify->find_compute(utils::strdup(arg[8])); - if (icompute < 0) error->all(FLERR, "Could not find neighbor compute ID"); - c_voro = modify->compute[icompute]; - if (c_voro->local_flag == 0) - error->all(FLERR, "Neighbor compute does not compute local info"); - if (c_voro->size_local_cols != 3) - error->all(FLERR, "Neighbor compute does not give i, j, size as expected"); - - if (nevery <= 0) error->all(FLERR, "Illegal fix neighbor/swap command nevery value"); - if (ncycles < 0) error->all(FLERR, "Illegal fix neighbor/swap command ncycles value"); - if (seed <= 0) error->all(FLERR, "Illegal fix neighbor/swap command seed value"); - if (temperature <= 0.0) error->all(FLERR, "Illegal fix neighbor/swap command temperature value"); - - beta = 1.0 / (force->boltz * temperature); - - memory->create(type_list, atom->ntypes, "neighbor/swap:type_list"); - memory->create(rate_list, atom->ntypes, "neighbor/swap:rate_list"); - - // read options from end of input line - - options(narg - 8, &arg[8]); - - // random number generator, same for all procs - - random_equal = new RanPark(lmp, seed); - - // set up reneighboring - - force_reneighbor = 1; - next_reneighbor = update->ntimestep + 1; - - // zero out counters - - nswap_attempts = 0.0; - nswap_successes = 0.0; - - atom_swap_nmax = 0; - voro_neighbor_list = nullptr; - local_swap_iatom_list = nullptr; - local_swap_neighbor_list = nullptr; - local_swap_probability = nullptr; - local_swap_type_list = nullptr; - - // set comm size needed by this Fix - - if (atom->q_flag) - comm_forward = 2; - else - comm_forward = 1; -} - -/* ---------------------------------------------------------------------- */ - -FixNeighborSwap::~FixNeighborSwap() -{ - memory->destroy(type_list); - memory->destroy(rate_list); - memory->destroy(qtype); - memory->destroy(sqrt_mass_ratio); - memory->destroy(local_swap_iatom_list); - memory->destroy(local_swap_neighbor_list); - memory->destroy(local_swap_probability); - memory->destroy(local_swap_type_list); - delete[] idregion; - delete random_equal; -} - -/* ---------------------------------------------------------------------- - parse optional parameters at end of input line -------------------------------------------------------------------------- */ - -void FixNeighborSwap::options(int narg, char **arg) -{ - if (narg < 0) error->all(FLERR, "Illegal fix neighbor/swap command\n"); - - ke_flag = 1; - diff_flag = 0; - rates_flag = 0; - nswaptypes = 0; - - int iarg = 0; - while (iarg < narg) { - if (strcmp(arg[iarg], "region") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal fix neighbor/swap command"); - region = domain->get_region_by_id(arg[iarg + 1]); - if (!region) error->all(FLERR, "Region ID for fix neighbor/swap does not exist"); - idregion = utils::strdup(arg[iarg + 1]); - iarg += 2; - } else if (strcmp(arg[iarg], "ke") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal fix neighbor/swap command"); - ke_flag = utils::logical(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "types") == 0) { - if (iarg + 3 > narg) error->all(FLERR, "Illegal fix neighbor/swap command"); - if (diff_flag != 0) error->all(FLERR, "Illegal fix neighbor/swap command"); - iarg++; - nswaptypes = 0; - while (iarg < narg) { - if (isalpha(arg[iarg][0])) break; - if (nswaptypes >= atom->ntypes) error->all(FLERR, "Illegal fix neighbor/swap command"); - type_list[nswaptypes] = utils::numeric(FLERR, arg[iarg], false, lmp); - nswaptypes++; - iarg++; - } - } else if (strcmp(arg[iarg], "diff") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal fix neighbor/swap command"); - if (nswaptypes != 0) error->all(FLERR, "Illegal fix neighbor/swap command"); - type_list[nswaptypes] = utils::numeric(FLERR, arg[iarg + 1], false, lmp); - diff_flag = 1; - nswaptypes++; - iarg += 2; - } else if (strcmp(arg[iarg], "rates") == 0) { - if (iarg + atom->ntypes >= narg) error->all(FLERR, "Illegal fix neighbor/swap command"); - iarg++; - int i = 0; - while (iarg < narg) { - if (isalpha(arg[iarg][0])) break; - if (i >= atom->ntypes) error->all(FLERR, "Illegal fix neighbor/swap command"); - rate_list[i] = utils::numeric(FLERR, arg[iarg], false, lmp); - i++; - iarg++; - } - rates_flag = 1; - if (i != atom->ntypes) error->all(FLERR, "Illegal fix neighbor/swap command"); - } else - error->all(FLERR, "Illegal fix neighbor/swap command"); - } -} - -/* ---------------------------------------------------------------------- */ - -int FixNeighborSwap::setmask() -{ - int mask = 0; - mask |= PRE_EXCHANGE; - return mask; -} - -/* ---------------------------------------------------------------------- */ - -void FixNeighborSwap::init() -{ - c_pe = modify->get_compute_by_id("thermo_pe"); - - int *type = atom->type; - - if (nswaptypes < 2 && !diff_flag) - error->all(FLERR, "Must specify at least 2 types in fix neighbor/swap command"); - - // set index and check validity of region - - if (idregion) { - region = domain->get_region_by_id(idregion); - if (!region) error->all(FLERR, "Region {} for fix setforce does not exist", idregion); - } - - for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) - if (type_list[iswaptype] <= 0 || type_list[iswaptype] > atom->ntypes) - error->all(FLERR, "Invalid atom type in fix neighbor/swap command"); - - if (atom->q_flag) { - double qmax, qmin; - int firstall, first; - memory->create(qtype, nswaptypes, "neighbor/swap:qtype"); - for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) { - first = 1; - for (int i = 0; i < atom->nlocal; i++) { - if (atom->mask[i] & groupbit) { - if (type[i] == type_list[iswaptype]) { - if (first) { - qtype[iswaptype] = atom->q[i]; - first = 0; - } else if (qtype[iswaptype] != atom->q[i]) - error->one(FLERR, "All atoms of a swapped type must have the same charge."); - } - } - } - MPI_Allreduce(&first, &firstall, 1, MPI_INT, MPI_MIN, world); - if (firstall) - error->all(FLERR, - "At least one atom of each swapped type must be present to define charges."); - if (first) qtype[iswaptype] = -DBL_MAX; - MPI_Allreduce(&qtype[iswaptype], &qmax, 1, MPI_DOUBLE, MPI_MAX, world); - if (first) qtype[iswaptype] = DBL_MAX; - MPI_Allreduce(&qtype[iswaptype], &qmin, 1, MPI_DOUBLE, MPI_MIN, world); - if (qmax != qmin) error->all(FLERR, "All atoms of a swapped type must have same charge."); - } - } - - memory->create(sqrt_mass_ratio, atom->ntypes + 1, atom->ntypes + 1, - "neighbor/swap:sqrt_mass_ratio"); - for (int itype = 1; itype <= atom->ntypes; itype++) - for (int jtype = 1; jtype <= atom->ntypes; jtype++) - sqrt_mass_ratio[itype][jtype] = sqrt(atom->mass[itype] / atom->mass[jtype]); - - // check to see if itype and jtype cutoffs are the same - // if not, reneighboring will be needed between swaps - - double **cutsq = force->pair->cutsq; - unequal_cutoffs = false; - for (int iswaptype = 0; iswaptype < nswaptypes; iswaptype++) - for (int jswaptype = 0; jswaptype < nswaptypes; jswaptype++) - for (int ktype = 1; ktype <= atom->ntypes; ktype++) - if (cutsq[type_list[iswaptype]][ktype] != cutsq[type_list[jswaptype]][ktype]) - unequal_cutoffs = true; - - // check that no swappable atoms are in atom->firstgroup - // swapping such an atom might not leave firstgroup atoms first - - if (atom->firstgroup >= 0) { - int *mask = atom->mask; - int firstgroupbit = group->bitmask[atom->firstgroup]; - - int flag = 0; - for (int i = 0; i < atom->nlocal; i++) - if ((mask[i] == groupbit) && (mask[i] && firstgroupbit)) flag = 1; - - int flagall; - MPI_Allreduce(&flag, &flagall, 1, MPI_INT, MPI_SUM, world); - - if (flagall) error->all(FLERR, "Cannot do neighbor/swap on atoms in atom_modify first group"); - } -} - -/* ---------------------------------------------------------------------- - attempt Monte Carlo swaps -------------------------------------------------------------------------- */ - -void FixNeighborSwap::pre_exchange() -{ - // just return if should not be called on this timestep - - if (next_reneighbor != update->ntimestep) return; - - // ensure current system is ready to compute energy - - if (domain->triclinic) domain->x2lamda(atom->nlocal); - domain->pbc(); - comm->exchange(); - comm->borders(); - if (domain->triclinic) domain->lamda2x(atom->nlocal + atom->nghost); - if (modify->n_pre_neighbor) modify->pre_neighbor(); - neighbor->build(1); - - // energy_stored = energy of current state - // will be updated after accepted swaps - - energy_stored = energy_full(); - - // attempt Ncycle atom swaps - - int nsuccess = 0; - update_iswap_atoms_list(); - for (int i = 0; i < ncycles; i++) nsuccess += attempt_swap(); - - // udpate MC stats - - nswap_attempts += ncycles; - nswap_successes += nsuccess; - - next_reneighbor = update->ntimestep + nevery; -} - -/* ---------------------------------------------------------------------- - attempt a swap of a pair of atoms - compare before/after energy and accept/reject the swap -------------------------------------------------------------------------- */ - -int FixNeighborSwap::attempt_swap() -{ - // int nlocal = atom->nlocal; - tagint *id = atom->tag; - - if (niswap == 0) return 0; - - // pre-swap energy - - double energy_before = energy_stored; - - // pick a random atom i - - int i = pick_i_swap_atom(); - - // get global id and position of atom i - // get_global_i(i); - - // build nearest-neighbor list based on atom i - - build_i_neighbor_list(i); - if (njswap <= 0) return 0; - - // pick a neighbor atom j based on i neighbor list - jtype_selected = -1; - int j = pick_j_swap_neighbor(i); - - int itype = type_list[0]; - int jtype = jtype_selected; - - // Accept swap if types are equal, no change to system - if (itype == jtype) { return 1; } - - // swap their properties - if (i >= 0) { - atom->type[i] = jtype; - if (atom->q_flag) atom->q[i] = qtype[jtype_selected]; - } - if (j >= 0) { - atom->type[j] = itype; - if (atom->q_flag) atom->q[j] = qtype[0]; - } - - // if unequal_cutoffs, call comm->borders() and rebuild neighbor list - // else communicate ghost atoms - // call to comm->exchange() is a no-op but clears ghost atoms - - if (unequal_cutoffs) { - if (domain->triclinic) domain->x2lamda(atom->nlocal); - domain->pbc(); - comm->exchange(); - comm->borders(); - if (domain->triclinic) domain->lamda2x(atom->nlocal + atom->nghost); - if (modify->n_pre_neighbor) modify->pre_neighbor(); - neighbor->build(1); - } else { - comm->forward_comm(this); - } - - // post-swap energy - - double energy_after = energy_full(); - - // if swap accepted, return 1 - // if ke_flag, rescale atom velocities - - if (random_equal->uniform() < exp(beta * (energy_before - energy_after))) { - update_iswap_atoms_list(); - if (ke_flag) { - if (i >= 0) { - atom->v[i][0] *= sqrt_mass_ratio[itype][jtype]; - atom->v[i][1] *= sqrt_mass_ratio[itype][jtype]; - atom->v[i][2] *= sqrt_mass_ratio[itype][jtype]; - } - if (j >= 0) { - atom->v[j][0] *= sqrt_mass_ratio[jtype][itype]; - atom->v[j][1] *= sqrt_mass_ratio[jtype][itype]; - atom->v[j][2] *= sqrt_mass_ratio[jtype][itype]; - } - } - energy_stored = energy_after; - return 1; - } - - // swap not accepted, return 0 - // restore the swapped itype & jtype atoms - // do not need to re-call comm->borders() and rebuild neighbor list - // since will be done on next cycle or in Verlet when this fix finishes - - if (i >= 0) { - atom->type[i] = itype; - if (atom->q_flag) atom->q[i] = qtype[0]; - } - if (j >= 0) { - atom->type[j] = jtype; - if (atom->q_flag) atom->q[j] = qtype[jtype_selected]; - } - - return 0; -} - -/* ---------------------------------------------------------------------- - compute system potential energy -------------------------------------------------------------------------- */ - -double FixNeighborSwap::energy_full() -{ - int eflag = 1; - int vflag = 0; - - if (modify->n_pre_force) modify->pre_force(vflag); - - if (force->pair) force->pair->compute(eflag, vflag); - - if (atom->molecular != Atom::ATOMIC) { - if (force->bond) force->bond->compute(eflag, vflag); - if (force->angle) force->angle->compute(eflag, vflag); - if (force->dihedral) force->dihedral->compute(eflag, vflag); - if (force->improper) force->improper->compute(eflag, vflag); - } - - if (force->kspace) force->kspace->compute(eflag, vflag); - - if (modify->n_post_force_any) modify->post_force(vflag); - - update->eflag_global = update->ntimestep; - double total_energy = c_pe->compute_scalar(); - - return total_energy; -} - -/* ---------------------------------------------------------------------- -------------------------------------------------------------------------- */ - -int FixNeighborSwap::pick_i_swap_atom() -{ - tagint *id = atom->tag; - int id_center_local = -1; - int i = -1; - - int iwhichglobal = static_cast(niswap * random_equal->uniform()); - if ((iwhichglobal >= niswap_before) && (iwhichglobal < niswap_before + niswap_local)) { - int iwhichlocal = iwhichglobal - niswap_before; - i = local_swap_iatom_list[iwhichlocal]; - id_center_local = id[i]; - MPI_Allreduce(&id[i], &id_center, 1, MPI_INT, MPI_MAX, world); - } else { - MPI_Allreduce(&id[i], &id_center, 1, MPI_INT, MPI_MAX, world); - } - - return i; -} - -/* ---------------------------------------------------------------------- -------------------------------------------------------------------------- */ - -int FixNeighborSwap::pick_j_swap_neighbor(int i) -{ - int j = -1; - int jtype_selected_local = -1; - - // Generate random double from 0 to maximum global probability - double selected_prob = static_cast(global_probability * random_equal->uniform()); - - // Find which local swap atom corresponds to probability - if ((selected_prob >= prev_probability) && - (selected_prob < prev_probability + local_probability)) { - double search_prob = selected_prob - prev_probability; - for (int n = 0; n < njswap_local; n++) { - if (search_prob > local_swap_probability[n]) { - search_prob -= local_swap_probability[n]; - } else { - j = local_swap_neighbor_list[n]; - jtype_selected_local = local_swap_type_list[n]; - MPI_Allreduce(&jtype_selected_local, &jtype_selected, 1, MPI_INT, MPI_MAX, world); - return j; - } - } - error->all(FLERR, "Did not select local neighbor swap atom"); - } - - MPI_Allreduce(&jtype_selected_local, &jtype_selected, 1, MPI_INT, MPI_MAX, world); - return j; -} - -/* ---------------------------------------------------------------------- -------------------------------------------------------------------------- */ - -double FixNeighborSwap::get_distance(double *i, double *j) -{ - double r = sqrt(MathSpecial::square((i[0] - j[0])) + MathSpecial::square((i[1] - j[1])) + - MathSpecial::square((i[2] - j[2]))); - return r; -} - -/* ---------------------------------------------------------------------- -------------------------------------------------------------------------- */ - -void FixNeighborSwap::build_i_neighbor_list(int i_center) -{ - int nghost = atom->nghost; - int nlocal = atom->nlocal; - int *type = atom->type; - double **x = atom->x; - tagint *id = atom->tag; - - // Allocate local_swap_neighbor_list size - - memory->sfree(local_swap_neighbor_list); - atom_swap_nmax = atom->nmax; - local_swap_neighbor_list = - (int *) memory->smalloc(atom_swap_nmax * sizeof(int), "MCSWAP:local_swap_neighbor_list"); - - memory->sfree(local_swap_probability); - local_swap_probability = (double *) memory->smalloc(atom_swap_nmax * sizeof(double), - "MCSWAP:local_swap_probability_list"); - - memory->sfree(local_swap_type_list); - local_swap_type_list = - (int *) memory->smalloc(atom_swap_nmax * sizeof(int), "MCSWAP:local_swap_type_list"); - - // Compute voronoi and access neighbor list - - c_voro->compute_local(); - - voro_neighbor_list = c_voro->array_local; - njswap_local = 0; - local_probability = 0.0; - - for (int n = 0; n < c_voro->size_local_rows; n++) { - - int temp_j_id = -1; - int temp_j = -1; - - // Find local voronoi entry with selected central atom - if ((int) voro_neighbor_list[n][0] == id_center) { - temp_j_id = voro_neighbor_list[n][1]; - temp_j = -1; - } else if (((int) voro_neighbor_list[n][1] == id_center) && (i_center < 0)) { - temp_j_id = voro_neighbor_list[n][0]; - temp_j = -1; - } else { - continue; - } - - // Find which local atom corresponds to neighbor - for (int j = 0; j < nlocal; j++) { - if (temp_j_id == id[j]) { - temp_j = j; - break; - } - } - - // If temp_j not on this processor, skip - if (temp_j < 0) continue; - - if (region) { - if (region->match(x[temp_j][0], x[temp_j][1], x[temp_j][2]) == 1) { - if (atom->mask[temp_j] & groupbit) { - if (diff_flag) { - // Calculate distance from i to each j, adjust probability of selection - - // Get distance if own center atom - double r = INFINITY; - if (i_center >= 0) { double r = get_distance(x[temp_j], x[i_center]); } - - // Get local id of ghost center atom when ghost - for (int i = nlocal; i < nlocal + nghost; i++) { - if ((id[i] == id_center) && (get_distance(x[temp_j], x[i]) < r)) { - r = get_distance(x[temp_j], x[i]); - } - } - - if (rates_flag) { - local_swap_probability[njswap_local] = - rate_list[type[temp_j] - 1] * exp(-MathSpecial::square(r / r_0)); - } else { - local_swap_probability[njswap_local] = exp(-MathSpecial::square(r / r_0)); - } - local_probability += local_swap_probability[njswap_local]; - local_swap_type_list[njswap_local] = type[temp_j]; - local_swap_neighbor_list[njswap_local] = temp_j; - njswap_local++; - } else { - for (int jswaptype = 1; jswaptype < nswaptypes; jswaptype++) { - if (type[temp_j] == type_list[jswaptype]) { - // Calculate distance from i to each j, adjust probability of selection - // Get distance if own center atom - double r = INFINITY; - if (i_center >= 0) { double r = get_distance(x[temp_j], x[i_center]); } - - // Get local id of ghost center atom when ghost - for (int i = nlocal; i < nlocal + nghost; i++) { - if ((id[i] == id_center) && (get_distance(x[temp_j], x[i]) < r)) { - r = get_distance(x[temp_j], x[i]); - } - } - - if (rates_flag) { - local_swap_probability[njswap_local] = - rate_list[type[temp_j] - 1] * exp(-MathSpecial::square(r / r_0)); - } else { - local_swap_probability[njswap_local] = exp(-MathSpecial::square(r / r_0)); - } - local_probability += local_swap_probability[njswap_local]; - - local_swap_type_list[njswap_local] = jswaptype; - local_swap_neighbor_list[njswap_local] = temp_j; - njswap_local++; - } - } - } - } - } - } else { - if (atom->mask[temp_j] & groupbit) { - if (diff_flag) { - // Calculate distance from i to each j, adjust probability of selection - // Get distance if own center atom - double r = INFINITY; - if (i_center >= 0) { r = get_distance(x[temp_j], x[i_center]); } - - // Get local id of ghost center atoms - for (int i = nlocal; i < nlocal + nghost; i++) { - if ((id[i] == id_center) && (get_distance(x[temp_j], x[i]) < r)) - r = get_distance(x[temp_j], x[i]); - } - - if (rates_flag) { - local_swap_probability[njswap_local] = - rate_list[type[temp_j] - 1] * exp(-MathSpecial::square(r / r_0)); - } else { - local_swap_probability[njswap_local] = exp(-MathSpecial::square(r / r_0)); - } - local_probability += local_swap_probability[njswap_local]; - - local_swap_type_list[njswap_local] = type[temp_j]; - local_swap_neighbor_list[njswap_local] = temp_j; - njswap_local++; - } else { - for (int jswaptype = 1; jswaptype < nswaptypes; jswaptype++) { - if (type[temp_j] == type_list[jswaptype]) { - // Calculate distance from i to each j, adjust probability of selection - // Get distance if own center atom - double r = INFINITY; - if (i_center >= 0) { double r = get_distance(x[temp_j], x[i_center]); } - - // Get local id of ghost center atom when ghost - for (int i = nlocal; i < nlocal + nghost; i++) { - if ((id[i] == id_center) && (get_distance(x[temp_j], x[i]) < r)) { - r = get_distance(x[temp_j], x[i]); - } - } - - if (rates_flag) { - local_swap_probability[njswap_local] = - rate_list[type[temp_j] - 1] * exp(-MathSpecial::square(r / r_0)); - } else { - local_swap_probability[njswap_local] = exp(-MathSpecial::square(r / r_0)); - } - local_probability += local_swap_probability[njswap_local]; - - local_swap_type_list[njswap_local] = jswaptype; - local_swap_neighbor_list[njswap_local] = temp_j; - njswap_local++; - } - } - } - } - } - } - - MPI_Allreduce(&njswap_local, &njswap, 1, MPI_INT, MPI_SUM, world); - MPI_Scan(&njswap_local, &njswap_before, 1, MPI_INT, MPI_SUM, world); - njswap_before -= njswap_local; - - MPI_Allreduce(&local_probability, &global_probability, 1, MPI_DOUBLE, MPI_SUM, world); - MPI_Scan(&local_probability, &prev_probability, 1, MPI_DOUBLE, MPI_SUM, world); - prev_probability -= local_probability; -} - -/* ---------------------------------------------------------------------- - update the list of swap atoms -------------------------------------------------------------------------- */ - -void FixNeighborSwap::update_iswap_atoms_list() -{ - int nlocal = atom->nlocal; - int *type = atom->type; - double **x = atom->x; - - if (atom->nmax > atom_swap_nmax) { - memory->sfree(local_swap_iatom_list); - atom_swap_nmax = atom->nmax; - local_swap_iatom_list = - (int *) memory->smalloc(atom_swap_nmax * sizeof(int), "MCSWAP:local_swap_iatom_list"); - } - - niswap_local = 0; - - if (region) { - - for (int i = 0; i < nlocal; i++) { - if (region->match(x[i][0], x[i][1], x[i][2]) == 1) { - if (atom->mask[i] & groupbit) { - if (type[i] == type_list[0]) { - local_swap_iatom_list[niswap_local] = i; - niswap_local++; - } - } - } - } - - } else { - for (int i = 0; i < nlocal; i++) { - if (atom->mask[i] & groupbit) { - if (type[i] == type_list[0]) { - local_swap_iatom_list[niswap_local] = i; - niswap_local++; - } - } - } - } - - MPI_Allreduce(&niswap_local, &niswap, 1, MPI_INT, MPI_SUM, world); - MPI_Scan(&niswap_local, &niswap_before, 1, MPI_INT, MPI_SUM, world); - niswap_before -= niswap_local; -} - -/* ---------------------------------------------------------------------- */ - -int FixNeighborSwap::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, - int * /*pbc*/) -{ - int i, j, m; - - int *type = atom->type; - double *q = atom->q; - - m = 0; - - if (atom->q_flag) { - for (i = 0; i < n; i++) { - j = list[i]; - buf[m++] = type[j]; - buf[m++] = q[j]; - } - } else { - for (i = 0; i < n; i++) { - j = list[i]; - buf[m++] = type[j]; - } - } - - return m; -} - -/* ---------------------------------------------------------------------- */ - -void FixNeighborSwap::unpack_forward_comm(int n, int first, double *buf) -{ - int i, m, last; - - int *type = atom->type; - double *q = atom->q; - - m = 0; - last = first + n; - - if (atom->q_flag) { - for (i = first; i < last; i++) { - type[i] = static_cast(buf[m++]); - q[i] = buf[m++]; - } - } else { - for (i = first; i < last; i++) type[i] = static_cast(buf[m++]); - } -} - -/* ---------------------------------------------------------------------- - return acceptance ratio -------------------------------------------------------------------------- */ - -double FixNeighborSwap::compute_vector(int n) -{ - if (n == 0) return nswap_attempts; - if (n == 1) return nswap_successes; - return 0.0; -} - -/* ---------------------------------------------------------------------- - memory usage of local atom-based arrays -------------------------------------------------------------------------- */ - -double FixNeighborSwap::memory_usage() -{ - double bytes = (double) atom_swap_nmax * sizeof(int); - return bytes; -} - -/* ---------------------------------------------------------------------- - pack entire state of Fix into one write -------------------------------------------------------------------------- */ - -void FixNeighborSwap::write_restart(FILE *fp) -{ - int n = 0; - double list[6]; - list[n++] = random_equal->state(); - list[n++] = ubuf(next_reneighbor).d; - list[n++] = nswap_attempts; - list[n++] = nswap_successes; - list[n++] = ubuf(update->ntimestep).d; - - if (comm->me == 0) { - int size = n * sizeof(double); - fwrite(&size, sizeof(int), 1, fp); - fwrite(list, sizeof(double), n, fp); - } -} - -/* ---------------------------------------------------------------------- - use state info from restart file to restart the Fix -------------------------------------------------------------------------- */ - -void FixNeighborSwap::restart(char *buf) -{ - int n = 0; - double *list = (double *) buf; - - seed = static_cast(list[n++]); - random_equal->reset(seed); - - next_reneighbor = (bigint) ubuf(list[n++]).i; - - nswap_attempts = static_cast(list[n++]); - nswap_successes = static_cast(list[n++]); - - bigint ntimestep_restart = (bigint) ubuf(list[n++]).i; - if (ntimestep_restart != update->ntimestep) - error->all(FLERR, "Must not reset timestep when restarting fix neighbor/swap"); -} diff --git a/src/MC/fix_neighbor_swap.h b/src/MC/fix_neighbor_swap.h deleted file mode 100644 index 821eda1bdc..0000000000 --- a/src/MC/fix_neighbor_swap.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - LAMMPS development team: developers@lammps.org - - Copyright (2003) Sandia Corporation. Under the terms of Contract - DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains - certain rights in this software. This software is distributed under - the GNU General Public License. - - See the README file in the top-level LAMMPS directory. -------------------------------------------------------------------------- */ - -#ifdef FIX_CLASS -// clang-format off -FixStyle(neighbor/swap,FixNeighborSwap); -// clang-format on -#else - -#ifndef LMP_FIX_NEIGH_SWAP_H -#define LMP_FIX_NEIGH_SWAP_H - -#include "fix.h" - -namespace LAMMPS_NS { - -class FixNeighborSwap : public Fix { - public: - FixNeighborSwap(class LAMMPS *, int, char **); - ~FixNeighborSwap(); - int setmask(); - void init(); - void pre_exchange(); - int pack_forward_comm(int, int *, double *, int, int *); - void unpack_forward_comm(int, int, double *); - double compute_vector(int); - double memory_usage(); - void write_restart(FILE *); - void restart(char *); - - private: - int nevery, seed; - int ke_flag; // yes = conserve ke, no = do not conserve ke - int diff_flag; // yes = simulate diffusion of central atom, no = swap only to certain types - int rates_flag; // yes = use modified type rates, no = swap rates are equivilent across types - int ncycles; - int niswap, njswap; // # of i,j swap atoms on all procs - int niswap_local, njswap_local; // # of swap atoms on this proc - int niswap_before, njswap_before; // # of swap atoms on procs < this proc - // int global_i_ID; // global id of selected i atom - class Region *region; // swap region - char *idregion; // swap region id - - int nswaptypes; - int jtype_selected; - int id_center; - double x_center; - double y_center; - double z_center; - int *type_list; - double *rate_list; - - double nswap_attempts; - double nswap_successes; - - bool unequal_cutoffs; - - int atom_swap_nmax; - double beta, r_0; - double local_probability; // Total swap probability stored on this proc - double global_probability; // Total swap probability across all proc - double prev_probability; // Swap probability on proc < this proc - double *qtype; - double energy_stored; - double **sqrt_mass_ratio; - double **voro_neighbor_list; - int *local_swap_iatom_list; - int *local_swap_neighbor_list; - int *local_swap_type_list; // Type list index of atoms stored on this proc - double *local_swap_probability; - - class RanPark *random_equal; - - class Compute *c_voro; - class Compute *c_pe; - - void options(int, char **); - int attempt_swap(); - double energy_full(); - int pick_i_swap_atom(); - int pick_j_swap_neighbor(int); - double get_distance(double[3], double[3]); - void build_i_neighbor_list(int); - void update_iswap_atoms_list(); -}; - -} // namespace LAMMPS_NS - -#endif -#endif diff --git a/src/PLUGIN/plugin.cpp b/src/PLUGIN/plugin.cpp index f4a0903be4..600d2fd8fa 100644 --- a/src/PLUGIN/plugin.cpp +++ b/src/PLUGIN/plugin.cpp @@ -21,6 +21,7 @@ #include "force.h" #include "input.h" #include "modify.h" +#include "update.h" #include #include @@ -57,6 +58,9 @@ void Plugin::command(int narg, char **arg) } else if (cmd == "clear") { plugin_clear(lmp); + } else if (cmd == "restore") { + plugin_restore(lmp, false); + } else if (cmd == "list") { if (comm->me == 0) { int num = plugin_get_num_plugins(); @@ -67,15 +71,16 @@ void Plugin::command(int narg, char **arg) } } } else - error->all(FLERR, "Illegal plugin command"); + error->all(FLERR, "Unknown plugin command {}", cmd); } // auto-load DSOs from designated folder(s) void plugin_auto_load(LAMMPS *lmp) { #if defined(LMP_PLUGIN) + bool oldverbose = verbose; + verbose = false; for (const auto &plugin_dir : platform::list_pathenv("LAMMPS_PLUGIN_PATH")) { - verbose = false; int count = 0; for (const auto &file : platform::list_directory(plugin_dir)) { if (utils::strmatch(file, "\\plugin.so$")) @@ -83,6 +88,7 @@ void plugin_auto_load(LAMMPS *lmp) } if (lmp->comm->me == 0) utils::logmesg(lmp, "Loaded {} plugins from {}\n", count, plugin_dir); } + verbose = oldverbose; #endif } @@ -249,6 +255,23 @@ void plugin_register(lammpsplugin_t *plugin, void *ptr) } (*command_map)[plugin->name] = (Input::CommandCreator) plugin->creator.v1; + } else if (pstyle == "run") { + auto integrate_map = lmp->update->integrate_map; + if (integrate_map->find(plugin->name) != integrate_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin->name); + } + (*integrate_map)[plugin->name] = (Update::IntegrateCreator) plugin->creator.v2; + + } else if (pstyle == "min") { + auto minimize_map = lmp->update->minimize_map; + if (minimize_map->find(plugin->name) != minimize_map->end()) { + if (lmp->comm->me == 0) + lmp->error->warning(FLERR, "Overriding built-in minimize style {} from plugin", + plugin->name); + } + (*minimize_map)[plugin->name] = (Update::MinimizeCreator) plugin->creator.v1; + } else { utils::logmesg(lmp, "Loading plugins for {} styles not yet implemented\n", pstyle); pluginlist.pop_back(); @@ -272,7 +295,8 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) (strcmp(style, "angle") != 0) && (strcmp(style, "dihedral") != 0) && (strcmp(style, "improper") != 0) && (strcmp(style, "kspace") != 0) && (strcmp(style, "compute") != 0) && (strcmp(style, "fix") != 0) && - (strcmp(style, "region") != 0) && (strcmp(style, "command") != 0)) { + (strcmp(style, "region") != 0) && (strcmp(style, "command") != 0) && + (strcmp(style, "run") != 0) && (strcmp(style, "min") != 0)) { if (me == 0) utils::logmesg(lmp, "Ignoring unload: {} is not a supported plugin style\n", style); return; @@ -301,9 +325,6 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) std::string pstyle = style; if (pstyle == "pair") { - auto found = lmp->force->pair_map->find(name); - if (found != lmp->force->pair_map->end()) lmp->force->pair_map->erase(found); - // must delete pair style instance if in use if (lmp->force->pair_style) { @@ -314,87 +335,121 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) } } - } else if (pstyle == "bond") { + auto found = lmp->force->pair_map->find(name); + if (found != lmp->force->pair_map->end()) lmp->force->pair_map->erase(found); - auto found = lmp->force->bond_map->find(name); - if (found != lmp->force->bond_map->end()) lmp->force->bond_map->erase(found); + } else if (pstyle == "bond") { // must delete bond style instance if in use if ((lmp->force->bond_style != nullptr) && (lmp->force->bond_match(name) != nullptr)) lmp->force->create_bond("none", 0); - } else if (pstyle == "angle") { + auto found = lmp->force->bond_map->find(name); + if (found != lmp->force->bond_map->end()) lmp->force->bond_map->erase(found); - auto found = lmp->force->angle_map->find(name); - if (found != lmp->force->angle_map->end()) lmp->force->angle_map->erase(found); + } else if (pstyle == "angle") { // must delete angle style instance if in use if ((lmp->force->angle_style != nullptr) && (lmp->force->angle_match(name) != nullptr)) lmp->force->create_angle("none", 0); - } else if (pstyle == "dihedral") { + auto found = lmp->force->angle_map->find(name); + if (found != lmp->force->angle_map->end()) lmp->force->angle_map->erase(found); - auto found = lmp->force->dihedral_map->find(name); - if (found != lmp->force->dihedral_map->end()) lmp->force->dihedral_map->erase(found); + } else if (pstyle == "dihedral") { // must delete dihedral style instance if in use if ((lmp->force->dihedral_style) && (lmp->force->dihedral_match(name) != nullptr)) lmp->force->create_dihedral("none", 0); - } else if (pstyle == "improper") { + auto found = lmp->force->dihedral_map->find(name); + if (found != lmp->force->dihedral_map->end()) lmp->force->dihedral_map->erase(found); - auto found = lmp->force->improper_map->find(name); - if (found != lmp->force->improper_map->end()) lmp->force->improper_map->erase(found); + } else if (pstyle == "improper") { // must delete improper style instance if in use if ((lmp->force->improper_style != nullptr) && (lmp->force->improper_match(name) != nullptr)) lmp->force->create_improper("none", 0); + auto found = lmp->force->improper_map->find(name); + if (found != lmp->force->improper_map->end()) lmp->force->improper_map->erase(found); + } else if (pstyle == "kspace") { + // must delete kspace style instance if in use + + if ((lmp->force->kspace_style != nullptr) && (lmp->force->kspace_match(name, 1) != nullptr)) + lmp->force->create_kspace("none", 0); + auto kspace_map = lmp->force->kspace_map; auto found = kspace_map->find(name); if (found != kspace_map->end()) kspace_map->erase(name); } else if (pstyle == "compute") { - auto compute_map = lmp->modify->compute_map; - auto found = compute_map->find(name); - if (found != compute_map->end()) compute_map->erase(name); - // must delete all compute instances using this compute style for (auto &icompute : lmp->modify->get_compute_by_style(name)) lmp->modify->delete_compute(icompute->id); - } else if (pstyle == "fix") { + auto compute_map = lmp->modify->compute_map; + auto found = compute_map->find(name); + if (found != compute_map->end()) compute_map->erase(name); - auto fix_map = lmp->modify->fix_map; - auto found = fix_map->find(name); - if (found != fix_map->end()) fix_map->erase(name); + } else if (pstyle == "fix") { // must delete all fix instances using this fix style for (auto &ifix : lmp->modify->get_fix_by_style(name)) lmp->modify->delete_fix(ifix->id); + auto fix_map = lmp->modify->fix_map; + auto found = fix_map->find(name); + if (found != fix_map->end()) fix_map->erase(name); + } else if (pstyle == "region") { + // must delete all region instances using this region style + + for (auto &iregion : lmp->domain->get_region_by_style(name)) + lmp->domain->delete_region(iregion); + auto region_map = lmp->domain->region_map; auto found = region_map->find(name); if (found != region_map->end()) region_map->erase(name); - for (auto &iregion : lmp->domain->get_region_by_style(name)) - lmp->domain->delete_region(iregion); - } else if (pstyle == "command") { auto command_map = lmp->input->command_map; auto found = command_map->find(name); if (found != command_map->end()) command_map->erase(name); + + } else if (pstyle == "run") { + + // must restore default run style if plugin style is in use + + if (strcmp(name, lmp->update->integrate_style) == 0) { + char *str = (char *) "verlet"; + lmp->update->create_integrate(1, &str, 1); + } + auto integrate_map = lmp->update->integrate_map; + auto found = integrate_map->find(name); + if (found != integrate_map->end()) integrate_map->erase(name); + + } else if (pstyle == "min") { + + // must restore default minimize style if plugin style is in use + + if (strcmp(name, lmp->update->minimize_style) == 0) { + char *str = (char *) "cg"; + lmp->update->create_minimize(1, &str, 1); + } + auto minimize_map = lmp->update->minimize_map; + auto found = minimize_map->find(name); + if (found != minimize_map->end()) minimize_map->erase(name); } // if reference count is down to zero, close DSO handle. @@ -404,18 +459,157 @@ void plugin_unload(const char *style, const char *name, LAMMPS *lmp) #endif } +/* -------------------------------------------------------------------- + restore previously loaded plugins + -------------------------------------------------------------------- */ + +void plugin_restore(LAMMPS *lmp, bool warnflag) +{ + for (auto &plugin : pluginlist) { + if (lmp->comm->me == 0) + utils::logmesg(lmp, "Restoring plugin: {} by {}\n", plugin.info, plugin.author); + + std::string pstyle = plugin.style; + if (pstyle == "pair") { + auto pair_map = lmp->force->pair_map; + if (pair_map->find(plugin.name) != pair_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in pair style {} from plugin", plugin.name); + } + (*pair_map)[plugin.name] = (Force::PairCreator) plugin.creator.v1; + + } else if (pstyle == "bond") { + auto bond_map = lmp->force->bond_map; + if (bond_map->find(plugin.name) != bond_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in bond style {} from plugin", plugin.name); + } + (*bond_map)[plugin.name] = (Force::BondCreator) plugin.creator.v1; + + } else if (pstyle == "angle") { + auto angle_map = lmp->force->angle_map; + if (angle_map->find(plugin.name) != angle_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in angle style {} from plugin", plugin.name); + } + (*angle_map)[plugin.name] = (Force::AngleCreator) plugin.creator.v1; + + } else if (pstyle == "dihedral") { + auto dihedral_map = lmp->force->dihedral_map; + if (dihedral_map->find(plugin.name) != dihedral_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in dihedral style {} from plugin", + plugin.name); + } + (*dihedral_map)[plugin.name] = (Force::DihedralCreator) plugin.creator.v1; + + } else if (pstyle == "improper") { + auto improper_map = lmp->force->improper_map; + if (improper_map->find(plugin.name) != improper_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in improper style {} from plugin", + plugin.name); + } + (*improper_map)[plugin.name] = (Force::ImproperCreator) plugin.creator.v1; + + } else if (pstyle == "kspace") { + auto kspace_map = lmp->force->kspace_map; + if (kspace_map->find(plugin.name) != kspace_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in kspace style {} from plugin", + plugin.name); + } + (*kspace_map)[plugin.name] = (Force::KSpaceCreator) plugin.creator.v1; + + } else if (pstyle == "compute") { + auto compute_map = lmp->modify->compute_map; + if (compute_map->find(plugin.name) != compute_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in compute style {} from plugin", + plugin.name); + } + (*compute_map)[plugin.name] = (Modify::ComputeCreator) plugin.creator.v2; + + } else if (pstyle == "fix") { + auto fix_map = lmp->modify->fix_map; + if (fix_map->find(plugin.name) != fix_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in fix style {} from plugin", plugin.name); + } + (*fix_map)[plugin.name] = (Modify::FixCreator) plugin.creator.v2; + + } else if (pstyle == "region") { + auto region_map = lmp->domain->region_map; + if (region_map->find(plugin.name) != region_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in region style {} from plugin", + plugin.name); + } + (*region_map)[plugin.name] = (Domain::RegionCreator) plugin.creator.v2; + + } else if (pstyle == "command") { + auto command_map = lmp->input->command_map; + if (command_map->find(plugin.name) != command_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in command style {} from plugin", + plugin.name); + } + (*command_map)[plugin.name] = (Input::CommandCreator) plugin.creator.v1; + + } else if (pstyle == "run") { + auto integrate_map = lmp->update->integrate_map; + if (integrate_map->find(plugin.name) != integrate_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in run style {} from plugin", plugin.name); + } + (*integrate_map)[plugin.name] = (Update::IntegrateCreator) plugin.creator.v2; + + } else if (pstyle == "min") { + auto minimize_map = lmp->update->minimize_map; + if (minimize_map->find(plugin.name) != minimize_map->end()) { + if (warnflag && (lmp->comm->me == 0)) + lmp->error->warning(FLERR, "Overriding built-in minimize style {} from plugin", + plugin.name); + } + (*minimize_map)[plugin.name] = (Update::MinimizeCreator) plugin.creator.v1; + } + } +} + /* -------------------------------------------------------------------- unload all loaded plugins -------------------------------------------------------------------- */ void plugin_clear(LAMMPS *lmp) { - verbose = false; + bool oldverbose = verbose; + verbose = true; while (pluginlist.size() > 0) { auto p = pluginlist.begin(); plugin_unload(p->style, p->name, lmp); } - verbose = true; + verbose = oldverbose; +} + +/* -------------------------------------------------------------------- + unload all shared objects + -------------------------------------------------------------------- */ + +void plugin_finalize() +{ +#if defined(LMP_PLUGIN) + while (pluginlist.size() > 0) { + auto p = pluginlist.begin(); + + void *handle = p->handle; + plugin_erase(p->style, p->name); + + // if reference count is down to zero, close DSO handle. + + --dso_refcounter[handle]; + if (dso_refcounter[handle] == 0) { platform::dlclose(handle); } + } +#endif } /* -------------------------------------------------------------------- diff --git a/src/PLUGIN/plugin.h b/src/PLUGIN/plugin.h index 4d8d6c29f8..838f32581c 100644 --- a/src/PLUGIN/plugin.h +++ b/src/PLUGIN/plugin.h @@ -34,10 +34,12 @@ class Plugin : public Command { void plugin_auto_load(LAMMPS *); int plugin_load(const char *, LAMMPS *); void plugin_register(lammpsplugin_t *, void *); +void plugin_restore(LAMMPS *, bool); void plugin_unload(const char *, const char *, LAMMPS *); void plugin_erase(const char *, const char *); void plugin_clear(LAMMPS *); +void plugin_finalize(); int plugin_get_num_plugins(); int plugin_find(const char *, const char *); diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index e4ec8d74d8..4ed208fb2f 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -17,6 +17,7 @@ #include "python_impl.h" +#include "comm.h" #include "error.h" #include "input.h" #include "memory.h" @@ -62,6 +63,7 @@ using namespace LAMMPS_NS; enum { NONE, INT, DOUBLE, STRING, PTR }; +enum { VALUE, VARIABLE, INTERNALVAR }; /* ---------------------------------------------------------------------- */ @@ -94,20 +96,27 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Inform python intialization scheme of the mliappy module. // This -must- happen before python is initialized. int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); + if (err) + error->all(FLERR, Error::NOLASTLINE, "Could not register MLIAPPY embedded python module."); err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); + if (err) + error->all(FLERR, Error::NOLASTLINE, + "Could not register MLIAPPY unified embedded python module."); #ifdef LMP_KOKKOS // Inform python intialization scheme of the mliappy module. // This -must- happen before python is initialized. err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", PyInit_mliap_model_python_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python KOKKOS module."); + if (err) + error->all(FLERR, Error::NOLASTLINE, + "Could not register MLIAPPY embedded python KOKKOS module."); err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python KOKKOS module."); + if (err) + error->all(FLERR, Error::NOLASTLINE, + "Could not register MLIAPPY unified embedded python KOKKOS module."); #endif } #endif @@ -129,7 +138,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) PyUtils::GIL lock; PyObject *pModule = PyImport_AddModule("__main__"); - if (!pModule) error->all(FLERR, "Could not initialize embedded Python"); + if (!pModule) error->all(FLERR, Error::NOLASTLINE, "Could not initialize embedded Python"); pyMain = (void *) pModule; } @@ -158,23 +167,31 @@ void PythonImpl::command(int narg, char **arg) { if (narg < 2) utils::missing_cmd_args(FLERR, "python", error); - // if invoke is only keyword, invoke the previously defined function + // if invoke keyword is used, invoke the previously defined function - if (narg == 2 && strcmp(arg[1], "invoke") == 0) { + if (strcmp(arg[1], "invoke") == 0) { int ifunc = find(arg[0]); - if (ifunc < 0) error->all(FLERR, "Python invoke of unknown function: {}", arg[0]); + if (ifunc < 0) + error->all(FLERR, Error::ARGZERO, "Python invoke of unknown function: {}", arg[0]); char *str = nullptr; if (pfuncs[ifunc].noutput) { str = input->variable->pythonstyle(pfuncs[ifunc].ovarname, pfuncs[ifunc].name); if (!str) - error->all(FLERR, - "Python variable {} does not match variable {} " - "registered with Python function {}", - arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name); + error->all( + FLERR, Error::ARGZERO, + "Python variable {} does not match variable {} registered with Python function {}", + arg[0], pfuncs[ifunc].ovarname, pfuncs[ifunc].name); } - invoke_function(ifunc, str); + bool logreturn = false; + if (narg == 3 && strcmp(arg[2], "logreturn") == 0) logreturn = true; + + invoke_function(ifunc, str, nullptr); + + if (logreturn && str && (comm->me == 0)) + utils::logmesg(lmp, "Invoked python function {} returned {}\n", arg[0], str); + return; } @@ -189,7 +206,7 @@ void PythonImpl::command(int narg, char **arg) if (platform::file_is_readable(arg[1])) err = execute_file(arg[1]); else - error->all(FLERR, "Could not open python source file {} for processing", arg[1]); + error->all(FLERR, 1, "Could not open python source file {} for processing", arg[1]); } if (err) error->all(FLERR, "Failure in python source command"); @@ -213,7 +230,8 @@ void PythonImpl::command(int narg, char **arg) if (strcmp(arg[iarg], "input") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python input", error); ninput = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (ninput < 0) error->all(FLERR, "Invalid number of python input arguments: {}", ninput); + if (ninput < 0) + error->all(FLERR, iarg + 1, "Invalid number of python input arguments: {}", ninput); iarg += 2; delete[] istr; istr = new char *[ninput]; @@ -232,7 +250,8 @@ void PythonImpl::command(int narg, char **arg) } else if (strcmp(arg[iarg], "length") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python length", error); length_longstr = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (length_longstr <= 0) error->all(FLERR, "Invalid python return value length"); + if (length_longstr <= 0) + error->all(FLERR, iarg + 1, "Invalid python return value length {}", length_longstr); iarg += 2; } else if (strcmp(arg[iarg], "file") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "python file", error); @@ -247,15 +266,18 @@ void PythonImpl::command(int narg, char **arg) existflag = 1; iarg++; } else - error->all(FLERR, "Unknown python command keyword: {}", arg[iarg]); + error->all(FLERR, iarg, "Unknown python command keyword: {}", arg[iarg]); } if (pyfile && herestr) - error->all(FLERR, "Must not use python 'file' and 'here' keywords at the same time"); + error->all(FLERR, Error::NOLASTLINE, + "Must not use python 'file' and 'here' keywords at the same time"); if (pyfile && existflag) - error->all(FLERR, "Must not use python 'file' and 'exists' keywords at the same time"); + error->all(FLERR, Error::NOLASTLINE, + "Must not use python 'file' and 'exists' keywords at the same time"); if (herestr && existflag) - error->all(FLERR, "Must not use python 'here' and 'exists' keywords at the same time"); + error->all(FLERR, Error::NOLASTLINE, + "Must not use python 'here' and 'exists' keywords at the same time"); // create or overwrite entry in pfuncs vector with name = arg[0] @@ -273,13 +295,13 @@ void PythonImpl::command(int narg, char **arg) if (fp == nullptr) { PyUtils::Print_Errors(); - error->all(FLERR, "Could not open Python file: {}", pyfile); + error->all(FLERR, Error::NOLASTLINE, "Could not open Python file: {}", pyfile); } int err = PyRun_SimpleFile(fp, pyfile); if (err) { PyUtils::Print_Errors(); - error->all(FLERR, "Could not process Python file: {}", pyfile); + error->all(FLERR, Error::NOLASTLINE, "Could not process Python file: {}", pyfile); } fclose(fp); @@ -287,7 +309,7 @@ void PythonImpl::command(int narg, char **arg) int err = PyRun_SimpleString(herestr); if (err) { PyUtils::Print_Errors(); - error->all(FLERR, "Could not process Python string: {}", herestr); + error->all(FLERR, Error::NOLASTLINE, "Could not process Python string: {}", herestr); } } @@ -298,12 +320,12 @@ void PythonImpl::command(int narg, char **arg) if (!pFunc) { PyUtils::Print_Errors(); - error->all(FLERR, "Could not find Python function {}", pfuncs[ifunc].name); + error->all(FLERR, Error::NOLASTLINE, "Could not find Python function {}", pfuncs[ifunc].name); } if (!PyCallable_Check(pFunc)) { PyUtils::Print_Errors(); - error->all(FLERR, "Python function {} is not callable", pfuncs[ifunc].name); + error->all(FLERR, Error::NOLASTLINE, "Python function {} is not callable", pfuncs[ifunc].name); } pfuncs[ifunc].pFunc = (void *) pFunc; @@ -317,7 +339,7 @@ void PythonImpl::command(int narg, char **arg) /* ------------------------------------------------------------------ */ -void PythonImpl::invoke_function(int ifunc, char *result) +void PythonImpl::invoke_function(int ifunc, char *result, double *dvalue) { PyUtils::GIL lock; PyObject *pValue; @@ -331,44 +353,54 @@ void PythonImpl::invoke_function(int ifunc, char *result) PyObject *pArgs = PyTuple_New(ninput); if (!pArgs) - error->all(FLERR, "Could not prepare arguments for Python function {}", pfuncs[ifunc].name); + error->all(FLERR, Error::NOLASTLINE, "Could not prepare arguments for Python function {}", + pfuncs[ifunc].name); for (int i = 0; i < ninput; i++) { int itype = pfuncs[ifunc].itype[i]; if (itype == INT) { - if (pfuncs[ifunc].ivarflag[i]) { + if (pfuncs[ifunc].ivarflag[i] == VARIABLE) { str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); if (!str) - error->all(FLERR, "Could not evaluate Python function {} input variable: {}", - pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); + error->all(FLERR, Error::NOLASTLINE, + "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name, + pfuncs[ifunc].svalue[i]); pValue = PY_INT_FROM_LONG(PY_LONG_FROM_STRING(str)); + } else if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) { + double value = input->variable->compute_equal(pfuncs[ifunc].internal_var[i]); + pValue = PyLong_FromDouble(value); } else { pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]); } } else if (itype == DOUBLE) { - if (pfuncs[ifunc].ivarflag[i]) { + if (pfuncs[ifunc].ivarflag[i] == VARIABLE) { str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); if (!str) - error->all(FLERR, "Could not evaluate Python function {} input variable: {}", - pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); + error->all(FLERR, Error::NOLASTLINE, + "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name, + pfuncs[ifunc].svalue[i]); pValue = PyFloat_FromDouble(std::stod(str)); + } else if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) { + double value = input->variable->compute_equal(pfuncs[ifunc].internal_var[i]); + pValue = PyFloat_FromDouble(value); } else { pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]); } } else if (itype == STRING) { - if (pfuncs[ifunc].ivarflag[i]) { + if (pfuncs[ifunc].ivarflag[i] == VARIABLE) { str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); if (!str) - error->all(FLERR, "Could not evaluate Python function {} input variable: {}", - pfuncs[ifunc].name, pfuncs[ifunc].svalue[i]); + error->all(FLERR, Error::NOLASTLINE, + "Could not evaluate Python function {} input variable: {}", pfuncs[ifunc].name, + pfuncs[ifunc].svalue[i]); pValue = PyUnicode_FromString(str); } else { pValue = PyUnicode_FromString(pfuncs[ifunc].svalue[i]); } } else if (itype == PTR) { - pValue = PyCapsule_New((void *)lmp, nullptr, nullptr); + pValue = PyCapsule_New((void *) lmp, nullptr, nullptr); } else { - error->all(FLERR, "Unsupported variable type: {}", itype); + error->all(FLERR, Error::NOLASTLINE, "Unsupported variable type: {}", itype); } PyTuple_SetItem(pArgs, i, pValue); } @@ -381,29 +413,37 @@ void PythonImpl::invoke_function(int ifunc, char *result) if (!pValue) { PyUtils::Print_Errors(); - error->one(FLERR, "Python evaluation of function {} failed", pfuncs[ifunc].name); + error->one(FLERR, Error::NOLASTLINE, "Python evaluation of function {} failed", + pfuncs[ifunc].name); } // function returned a value - // assign it to result string stored by python-style variable - // or if user specified a length, assign it to longstr + // if result is non-NULL, assign to result string stored by python-style variable + // or if value is string and user specified a length, assign it to longstr + // if dvalue is non-NULL, assign numeric value directly to dvalue if (pfuncs[ifunc].noutput) { int otype = pfuncs[ifunc].otype; if (otype == INT) { - auto value = fmt::format("{}", PY_INT_AS_LONG(pValue)); - strncpy(result, value.c_str(), Variable::VALUELENGTH - 1); + if (dvalue) *dvalue = (double) PY_INT_AS_LONG(pValue); + if (result) { + auto value = fmt::format("{}", PY_INT_AS_LONG(pValue)); + strncpy(result, value.c_str(), Variable::VALUELENGTH - 1); + } } else if (otype == DOUBLE) { - auto value = fmt::format("{:.15g}", PyFloat_AsDouble(pValue)); - strncpy(result, value.c_str(), Variable::VALUELENGTH - 1); + if (dvalue) *dvalue = PyFloat_AsDouble(pValue); + if (result) { + auto value = fmt::format("{:.15g}", PyFloat_AsDouble(pValue)); + strncpy(result, value.c_str(), Variable::VALUELENGTH - 1); + } } else if (otype == STRING) { const char *pystr = PyUnicode_AsUTF8(pValue); if (pfuncs[ifunc].longstr) strncpy(pfuncs[ifunc].longstr, pystr, pfuncs[ifunc].length_longstr); - else - strncpy(result, pystr, Variable::VALUELENGTH - 1); + if (result) strncpy(result, pystr, Variable::VALUELENGTH - 1); } } + Py_CLEAR(pValue); } @@ -416,15 +456,80 @@ int PythonImpl::find(const char *name) return -1; } -/* ------------------------------------------------------------------ */ +/* --------------------------------------------------------------------- + called by Variable class when a python-style variable is evaluated + this will call invoke_function() in this class + either via Variable::retrieve() or Variable::equalstyle + retrieve calls with numeric = 0, equalstyle with numeric = 1 + ensure name matches a Python function + ensure the Python function produces an output + ensure the Python function outputs to the matching python-style variable + ensure a string is returned only if retrieve() is the caller +--------------------------------------------------------------------- */ -int PythonImpl::variable_match(const char *name, const char *varname, int numeric) +int PythonImpl::function_match(const char *name, const char *varname, int numeric, Error *error) { int ifunc = find(name); - if (ifunc < 0) return -1; - if (pfuncs[ifunc].noutput == 0) return -2; - if (strcmp(pfuncs[ifunc].ovarname, varname) != 0) return -3; - if (numeric && pfuncs[ifunc].otype == STRING) return -4; + + if (ifunc < 0) + error->all(FLERR, Error::NOLASTLINE, "Python function {} specified by variable {} not found", + name, varname); + if (pfuncs[ifunc].noutput == 0) + error->all(FLERR, Error::NOLASTLINE, + "Python function {} for variable {} does not return a value", name, varname); + if (strcmp(pfuncs[ifunc].ovarname, varname) != 0) + error->all(FLERR, Error::NOLASTLINE, + "Python function {} and variable {} do not link to each other", name, varname); + if (numeric && pfuncs[ifunc].otype == STRING) + error->all(FLERR, Error::NOLASTLINE, "Python function {} for variable {} returns a string", + name, varname); + + return ifunc; +} + +/* --------------------------------------------------------------------- + called by Variable class when evaluating a Python wrapper function + which will call invoke_function() + either via equal-style or atom-style variable formula + the latter calls invoke_function() once per atom + same error checks as function_match() plus 2 new ones + ensure match of number of Python function args mapped to internal-style variables + ensure each internal-style variable still exists + must check now in case user input script deleted variables between runs + which could invalidate indices set in create_event() + other classes avoid this issue by setting variable indices in their init() method +--------------------------------------------------------------------- */ + +int PythonImpl::wrapper_match(const char *name, const char *varname, int narg, int *argvars, + Error *error) +{ + int ifunc = function_match(name, varname, 1, error); + if (ifunc < 0) return ifunc; + + int internal_count = 0; + for (int i = 0; i < pfuncs[ifunc].ninput; i++) + if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) internal_count++; + if (internal_count != narg) + error->all(FLERR, Error::NOLASTLINE, + "Python function {} does not use {} internal variable args", name, narg); + + // set argvars of internal-style variables for use by Variable class + // in Python wrapper functions + // also set internal_var for use by invoke_function() + // so that invoke_function() is as fast as possible for args which are internal-style vars + + int j = 0; + for (int i = 0; i < pfuncs[ifunc].ninput; i++) { + if (pfuncs[ifunc].ivarflag[i] == INTERNALVAR) { + int ivar = input->variable->find(pfuncs[ifunc].svalue[i]); + if (ivar < 0) + error->all(FLERR, Error::NOLASTLINE, "Python function {} cannot find internal variable {}", + name, pfuncs[ifunc].svalue[i]); + pfuncs[ifunc].internal_var[i] = ivar; + argvars[j++] = ivar; + } + } + return ifunc; } @@ -457,9 +562,10 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon pfuncs[ifunc].noutput = noutput; if (!format && ninput + noutput) - error->all(FLERR, "Missing python format keyword"); + error->all(FLERR, Error::NOLASTLINE, "Missing python format keyword"); else if (format && ((int) strlen(format) != ninput + noutput)) - error->all(FLERR, "Input/output arguments ({}) and format characters ({}) are inconsistent", + error->all(FLERR, Error::NOLASTLINE, + "Input/output arguments ({}) and format characters ({}) are inconsistent", (ninput + noutput), strlen(format)); // process inputs as values or variables @@ -469,6 +575,7 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon pfuncs[ifunc].ivalue = new int[ninput]; pfuncs[ifunc].dvalue = new double[ninput]; pfuncs[ifunc].svalue = new char *[ninput]; + pfuncs[ifunc].internal_var = new int[ninput]; for (int i = 0; i < ninput; i++) { pfuncs[ifunc].svalue[i] = nullptr; @@ -476,37 +583,64 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon if (type == 'i') { pfuncs[ifunc].itype[i] = INT; if (utils::strmatch(istr[i], "^v_")) { - pfuncs[ifunc].ivarflag[i] = 1; + pfuncs[ifunc].ivarflag[i] = VARIABLE; pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); + } else if (utils::strmatch(istr[i], "^iv_")) { + pfuncs[ifunc].ivarflag[i] = INTERNALVAR; + pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 3); + char *vname = pfuncs[ifunc].svalue[i]; + int ivar = input->variable->find(vname); + if (ivar < 0) { // create internal variable if does not exist + input->variable->internal_create(vname, 0.0); + ivar = input->variable->find(vname); + } + if (!input->variable->internalstyle(ivar)) + error->all(FLERR, Error::NOLASTLINE, "Variable {} for python command is invalid style", + vname); } else { - pfuncs[ifunc].ivarflag[i] = 0; + pfuncs[ifunc].ivarflag[i] = VALUE; pfuncs[ifunc].ivalue[i] = utils::inumeric(FLERR, istr[i], false, lmp); } } else if (type == 'f') { pfuncs[ifunc].itype[i] = DOUBLE; if (utils::strmatch(istr[i], "^v_")) { - pfuncs[ifunc].ivarflag[i] = 1; + pfuncs[ifunc].ivarflag[i] = VARIABLE; pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); + } else if (utils::strmatch(istr[i], "^iv_")) { + pfuncs[ifunc].ivarflag[i] = INTERNALVAR; + pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 3); + char *vname = pfuncs[ifunc].svalue[i]; + int ivar = input->variable->find(vname); + if (ivar < 0) { // create internal variable if does not exist + input->variable->internal_create(vname, 0.0); + ivar = input->variable->find(vname); + } + if (!input->variable->internalstyle(ivar)) + error->all(FLERR, Error::NOLASTLINE, "Variable {} for python command is invalid style", + vname); } else { - pfuncs[ifunc].ivarflag[i] = 0; + pfuncs[ifunc].ivarflag[i] = VALUE; pfuncs[ifunc].dvalue[i] = utils::numeric(FLERR, istr[i], false, lmp); } } else if (type == 's') { pfuncs[ifunc].itype[i] = STRING; if (utils::strmatch(istr[i], "^v_")) { - pfuncs[ifunc].ivarflag[i] = 1; + pfuncs[ifunc].ivarflag[i] = VARIABLE; pfuncs[ifunc].svalue[i] = utils::strdup(istr[i] + 2); + } else if (utils::strmatch(istr[i], "^iv_")) { + error->all(FLERR, Error::NOLASTLINE, + "Input argument {} cannot be internal variable with string format", istr[i]); } else { - pfuncs[ifunc].ivarflag[i] = 0; + pfuncs[ifunc].ivarflag[i] = VALUE; pfuncs[ifunc].svalue[i] = utils::strdup(istr[i]); } } else if (type == 'p') { - pfuncs[ifunc].ivarflag[i] = 0; + pfuncs[ifunc].ivarflag[i] = VALUE; pfuncs[ifunc].itype[i] = PTR; if (strcmp(istr[i], "SELF") != 0) error->all(FLERR, "Invalid python command"); } else - error->all(FLERR, "Invalid python format character: {}", type); + error->all(FLERR, Error::NOLASTLINE, "Invalid python format character: {}", type); } // process output as value or variable @@ -523,17 +657,19 @@ int PythonImpl::create_entry(char *name, int ninput, int noutput, int length_lon else if (type == 's') pfuncs[ifunc].otype = STRING; else - error->all(FLERR, "Invalid python return format character: {}", type); + error->all(FLERR, Error::NOLASTLINE, "Invalid python return format character: {}", type); if (length_longstr) { if (pfuncs[ifunc].otype != STRING) - error->all(FLERR, "Python command length keyword cannot be used unless output is a string"); + error->all(FLERR, Error::NOLASTLINE, + "Python command length keyword 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"); + if (strstr(ostr, "v_") != ostr) + error->all(FLERR, Error::NOLASTLINE, "Invalid python output variable name {}", ostr); pfuncs[ifunc].ovarname = utils::strdup(ostr + 2); return ifunc; @@ -574,6 +710,7 @@ void PythonImpl::deallocate(int i) delete[] pfuncs[i].dvalue; for (int j = 0; j < pfuncs[i].ninput; j++) delete[] pfuncs[i].svalue[j]; delete[] pfuncs[i].svalue; + delete[] pfuncs[i].internal_var; delete[] pfuncs[i].ovarname; delete[] pfuncs[i].longstr; } diff --git a/src/PYTHON/python_impl.h b/src/PYTHON/python_impl.h index 667980f8aa..5c3955afc6 100644 --- a/src/PYTHON/python_impl.h +++ b/src/PYTHON/python_impl.h @@ -25,9 +25,10 @@ class PythonImpl : protected Pointers, public PythonInterface { PythonImpl(class LAMMPS *); ~PythonImpl() override; void command(int, char **) override; - void invoke_function(int, char *) override; + void invoke_function(int, char *, double *) override; int find(const char *) override; - int variable_match(const char *, const char *, int) override; + int function_match(const char *, const char *, int, Error *) override; + int wrapper_match(const char *, const char *, int, int *, Error *) override; char *long_string(int) override; int execute_string(char *) override; int execute_file(char *) override; @@ -44,6 +45,7 @@ class PythonImpl : protected Pointers, public PythonInterface { int *ivalue; double *dvalue; char **svalue; + int *internal_var; // stores per-arg index of internal variable int otype; char *ovarname; char *longstr; diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index 6ba6954541..970418f49b 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -3109,7 +3109,7 @@ void FixBondReact::update_everything() update_num_mega++; } MPI_Allreduce(MPI_IN_PLACE, &noccur[0], nreacts, MPI_INT, MPI_SUM, world); - reaction_count_total[rxnID] += noccur[rxnID]; + for (rxnID = 0; rxnID < nreacts; rxnID++) reaction_count_total[rxnID] += noccur[rxnID]; } else if (pass == 1) { for (int i = 0; i < global_megasize; i++) { rxnID = (int) global_mega_glove[0][i]; diff --git a/src/RHEO/compute_rheo_kernel.cpp b/src/RHEO/compute_rheo_kernel.cpp index 61aad6a9b8..e44e356133 100644 --- a/src/RHEO/compute_rheo_kernel.cpp +++ b/src/RHEO/compute_rheo_kernel.cpp @@ -227,7 +227,7 @@ double ComputeRHEOKernel::calc_dw(int i, int j, double delx, double dely, double int corrections_i = check_corrections(i); int corrections_j = check_corrections(j); - wp = calc_dw_scalar_quintic(delx, dely, delz, r); + wp = calc_dw_scalar_quintic(r); // Overwrite if there are corrections double dxij[3] = {delx, dely, delz}; @@ -279,7 +279,7 @@ double ComputeRHEOKernel::calc_w_quintic(double r) /* ---------------------------------------------------------------------- */ -double ComputeRHEOKernel::calc_dw_scalar_quintic(double delx, double dely, double delz, double r) +double ComputeRHEOKernel::calc_dw_scalar_quintic(double r) { double wp, tmp1, tmp2, tmp3, tmp1sq, tmp2sq, tmp3sq, s; @@ -312,7 +312,7 @@ double ComputeRHEOKernel::calc_dw_scalar_quintic(double delx, double dely, doubl double ComputeRHEOKernel::calc_dw_quintic(double delx, double dely, double delz, double r, double *dW1, double *dW2) { - double wp = calc_dw_scalar_quintic(delx, dely, delz, r); + double wp = calc_dw_scalar_quintic(r); double wprinv = wp / r; dW1[0] = delx * wprinv; diff --git a/src/RHEO/compute_rheo_kernel.h b/src/RHEO/compute_rheo_kernel.h index 037e9e3683..10f7baf425 100644 --- a/src/RHEO/compute_rheo_kernel.h +++ b/src/RHEO/compute_rheo_kernel.h @@ -40,7 +40,7 @@ class ComputeRHEOKernel : public Compute { double calc_w(int, int, double, double, double, double); double calc_dw(int, int, double, double, double, double); double calc_w_quintic(double); - double calc_dw_scalar_quintic(double, double, double, double); + double calc_dw_scalar_quintic(double); double calc_dw_quintic(double, double, double, double, double *, double *); double calc_w_wendlandc4(double); double calc_dw_wendlandc4(double, double, double, double, double *, double *); diff --git a/src/RHEO/fix_rheo_pressure.cpp b/src/RHEO/fix_rheo_pressure.cpp index 6088c44348..16a96ef430 100644 --- a/src/RHEO/fix_rheo_pressure.cpp +++ b/src/RHEO/fix_rheo_pressure.cpp @@ -289,7 +289,7 @@ double FixRHEOPressure::calc_rho(double p, int i) /* ---------------------------------------------------------------------- */ -double FixRHEOPressure::calc_csq(double p, int i) +double FixRHEOPressure::calc_csq(double /*p*/, int i) { int type = atom->type[i]; double csq2 = csq[type]; diff --git a/src/VORONOI/compute_voronoi_atom.cpp b/src/VORONOI/compute_voronoi_atom.cpp index 12ea173a23..f6de73e73e 100644 --- a/src/VORONOI/compute_voronoi_atom.cpp +++ b/src/VORONOI/compute_voronoi_atom.cpp @@ -40,10 +40,9 @@ static constexpr int FACESDELTA = 10000; /* ---------------------------------------------------------------------- */ ComputeVoronoi::ComputeVoronoi(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), con_mono(nullptr), con_poly(nullptr), - radstr(nullptr), voro(nullptr), edge(nullptr), sendvector(nullptr), - rfield(nullptr), tags(nullptr), occvec(nullptr), sendocc(nullptr), - lroot(nullptr), lnext(nullptr), faces(nullptr) + Compute(lmp, narg, arg), con_mono(nullptr), con_poly(nullptr), radstr(nullptr), + voro(nullptr), edge(nullptr), sendvector(nullptr), rfield(nullptr), tags(nullptr), + occvec(nullptr), lroot(nullptr), lnext(nullptr), faces(nullptr) { int sgroup; @@ -156,9 +155,6 @@ ComputeVoronoi::~ComputeVoronoi() memory->destroy(lroot); memory->destroy(lnext); memory->destroy(occvec); -#ifdef NOTINPLACE - memory->destroy(sendocc); -#endif memory->destroy(tags); memory->destroy(faces); } @@ -211,9 +207,6 @@ void ComputeVoronoi::compute_peratom() oldnatoms = atom->natoms; oldmaxtag = atom->map_tag_max; memory->create(occvec,oldmaxtag,"voronoi/atom:occvec"); -#ifdef NOTINPLACE - memory->create(sendocc,oldmaxtag,"voronoi/atom:sendocc"); -#endif } // get the occupation of each original voronoi cell @@ -429,12 +422,7 @@ void ComputeVoronoi::checkOccupation() // MPI sum occupation -#ifdef NOTINPLACE - memcpy(sendocc, occvec, oldnatoms*sizeof(*occvec)); - MPI_Allreduce(sendocc, occvec, oldnatoms, MPI_INT, MPI_SUM, world); -#else MPI_Allreduce(MPI_IN_PLACE, occvec, oldnatoms, MPI_INT, MPI_SUM, world); -#endif // determine the total number of atoms in this atom's currently occupied cell diff --git a/src/VORONOI/compute_voronoi_atom.h b/src/VORONOI/compute_voronoi_atom.h index fbcf14c9bc..717928b054 100644 --- a/src/VORONOI/compute_voronoi_atom.h +++ b/src/VORONOI/compute_voronoi_atom.h @@ -61,7 +61,7 @@ class ComputeVoronoi : public Compute { bool onlyGroup, occupation; tagint *tags, oldmaxtag; - int *occvec, *sendocc, *lroot, *lnext, lmax, oldnatoms, oldnall; + int *occvec, *lroot, *lnext, lmax, oldnatoms, oldnall; int faces_flag, nfaces, nfacesmax; double **faces; }; diff --git a/src/atom.cpp b/src/atom.cpp index ac9ebb1634..6fbfe9adce 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -2124,7 +2124,7 @@ int Atom::shape_consistency(int itype, double &shapex, double &shapey, double &s } /* ---------------------------------------------------------------------- - add a new molecule template = set of molecules + add a new molecule template = set of molecules from the "molecule" command ------------------------------------------------------------------------- */ void Atom::add_molecule(int narg, char **arg) @@ -2143,7 +2143,8 @@ void Atom::add_molecule(int narg, char **arg) while (true) { molecules = (Molecule **) memory->srealloc(molecules,(nmolecule+1)*sizeof(Molecule *), "atom::molecules"); - molecules[nmolecule] = new Molecule(lmp,narg,arg,index); + molecules[nmolecule] = new Molecule(lmp); + molecules[nmolecule]->command(narg,arg,index); molecules[nmolecule]->nset = 0; molecules[nmolecule-ifile+1]->nset++; nmolecule++; @@ -2152,6 +2153,26 @@ void Atom::add_molecule(int narg, char **arg) } } +/* ---------------------------------------------------------------------- + add a new molecule template from a JSON object +------------------------------------------------------------------------- */ + +void Atom::add_molecule(const std::string &id, const json &moldata) +{ + if (id.empty()) error->all(FLERR, "Must provide molecule ID"); + + if (find_molecule(id.c_str()) >= 0) + error->all(FLERR, Error::NOLASTLINE, "Reuse of molecule template ID {}", id); + + molecules = (Molecule **) + memory->srealloc(molecules,(nmolecule+1)*sizeof(Molecule *), "atom::molecules"); + molecules[nmolecule] = new Molecule(lmp); + molecules[nmolecule]->from_json(id, moldata); + molecules[nmolecule]->nset = 1; + molecules[nmolecule]->last = 1; + nmolecule++; +} + /* ---------------------------------------------------------------------- find first molecule in set with template ID return -1 if does not exist diff --git a/src/atom.h b/src/atom.h index c98f06cbe8..667da04823 100644 --- a/src/atom.h +++ b/src/atom.h @@ -16,6 +16,8 @@ #include "pointers.h" +#include "json_fwd.h" + #include #include @@ -358,6 +360,7 @@ class Atom : protected Pointers { int shape_consistency(int, double &, double &, double &); void add_molecule(int, char **); + void add_molecule(const std::string &, const json &); int find_molecule(const char *); std::vector get_molecule_by_id(const std::string &); void add_molecule_atom(Molecule *, int, int, tagint); diff --git a/src/compute_angle_local.cpp b/src/compute_angle_local.cpp index 426f25953d..3129f05b56 100644 --- a/src/compute_angle_local.cpp +++ b/src/compute_angle_local.cpp @@ -108,7 +108,10 @@ ComputeAngleLocal::ComputeAngleLocal(LAMMPS *lmp, int narg, char **arg) : if (tstr) { tvar = input->variable->find(tstr); - if (tvar < 0) error->all(FLERR, "Variable name for compute angle/local does not exist"); + if (tvar < 0) { + input->variable->internal_create(tstr,0.0); + tvar = input->variable->find(tstr); + } if (!input->variable->internalstyle(tvar)) error->all(FLERR, "Variable for compute angle/local is invalid style"); } diff --git a/src/compute_bond_local.cpp b/src/compute_bond_local.cpp index 54c8c734c7..586508b1d3 100644 --- a/src/compute_bond_local.cpp +++ b/src/compute_bond_local.cpp @@ -154,7 +154,10 @@ ComputeBondLocal::ComputeBondLocal(LAMMPS *lmp, int narg, char **arg) : if (dstr) { dvar = input->variable->find(dstr); - if (dvar < 0) error->all(FLERR, "Variable name for compute bond/local does not exist"); + if (dvar < 0) { + input->variable->internal_create(dstr,0.0); + dvar = input->variable->find(dstr); + } if (!input->variable->internalstyle(dvar)) error->all(FLERR, "Variable for compute bond/local is invalid style"); } diff --git a/src/compute_dihedral_local.cpp b/src/compute_dihedral_local.cpp index 56718aa201..ab03ba74fc 100644 --- a/src/compute_dihedral_local.cpp +++ b/src/compute_dihedral_local.cpp @@ -102,7 +102,10 @@ ComputeDihedralLocal::ComputeDihedralLocal(LAMMPS *lmp, int narg, char **arg) : if (pstr) { pvar = input->variable->find(pstr); - if (pvar < 0) error->all(FLERR, "Variable name for compute dihedral/local does not exist"); + if (pvar < 0) { + input->variable->internal_create(pstr,0.0); + pvar = input->variable->find(pstr); + } if (!input->variable->internalstyle(pvar)) error->all(FLERR, "Variable for compute dihedral/local is invalid style"); } diff --git a/src/create_atoms.cpp b/src/create_atoms.cpp index 68ab376692..5eb7e47a84 100644 --- a/src/create_atoms.cpp +++ b/src/create_atoms.cpp @@ -390,28 +390,23 @@ void CreateAtoms::command(int narg, char **arg) if (!input->variable->equalstyle(vvar)) error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", vstr); - if (xstr) { - xvar = input->variable->find(xstr); - if (xvar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", xstr); - if (!input->variable->internalstyle(xvar)) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", xstr); - } - if (ystr) { - yvar = input->variable->find(ystr); - if (yvar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", ystr); - if (!input->variable->internalstyle(yvar)) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", ystr); - } - if (zstr) { - zvar = input->variable->find(zstr); - if (zvar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms does not exist", zstr); - if (!input->variable->internalstyle(zvar)) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for create_atoms is invalid style", zstr); +#define SETUP_XYZ_VAR(str,var) \ + if (str) { \ + var = input->variable->find(str); \ + if (var < 0) { \ + input->variable->internal_create(str, 0.0); \ + var = input->variable->find(str); \ + } \ + if (!input->variable->internalstyle(var)) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Variable {} for create_atoms is invalid style", str); \ } + + SETUP_XYZ_VAR(xstr, xvar); + SETUP_XYZ_VAR(ystr, yvar); + SETUP_XYZ_VAR(zstr, zvar); } +#undef SETUP_XYZ_VAR // require non-none lattice be defined for BOX or REGION styles diff --git a/src/fix_deposit.cpp b/src/fix_deposit.cpp index 46a8adad7b..f1c80f42a4 100644 --- a/src/fix_deposit.cpp +++ b/src/fix_deposit.cpp @@ -868,19 +868,28 @@ void FixDeposit::options(int narg, char **arg) if (xstr) { xvar = input->variable->find(xstr); - if (xvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", xstr); + if (xvar < 0) { + input->variable->internal_create(xstr,0.0); + xvar = input->variable->find(xstr); + } if (!input->variable->internalstyle(xvar)) error->all(FLERR, "Variable for fix deposit is invalid style"); } if (ystr) { yvar = input->variable->find(ystr); - if (yvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", ystr); + if (yvar < 0) { + input->variable->internal_create(ystr,0.0); + yvar = input->variable->find(ystr); + } if (!input->variable->internalstyle(yvar)) error->all(FLERR, "Variable for fix deposit is invalid style"); } if (zstr) { zvar = input->variable->find(zstr); - if (zvar < 0) error->all(FLERR, "Variable {} for fix deposit does not exist", zstr); + if (zvar < 0) { + input->variable->internal_create(zstr,0.0); + zvar = input->variable->find(zstr); + } if (!input->variable->internalstyle(zvar)) error->all(FLERR, "Variable for fix deposit is invalid style"); } diff --git a/src/fix_set.cpp b/src/fix_set.cpp new file mode 100644 index 0000000000..ac4400c1a9 --- /dev/null +++ b/src/fix_set.cpp @@ -0,0 +1,85 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "fix_set.h" + +#include "atom.h" +#include "error.h" +#include "set.h" +#include "update.h" + +using namespace LAMMPS_NS; +using namespace FixConst; + +enum{SETCOMMAND,FIXSET}; // also used in Set class + +/* ---------------------------------------------------------------------- */ + +FixSet::FixSet(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) +{ + if (narg < 8) error->all(FLERR, 1, "Illegal fix set command: need at least eight arguments"); + + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, "Fix {} Nevery must be > 0", style); + + reneighbor = utils::inumeric(FLERR, arg[4], false, lmp); + if (reneighbor < 0 || reneighbor > 1) + error->all(FLERR, "Fix {} rnflag must be 0/1", style); + + // create instance of Set class + + set = new Set(lmp); + + // pass remaining args to Set class + // only keywords which use per-atom variables are currently allowed + // NOTE: could also allow when set style = region, + // since atoms may move in/out of regions + + set->process_args(FIXSET,narg-5,&arg[5]); +} + +/* ---------------------------------------------------------------------- */ + +FixSet::~FixSet() +{ + delete set; +} + +/* ---------------------------------------------------------------------- */ + +int FixSet::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + return mask; +} + +/* ---------------------------------------------------------------------- + use the Set instance to update per-atom properties + NOTE: could return count of updated atoms from Set for use as fix output +---------------------------------------------------------------------- */ + +void FixSet::end_of_step() +{ + // select which atoms to act on + + set->selection(atom->nlocal); + + // loop over list of actions to reset atom attributes + + set->invoke_actions(); + + // trigger reneighboring on next timestep if requested + + if (reneighbor) next_reneighbor = update->ntimestep + 1; +} diff --git a/src/fix_set.h b/src/fix_set.h new file mode 100644 index 0000000000..7328ebd144 --- /dev/null +++ b/src/fix_set.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS +// clang-format off +FixStyle(set,FixSet); +// clang-format on +#else + +#ifndef LMP_FIX_SET_H +#define LMP_FIX_SET_H + +#include "fix.h" + +namespace LAMMPS_NS { + +class FixSet : public Fix { + public: + FixSet(class LAMMPS *, int, char **); + ~FixSet() override; + int setmask() override; + void end_of_step() override; + + private: + int reneighbor; + + class Set *set; +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/fmt/core.h b/src/fmt/core.h index 6a53b8c52c..d715fe18ac 100644 --- a/src/fmt/core.h +++ b/src/fmt/core.h @@ -219,6 +219,9 @@ # define FMT_UNICODE !FMT_MSC_VERSION #endif +// LAMMPS customization: avoid problems with consteval altogether +#define FMT_CONSTEVAL + #ifndef FMT_CONSTEVAL # if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ (!defined(__apple_build_version__) || \ diff --git a/src/info.h b/src/info.h index 8ef38b3c9d..fc7267dbda 100644 --- a/src/info.h +++ b/src/info.h @@ -21,6 +21,7 @@ CommandStyle(info,Info); #define LMP_INFO_H #include "command.h" +#include "json_fwd.h" #include diff --git a/src/input.cpp b/src/input.cpp index bc9a55052c..ae2c9f0874 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -585,7 +585,6 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) int i,n,paren_count,nchars; char immediate[256]; char *var,*value,*beyond; - int quoteflag = 0; char *ptrmatch; char *ptr = str; @@ -599,7 +598,7 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) // variable substitution - if (*ptr == '$' && !quoteflag) { + if (*ptr == '$') { // value = ptr to expanded variable // variable name between curly braces, e.g. ${a} diff --git a/src/json.h b/src/json.h index 2f364b445e..46aae1c966 100644 --- a/src/json.h +++ b/src/json.h @@ -20,6 +20,6 @@ #include "nlohmann/json.hpp" namespace LAMMPS_NS { -using json = ::nlohmann_lmp::json; +using json = ::nlohmann_lmp::basic_json<>; } #endif diff --git a/src/json_fwd.h b/src/json_fwd.h new file mode 100644 index 0000000000..b63d41bd4d --- /dev/null +++ b/src/json_fwd.h @@ -0,0 +1,25 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef LMP_JSON_FWD_H +#define LMP_JSON_FWD_H + +// Forward declarations for header-only JSON class +// For use in headers + +#include "nlohmann/json_fwd.hpp" + +namespace LAMMPS_NS { +using json = ::nlohmann_lmp::basic_json<>; +} +#endif diff --git a/src/kspace.cpp b/src/kspace.cpp index 8ab7a11eda..f0f6dd90cd 100644 --- a/src/kspace.cpp +++ b/src/kspace.cpp @@ -33,15 +33,15 @@ static constexpr double SMALL = 0.00001; /* ---------------------------------------------------------------------- */ -KSpace::KSpace(LAMMPS *lmp) : Pointers(lmp) +KSpace::KSpace(LAMMPS *lmp) : + Pointers(lmp), eatom(nullptr), vatom(nullptr), gcons(nullptr), dgcons(nullptr) { order_allocated = 0; energy = 0.0; virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; triclinic_support = 1; - ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = - dipoleflag = spinflag = 0; + ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = spinflag = 0; compute_flag = 1; group_group_enable = 0; stagger_flag = 0; @@ -83,14 +83,17 @@ KSpace::KSpace(LAMMPS *lmp) : Pointers(lmp) accuracy_real_6 = -1.0; accuracy_kspace_6 = -1.0; + qqrd2e = force->qqrd2e; + g_ewald = g_ewald_6 = 0.0; + scale = 1.0; + neighrequest_flag = 1; mixflag = 0; splittol = 1.0e-6; + scale = 1.0; maxeatom = maxvatom = 0; - eatom = nullptr; - vatom = nullptr; centroidstressflag = CENTROID_NOTAVAIL; execution_space = Host; diff --git a/src/kspace_zero.cpp b/src/kspace_zero.cpp new file mode 100644 index 0000000000..0443898d8d --- /dev/null +++ b/src/kspace_zero.cpp @@ -0,0 +1,112 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Axel Kohlmeyer (Temple U) +------------------------------------------------------------------------- */ + +#include "kspace_zero.h" + +#include "atom.h" +#include "comm.h" +#include "domain.h" +#include "error.h" +#include "force.h" +#include "pair.h" + +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpaceZero::KSpaceZero(LAMMPS *lmp) : KSpace(lmp) +{ + ewaldflag = 1; + pppmflag = 1; + msmflag = 1; + dispersionflag = 1; + tip4pflag = 1; + dipoleflag = 1; + spinflag = 1; +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::settings(int narg, char **arg) +{ + if (narg != 1) error->all(FLERR, "Illegal kspace_style {} command", force->kspace_style); + + accuracy_relative = fabs(utils::numeric(FLERR, arg[0], false, lmp)); + if (accuracy_relative > 1.0) + error->all(FLERR, "Invalid relative accuracy {:g} for kspace_style {}", accuracy_relative, + force->kspace_style); + if ((narg != 0) && (narg != 1)) error->all(FLERR, "Illegal kspace_style command"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::init() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace initialization ...\n"); + + // error checks + + if (force->pair == nullptr) error->all(FLERR, "KSpace solver requires a pair style"); + if (!atom->q_flag) error->all(FLERR, "KSpace style zero requires atom attribute q"); + + // compute two charge force + + two_charge(); + + int itmp; + auto p_cutoff = (double *) force->pair->extract("cut_coul", itmp); + if (p_cutoff == nullptr) error->all(FLERR, "KSpace style is incompatible with Pair style"); + double cutoff = *p_cutoff; + + qsum_qsq(); + + accuracy = accuracy_relative * two_charge_force; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + if (!gewaldflag) { + if (accuracy <= 0.0) error->all(FLERR, "KSpace accuracy must be > 0"); + if (q2 == 0.0) error->all(FLERR, "Must use 'kspace_modify gewald' for uncharged system"); + g_ewald = accuracy * sqrt(atom->natoms * cutoff * domain->xprd * domain->yprd * domain->zprd) / + (2.0 * q2); + if (g_ewald >= 1.0) + g_ewald = (1.35 - 0.15 * log(accuracy)) / cutoff; + else + g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + if (comm->me == 0) utils::logmesg(lmp, " G vector (1/distance) = {:.8g}\n", g_ewald); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::setup() +{ + if (comm->me == 0) utils::logmesg(lmp, "Dummy KSpace setup\n"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpaceZero::compute(int eflag, int vflag) +{ + ev_init(eflag, vflag); +} diff --git a/src/kspace_zero.h b/src/kspace_zero.h new file mode 100644 index 0000000000..d55b437fc2 --- /dev/null +++ b/src/kspace_zero.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/ Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifdef KSPACE_CLASS +// clang-format off +KSpaceStyle(zero,KSpaceZero); +// clang-format on +#else + +#ifndef LMP_KSPACE_ZERO_H +#define LMP_KSPACE_ZERO_H + +#include "kspace.h" + +namespace LAMMPS_NS { + +class KSpaceZero : public KSpace { + public: + KSpaceZero(class LAMMPS *); + + void init() override; + void setup() override; + void settings(int, char **) override; + + void compute(int, int) override; +}; +} // namespace LAMMPS_NS +#endif +#endif diff --git a/src/lammps.cpp b/src/lammps.cpp index c891cea14d..98839e33fb 100644 --- a/src/lammps.cpp +++ b/src/lammps.cpp @@ -883,8 +883,9 @@ void LAMMPS::create() python = new Python(this); - // auto-load plugins + // restore and auto-load plugins #if defined(LMP_PLUGIN) + plugin_restore(this, true); plugin_auto_load(this); #endif } @@ -991,11 +992,6 @@ void LAMMPS::init() void LAMMPS::destroy() { - // must wipe out all plugins first, if configured -#if defined(LMP_PLUGIN) - plugin_clear(this); -#endif - delete update; update = nullptr; diff --git a/src/library.cpp b/src/library.cpp index a8acbade52..aacea70269 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -357,7 +357,8 @@ completed and then MPI is cleanly shut down. After calling this function no more MPI calls may be made. *See also* - :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize` + :cpp:func:`lammps_kokkos_finalize`, :cpp:func:`lammps_python_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_mpi_finalize() @@ -389,7 +390,8 @@ closed (to release associated resources). After calling this function no Kokkos functionality may be used. *See also* - :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_python_finalize` + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_python_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_kokkos_finalize() @@ -399,6 +401,35 @@ void lammps_kokkos_finalize() /* ---------------------------------------------------------------------- */ +/** Unload all plugins and release the corresponding DSO handles + * +\verbatim embed:rst + +.. versionadded:: TBD + +This function clears the list of all loaded plugins and closes the +corresponding DSO handles and releases the imported executable code. + +However, this is **not** done when a LAMMPS instance is deleted because +plugins and their shared objects are global properties. + +This function can be called to explicitly clear out all loaded plugins +in case it is safe to do so. + +*See also* + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, + :cpp:func:`lammps_python_finalize` +\endverbatim */ + +void lammps_plugin_finalize() +{ +#if defined(LMP_PLUGIN) + plugin_finalize(); +#endif +} + +/* ---------------------------------------------------------------------- */ + /** Clear the embedded Python environment * \verbatim embed:rst @@ -425,7 +456,8 @@ This function can be called to explicitly clear the Python environment in case it is safe to do so. *See also* - :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize` + :cpp:func:`lammps_mpi_finalize`, :cpp:func:`lammps_kokkos_finalize`, + :cpp:func:`lammps_plugin_finalize` \endverbatim */ void lammps_python_finalize() @@ -433,7 +465,6 @@ void lammps_python_finalize() Python::finalize(); } - /* ---------------------------------------------------------------------- */ /** Call a LAMMPS Error class function diff --git a/src/library.h b/src/library.h index a4ca396628..08b619eb78 100644 --- a/src/library.h +++ b/src/library.h @@ -140,6 +140,7 @@ void lammps_close(void *handle); void lammps_mpi_init(); void lammps_mpi_finalize(); void lammps_kokkos_finalize(); +void lammps_plugin_finalize(); void lammps_python_finalize(); void lammps_error(void *handle, int error_type, const char *error_text); diff --git a/src/lmppython.cpp b/src/lmppython.cpp index b3c52111fe..f7a213302a 100644 --- a/src/lmppython.cpp +++ b/src/lmppython.cpp @@ -14,9 +14,8 @@ #include "lmppython.h" #if defined(LMP_PYTHON) #include "python_impl.h" -#else -#include "error.h" #endif +#include "error.h" using namespace LAMMPS_NS; @@ -43,7 +42,7 @@ void Python::init() #if defined(LMP_PYTHON) if (!impl) impl = new PythonImpl(lmp); #else - error->all(FLERR, "Python support missing! Compile with PYTHON package installed!"); + error->all(FLERR, Error::NOLASTLINE, "Python support missing! Compile with PYTHON package installed!"); #endif } @@ -67,10 +66,10 @@ void Python::command(int narg, char **arg) /* ------------------------------------------------------------------ */ -void Python::invoke_function(int ifunc, char *result) +void Python::invoke_function(int ifunc, char *result, double *dvalue) { init(); - impl->invoke_function(ifunc, result); + impl->invoke_function(ifunc, result, dvalue); } /* ------------------------------------------------------------------ */ @@ -83,10 +82,19 @@ int Python::find(const char *name) /* ------------------------------------------------------------------ */ -int Python::variable_match(const char *name, const char *varname, int numeric) +int Python::function_match(const char *name, const char *varname, int numeric, Error *errptr) { init(); - return impl->variable_match(name, varname, numeric); + return impl->function_match(name, varname, numeric, errptr); +} + +/* ------------------------------------------------------------------ */ + +int Python::wrapper_match(const char *name, const char *varname, int narg, int *argvars, + Error *errptr) +{ + init(); + return impl->wrapper_match(name, varname, narg, argvars, errptr); } /* ------------------------------------------------------------------ */ diff --git a/src/lmppython.h b/src/lmppython.h index d961e1b046..f7db8a4c06 100644 --- a/src/lmppython.h +++ b/src/lmppython.h @@ -22,9 +22,10 @@ class PythonInterface { public: virtual ~PythonInterface() noexcept(false) {} virtual void command(int, char **) = 0; - virtual void invoke_function(int, char *) = 0; + virtual void invoke_function(int, char *, double *) = 0; virtual int find(const char *) = 0; - virtual int variable_match(const char *, const char *, int) = 0; + virtual int function_match(const char *, const char *, int, Error *) = 0; + virtual int wrapper_match(const char *, const char *, int, int *, Error *) = 0; virtual char *long_string(int ifunc) = 0; virtual int execute_string(char *) = 0; virtual int execute_file(char *) = 0; @@ -37,9 +38,10 @@ class Python : protected Pointers { ~Python() override; void command(int, char **); - void invoke_function(int, char *); + void invoke_function(int, char *, double *); int find(const char *); - int variable_match(const char *, const char *, int); + int function_match(const char *, const char *, int, Error *); + int wrapper_match(const char *, const char *, int, int *, Error *); char *long_string(int ifunc); int execute_string(char *); int execute_file(char *); diff --git a/src/main.cpp b/src/main.cpp index 951d828b51..65f5a9658d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ static void finalize() { lammps_kokkos_finalize(); lammps_python_finalize(); + lammps_plugin_finalize(); } /* ---------------------------------------------------------------------- diff --git a/src/molecule.cpp b/src/molecule.cpp index 318e2a1e0f..9720cd6fc2 100644 --- a/src/molecule.cpp +++ b/src/molecule.cpp @@ -20,16 +20,20 @@ #include "domain.h" #include "error.h" #include "force.h" +#include "json.h" #include "label_map.h" #include "math_eigen.h" #include "math_extra.h" +#include "math_special.h" #include "memory.h" #include "tokenizer.h" +#include "update.h" #include #include using namespace LAMMPS_NS; +using MathSpecial::powint; static constexpr int MAXLINE = 1024; static constexpr double EPSILON = 1.0e-7; @@ -39,7 +43,7 @@ static constexpr double SINERTIA = 0.4; // moment of inertia prefactor for sp /* ---------------------------------------------------------------------- */ -Molecule::Molecule(LAMMPS *lmp, int narg, char **arg, int &index) : +Molecule::Molecule(LAMMPS *lmp) : Pointers(lmp), id(nullptr), x(nullptr), type(nullptr), molecule(nullptr), q(nullptr), radius(nullptr), rmass(nullptr), mu(nullptr), num_bond(nullptr), bond_type(nullptr), bond_atom(nullptr), num_angle(nullptr), angle_type(nullptr), angle_atom1(nullptr), @@ -52,22 +56,32 @@ Molecule::Molecule(LAMMPS *lmp, int narg, char **arg, int &index) : dx(nullptr), dxcom(nullptr), dxbody(nullptr), quat_external(nullptr), fp(nullptr), count(nullptr) { - me = comm->me; - - if (index >= narg) utils::missing_cmd_args(FLERR, "molecule", error); - - id = utils::strdup(arg[0]); - if (!utils::is_id(id)) - error->all(FLERR, Error::ARGZERO, - "Molecule template ID {} must have only alphanumeric or underscore" - " characters", - id); - // parse args until reach unknown arg (next file) toffset = 0; boffset = aoffset = doffset = ioffset = 0; sizescale = 1.0; + json_format = 0; + + // initialize all fields to empty + + Molecule::initialize(); +} + +// ------------------------------------------------------------------------------ +// process arguments from "molecule" command +// ------------------------------------------------------------------------------ + +void Molecule::command(int narg, char **arg, int &index) +{ + if (index >= narg) utils::missing_cmd_args(FLERR, "molecule", error); + + id = utils::strdup(arg[0]); + if (!utils::is_id(id)) + error->all(FLERR, Error::ARGZERO, + "Molecule template ID {} must have only alphanumeric or underscore characters", id); + + // parse args until reach unknown arg (next file) fileiarg = index; @@ -120,7 +134,7 @@ Molecule::Molecule(LAMMPS *lmp, int narg, char **arg, int &index) : } else break; } - + // clang-format on index = iarg; if (atom->labelmapflag && @@ -138,43 +152,1159 @@ Molecule::Molecule(LAMMPS *lmp, int narg, char **arg, int &index) : else last = 0; - // initialize all fields to empty + json moldata; + std::vector jsondata; + int jsondata_size = 0; - Molecule::initialize(); - - // scan file for sizes of all fields and allocate storage for them - - if (me == 0) { + if (comm->me == 0) { fp = fopen(arg[fileiarg], "r"); if (fp == nullptr) error->one(FLERR, fileiarg, "Cannot open molecule file {}: {}", arg[fileiarg], utils::getsyserror()); + try { + // try to parse as a JSON file + // if successful serialize to bytearray for communication + moldata = json::parse(fp); + jsondata = json::to_ubjson(moldata); + jsondata_size = jsondata.size(); + fclose(fp); + } catch (std::exception &) { + // rewind so we can try reading the file as a native molecule file + rewind(fp); + } } - Molecule::read(0); - if (me == 0) fclose(fp); + MPI_Bcast(&jsondata_size, 1, MPI_INT, 0, world); + + if (jsondata_size > 0) { + // broadcast binary JSON data to all processes and deserialize again + if (comm->me != 0) jsondata.resize(jsondata_size); + MPI_Bcast(jsondata.data(), jsondata_size, MPI_CHAR, 0, world); + // convert back to json class on all processors + moldata.clear(); + moldata = json::from_ubjson(jsondata); + jsondata.clear(); // free binary data + + // process JSON data + Molecule::from_json(id, moldata); + + } else { // process native molecule file + + // scan file for sizes of all fields and allocate storage for them + + Molecule::read(0); + Molecule::allocate(); + + // read file again to populate all fields + + if (comm->me == 0) rewind(fp); + Molecule::read(1); + if (comm->me == 0) fclose(fp); + } + Molecule::stats(); +} + +// ------------------------------------------------------------------------------ +// convert json data structure to molecule data structure +// ------------------------------------------------------------------------------ + +void Molecule::from_json(const std::string &molid, const json &moldata) +{ + json_format = 1; + if (!utils::is_id(molid)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template ID {} must have only alphanumeric or underscore characters", + molid); + delete[] id; + id = utils::strdup(molid); + + // check required fields if JSON data is compatible + + std::string val; + if (moldata.contains("application")) { + if (moldata["application"] != "LAMMPS") + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data is for incompatible application: {}", id, + std::string(moldata["application"])); + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data does not contain required 'application' field", id); + } + if (moldata.contains("format")) { + if (moldata["format"] != "molecule") + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data is not for a molecule: {}", id, + std::string(moldata["format"])); + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data does not contain required 'format' field", id); + } + if (moldata.contains("revision")) { + int rev = moldata["revision"]; + if ((rev < 1) || (rev > 1)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON molecule data with unsupported revision {}", id, rev); + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data does not contain required 'revision' field", id); + } + + // length of types data list determines the number of atoms in the template and is thus required + if (!moldata.contains("types")) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON data does not contain required 'types' field", id); + + // optional fields + + // check for compatible units + + if (moldata.contains("units")) { + bool incompatible_units = true; + auto jsonunits = std::string(moldata["units"]); + auto lammpsunits = std::string(update->unit_style); + if ((jsonunits == "real") || (jsonunits == "metal")) { + if ((lammpsunits == "real") || (lammpsunits == "metal")) incompatible_units = false; + } else if (jsonunits == lammpsunits) { + incompatible_units = false; + } + + if (incompatible_units) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: Incompatible units in JSON molecule data: current = {}, JSON = {}", + id, lammpsunits, jsonunits); + } + if (moldata.contains("title")) title = moldata["title"]; + + // determine and check sizes + + int dummyvar; + +#define JSON_INIT_FIELD(field, sizevar, flagvar, required, sizecheck) \ + if (moldata.contains(#field)) { \ + sizevar = 0; \ + flagvar = 0; \ + if (!moldata[#field].contains("format")) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Molecule template {}: JSON molecule data does not contain required 'format' " \ + "field for '{}'", \ + id, #field); \ + if (moldata[#field].contains("data")) { \ + flagvar = 1; \ + sizevar = moldata[#field]["data"].size(); \ + } else { \ + error->all(FLERR, Error::NOLASTLINE, \ + "Molecule template {}: JSON molecule data does not contain required 'data' " \ + "field for '{}'", \ + id, #field); \ + } \ + if (sizevar < 1) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Molecule template {}: No {} entries in JSON data for molecule", id, #field); \ + } else { \ + if (required) \ + error->all( \ + FLERR, Error::NOLASTLINE, \ + "Molecule template {}: JSON data for molecule does not contain required '{}' field", id, \ + #field); \ + } \ + if (sizecheck && (sizecheck != sizevar)) \ + error->all(FLERR, Error::NOLASTLINE, \ + "Molecule template {}: Found {} instead of {} data entries for '{}'", id, sizevar, \ + sizecheck, #field); + + JSON_INIT_FIELD(types, natoms, typeflag, true, 0); + JSON_INIT_FIELD(coords, dummyvar, xflag, false, natoms); + JSON_INIT_FIELD(molecules, dummyvar, moleculeflag, false, natoms); + JSON_INIT_FIELD(fragments, nfragments, fragmentflag, false, 0); + JSON_INIT_FIELD(charges, dummyvar, qflag, false, natoms); + JSON_INIT_FIELD(diameters, dummyvar, radiusflag, false, natoms); + JSON_INIT_FIELD(dipoles, dummyvar, muflag, false, natoms); + JSON_INIT_FIELD(masses, dummyvar, rmassflag, false, natoms); + JSON_INIT_FIELD(bonds, nbonds, bondflag, false, 0); + JSON_INIT_FIELD(angles, nangles, angleflag, false, 0); + JSON_INIT_FIELD(dihedrals, ndihedrals, dihedralflag, false, 0); + JSON_INIT_FIELD(impropers, nimpropers, improperflag, false, 0); + +#undef JSON_INIT_FIELD + + if ((nbonds > 0) || (nangles > 0) || (ndihedrals > 0) || (nimpropers > 0)) tag_require = 1; + + // extract global properties, if present + + if (moldata.contains("masstotal")) { + massflag = 1; + masstotal = double(moldata["masstotal"]) * sizescale * sizescale * sizescale; + } + + if (moldata.contains("com") && (moldata["com"].size() == 3)) { + comflag = 1; + com[0] = double(moldata["com"][0]) * sizescale; + com[1] = double(moldata["com"][1]) * sizescale; + com[2] = double(moldata["com"][2]) * sizescale; + } + + if (moldata.contains("inertia") && (moldata["inertia"].size() == 6)) { + inertiaflag = 1; + const double scale5 = powint(sizescale, 5); + itensor[0] = double(moldata["inertia"][0]) * scale5; + itensor[1] = double(moldata["inertia"][1]) * scale5; + itensor[2] = double(moldata["inertia"][2]) * scale5; + itensor[3] = double(moldata["inertia"][3]) * scale5; + itensor[4] = double(moldata["inertia"][4]) * scale5; + itensor[5] = double(moldata["inertia"][5]) * scale5; + } + + if (moldata.contains("body") && (moldata["body"].size() == 2)) { + bodyflag = 1; + const double scale5 = powint(sizescale, 5); + avec_body = dynamic_cast(atom->style_match("body")); + if (!avec_body) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: JSON molecule data requires atom style body", id); + nibody = moldata["body"][0]; + ndbody = moldata["body"][1]; + } + + // checks. No checks for < 0 needed since size() is at least 0 + + if ((domain->dimension == 2) && (com[2] != 0.0)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Molecule data z center-of-mass must be 0.0 for 2d systems", + id); + + // allocate required storage + Molecule::allocate(); - // read file again to populate all fields + // count = vector for tallying bonds,angles,etc per atom - if (me == 0) fp = fopen(arg[fileiarg], "r"); - Molecule::read(1); - if (me == 0) fclose(fp); + memory->create(count, natoms, "molecule:count"); - // stats + // process data sections + std::vector secfmt; - if (title.empty()) title = "(no title)"; - if (me == 0) - utils::logmesg(lmp, - "Read molecule template {}:\n{}\n" - " {} molecules\n" - " {} fragments\n" - " {} atoms with max type {}\n" - " {} bonds with max type {}\n" - " {} angles with max type {}\n" - " {} dihedrals with max type {}\n" - " {} impropers with max type {}\n", - id, title, nmolecules, nfragments, natoms, ntypes, nbonds, nbondtypes, nangles, - nangletypes, ndihedrals, ndihedraltypes, nimpropers, nimpropertypes); + // coords + if (xflag) { + for (int i = 0; i < 4; ++i) secfmt.push_back(moldata["coords"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "x") && (secfmt[2] == "y") && + (secfmt[3] == "z")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["coords"]["data"]) { + if (c.size() < 4) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"coords\" section of molecule JSON data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"coords\" section of molecule JSON " + "data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id {} in coords section of molecule JSON data", + id, iatom + 1); + count[iatom]++; + x[iatom][0] = c[1]; + x[iatom][1] = c[2]; + x[iatom][2] = c[3]; + + x[iatom][0] *= sizescale; + x[iatom][1] *= sizescale; + x[iatom][2] *= sizescale; + } + + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"coords\" JSON section", id, i + 1); + } + } + if (domain->dimension == 2) { + for (int i = 0; i < natoms; i++) { + if (x[i][2] != 0.0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Z coord for atom {} must be 0.0 for 2d-simulation", + id, i + 1); + } + } + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"coords\" format [\"atom-id\",\"x\",\"y\",\"z\"] " + "but found [\"{}\",\"{}\",\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1], secfmt[2], secfmt[3]); + } + } + + // types (is a required section and we tested for it above) + + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["types"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "type")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["types"]["data"]) { + if (c.size() < 2) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"types\" section of molecule JSON data: {}", id, + to_string(c)); + if (!c[0].is_number_integer()) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"types\" section of molecule JSON data: {}", + id, to_string(c[0])); + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id {} in types section of molecule JSON data", id, + iatom + 1); + if (c[1].is_number_integer()) { // numeric type + type[iatom] = int(c[1]) + toffset; + } else { + const auto &typestr = std::string(c[1]); + if (!atom->labelmapflag) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom type in \"types\" JSON section", id, + typestr); + type[iatom] = atom->lmap->find(typestr, Atom::ATOM); + if (type[iatom] == -1) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Unknown atom type {} in \"types\" JSON section", id, + typestr); + } + count[iatom]++; + } + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"types\" JSON section", id, i + 1); + } + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"types\" format [\"atom-id\",\"type\"] but found " + "[\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + + // molecules + + if (moleculeflag) { + + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["molecules"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "molecule-id")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["molecules"]["data"]) { + if (c.size() < 2) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"molecules\" section of molecule JSON " + "data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"molecules\" section of molecule " + "JSON data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom-id {} in \"molecules\" section of molecule JSON data", + iatom + 1); + if (!c[1].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid molecule-id in \"molecules\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + molecule[iatom] = int(c[1]); + if (molecule[iatom] < 0) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid molecule-id in \"molecules\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + count[iatom]++; + } + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"molecules\" JSON section", id, + i + 1); + } + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"molecules\" format " + "[\"atom-id\",\"molecule-id\"] but found " + "[\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + } + + // fragments + + if (fragmentflag) { + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["fragments"]["format"][i]); + if ((secfmt[0] == "fragment-id") && (secfmt[1] == "atom-id-list")) { + + for (int i = 0; i < nfragments; ++i) { + fragmentnames[i] = to_string(moldata["fragments"]["data"][i][0]); + for (const auto &c : moldata["fragments"]["data"][i][1]) { + if (!c.is_number_integer()) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"fragments\" section JSON data: {}", id, + to_string(c)); + + const int iatom = int(c) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom id {} in \"fragments\" section of molecule JSON data", + iatom + 1); + fragmentmask[i][iatom] = 1; + } + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"fragments\" format " + "[\"fragment-id\",\"atom-id-list\"] but found [\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + } + + // charges + + if (qflag) { + + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["charges"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "charge")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["charges"]["data"]) { + if (c.size() < 2) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"charges\" section of molecule JSON " + "data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"charges\" section of molecule " + "JSON data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom-id {} in \"charges\" section of molecule JSON data", iatom + 1); + if (!c[1].is_number()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid charge in \"charges\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + q[iatom] = double(c[1]); + count[iatom]++; + } + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"charges\" JSON section", id, + i + 1); + } + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"charges\" format [\"atom-id\",\"charge\"] but " + "found [\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + } + + // diameters + + if (radiusflag) { + maxradius = 0.0; + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["diameters"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "diameter")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["diameters"]["data"]) { + if (c.size() < 2) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"diameters\" section of molecule JSON " + "data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"diameters\" section of molecule " + "JSON data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom-id {} in \"diameters\" section of molecule JSON data", + iatom + 1); + if (!c[1].is_number()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid diameter in \"diameters\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + radius[iatom] = double(c[1]) * sizescale * 0.5; + maxradius = MAX(maxradius, radius[iatom]); + if (!c[1].is_number()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid diameter in \"diameters\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + count[iatom]++; + } + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"diameters\" JSON section", id, + i + 1); + } + if (radius[i] < 0.0) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom diameter {} for atom {} in molecule JSON data", + id, radius[i] * 2.0 / sizescale, i + 1); + } + } else { + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"diameters\" format [\"atom-id\",\"diameter\"] but " + "found [\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + } + + // dipoles + + if (muflag) { + + secfmt.clear(); + for (int i = 0; i < 4; ++i) secfmt.push_back(moldata["dipoles"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "mux") && (secfmt[2] == "muy") && + (secfmt[3] == "muz")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["dipoles"]["data"]) { + if (c.size() < 4) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"dipoles\" section of molecule JSON data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"dipoles\" section of molecule JSON " + "data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id {} in dipoles section of molecule JSON data", + id, iatom + 1); + count[iatom]++; + mu[iatom][0] = c[1]; + mu[iatom][1] = c[2]; + mu[iatom][2] = c[3]; + mu[iatom][0] *= sizescale; + mu[iatom][1] *= sizescale; + mu[iatom][2] *= sizescale; + } + + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"dipoles\" JSON section", id, + i + 1); + } + } + if (domain->dimension == 2) { + for (int i = 0; i < natoms; i++) + if (mu[i][2] != 0.0) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: dipole moment z-component in JSON data for atom {} " + "must be 0.0 for 2d-simulation", + id, i + 1); + } + } else { + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"dipoles\" format [\"atom-id\",\"mux\",\"muy\",\"muz\"] " + "but found [\"{}\",\"{}\",\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1], secfmt[2], secfmt[3]); + } + } + + // masses + + if (rmassflag) { + secfmt.clear(); + for (int i = 0; i < 2; ++i) secfmt.push_back(moldata["masses"]["format"][i]); + if ((secfmt[0] == "atom-id") && (secfmt[1] == "mass")) { + + memset(count, 0, natoms * sizeof(int)); + for (const auto &c : moldata["masses"]["data"]) { + if (c.size() < 2) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: missing data in \"masses\" section of molecule JSON " + "data: {}", + id, to_string(c)); + if (!c[0].is_number_integer()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom-id in \"masses\" section of molecule " + "JSON data: {}", + id, to_string(c[0])); + + const int iatom = int(c[0]) - 1; + if ((iatom < 0) || (iatom >= natoms)) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom-id {} in \"masses\" section of molecule JSON data", iatom + 1); + if (!c[1].is_number()) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid mass in \"masses\" section of " + "molecule JSON data: {}", + id, to_string(c[1])); + rmass[iatom] = double(c[1]) * sizescale * sizescale * sizescale; + count[iatom]++; + } + // checks + for (int i = 0; i < natoms; i++) { + if (count[i] == 0) { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: atom {} missing in \"masses\" JSON section", id, i + 1); + } + if (rmass[i] <= 0.0) + error->all(FLERR, Error::NOLASTLINE, + "Invalid atom mass {} for atom {} in molecule JSON data", + rmass[i] / sizescale / sizescale / sizescale, i + 1); + } + } else { + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Expected \"masses\" format [\"atom-id\",\"mass\"] but " + "found [\"{}\",\"{}\"]", + id, secfmt[0], secfmt[1]); + } + } + + // bonds + + if (bondflag) { + int itype; + tagint m, atom1, atom2; + const int newton_bond = force->newton_bond; + + // must loop over data twice: first time to count, second time to apply + + for (int flag = 0; flag < 2; ++flag) { + secfmt.clear(); + for (int i = 0; i < 3; ++i) secfmt.push_back(moldata["bonds"]["format"][i]); + if ((secfmt[0] == "bond-type") && (secfmt[1] == "atom1") && (secfmt[2] == "atom2")) { + + if (flag == 0) { + memset(count, 0, natoms * sizeof(int)); + } else { + // must reallocate here in second iteration because bond_per_atom was not set for allocate() . + memory->destroy(bond_type); + memory->destroy(bond_atom); + memory->create(bond_type, natoms, bond_per_atom, "molecule:bond_type"); + memory->create(bond_atom, natoms, bond_per_atom, "molecule:bond_atom"); + + memset(num_bond, 0, natoms * sizeof(int)); + } + + for (int i = 0; i < nbonds; ++i) { + const auto &item = moldata["bonds"]["data"][i]; + if (item.size() < 3) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid format of JSON data for bond {}: {}", id, + i + 1, to_string(item)); + + if (item[0].is_number_integer()) { // numeric type + itype = int(item[0]) + boffset; + } else { + const auto &typestr = std::string(item[0]); + if (!atom->labelmapflag) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid bond type in \"bonds\" JSON section", id, + typestr); + itype = atom->lmap->find(typestr, Atom::BOND); + if (itype == -1) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Unknown bond type {} in \"bonds\" JSON section", id, + typestr); + } + + atom1 = tagint(item[1]); + atom2 = tagint(item[2]); + if ((atom1 <= 0) || (atom1 > natoms) || (atom2 <= 0) || (atom2 > natoms) || + (atom1 == atom2)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom ID in bond {}: {}", id, i + 1, + to_string(item)); + if ((itype <= 0) || (domain->box_exist && (itype > atom->nbondtypes))) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid bond type in bond {}: {}", id, i + 1, + to_string(item)); + if (flag == 0) { + count[atom1 - 1]++; + if (newton_bond == 0) count[atom2 - 1]++; + } else { + m = atom1 - 1; + nbondtypes = MAX(nbondtypes, itype); + bond_type[m][num_bond[m]] = itype; + bond_atom[m][num_bond[m]] = atom2; + num_bond[m]++; + if (newton_bond == 0) { + m = atom2 - 1; + bond_type[m][num_bond[m]] = itype; + bond_atom[m][num_bond[m]] = atom1; + num_bond[m]++; + } + } + } + + // bond_per_atom = max of count vector + + if (flag == 0) { + bond_per_atom = 0; + for (int i = 0; i < natoms; i++) bond_per_atom = MAX(bond_per_atom, count[i]); + } + } + } + } + + // angles + + if (angleflag) { + int itype; + tagint m, atom1, atom2, atom3; + const int newton_bond = force->newton_bond; + + // must loop over data twice: first time to count, second time to apply + + for (int flag = 0; flag < 2; ++flag) { + secfmt.clear(); + for (int i = 0; i < 4; ++i) secfmt.push_back(moldata["angles"]["format"][i]); + if ((secfmt[0] == "angle-type") && (secfmt[1] == "atom1") && (secfmt[2] == "atom2") && + (secfmt[3] == "atom3")) { + + if (flag == 0) { + memset(count, 0, natoms * sizeof(int)); + } else { + // must reallocate here in second iteration because angle_per_atom was not set for allocate() . + memory->destroy(angle_type); + memory->destroy(angle_atom1); + memory->destroy(angle_atom2); + memory->destroy(angle_atom3); + memory->create(angle_type, natoms, angle_per_atom, "molecule:angle_type"); + memory->create(angle_atom1, natoms, angle_per_atom, "molecule:angle_atom1"); + memory->create(angle_atom2, natoms, angle_per_atom, "molecule:angle_atom2"); + memory->create(angle_atom3, natoms, angle_per_atom, "molecule:angle_atom3"); + + memset(num_angle, 0, natoms * sizeof(int)); + } + + for (int i = 0; i < nangles; ++i) { + const auto &item = moldata["angles"]["data"][i]; + if (item.size() < 4) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid format of JSON data for angle {}: {}", id, + i + 1, to_string(item)); + + if (item[0].is_number_integer()) { // numeric type + itype = int(item[0]) + aoffset; + } else { + const auto &typestr = std::string(item[0]); + if (!atom->labelmapflag) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid angle type in \"angles\" JSON section", id, + typestr); + itype = atom->lmap->find(typestr, Atom::ANGLE); + if (itype == -1) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: Unknown angle type {} in \"angles\" JSON section", + id, typestr); + } + + atom1 = tagint(item[1]); + atom2 = tagint(item[2]); + atom3 = tagint(item[3]); + + if ((atom1 <= 0) || (atom1 > natoms) || (atom2 <= 0) || (atom2 > natoms) || + (atom3 <= 0) || (atom3 > natoms) || (atom1 == atom2) || (atom1 == atom3) || + (atom2 == atom3)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom ID in angle {}: {}", id, i + 1, + to_string(item)); + if ((itype <= 0) || (domain->box_exist && (itype > atom->nangletypes))) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid angle type in angle {}: {}", id, i + 1, + to_string(item)); + if (flag == 0) { + count[atom1 - 1]++; + if (newton_bond == 0) { + count[atom2 - 1]++; + count[atom3 - 1]++; + } + } else { + m = atom2 - 1; + nangletypes = MAX(nangletypes, itype); + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + if (newton_bond == 0) { + m = atom1 - 1; + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + m = atom3 - 1; + angle_type[m][num_angle[m]] = itype; + angle_atom1[m][num_angle[m]] = atom1; + angle_atom2[m][num_angle[m]] = atom2; + angle_atom3[m][num_angle[m]] = atom3; + num_angle[m]++; + } + } + } + + // angle_per_atom = max of count vector + + if (flag == 0) { + angle_per_atom = 0; + for (int i = 0; i < natoms; i++) angle_per_atom = MAX(angle_per_atom, count[i]); + } + } + } + } + + // dihedrals + + if (dihedralflag) { + int itype; + tagint m, atom1, atom2, atom3, atom4; + const int newton_bond = force->newton_bond; + + // must loop over data twice: first time to count, second time to apply + + for (int flag = 0; flag < 2; ++flag) { + secfmt.clear(); + for (int i = 0; i < 5; ++i) secfmt.push_back(moldata["dihedrals"]["format"][i]); + if ((secfmt[0] == "dihedral-type") && (secfmt[1] == "atom1") && (secfmt[2] == "atom2") && + (secfmt[3] == "atom3") && (secfmt[4] == "atom4")) { + + if (flag == 0) { + memset(count, 0, natoms * sizeof(int)); + } else { + // must reallocate here in second iteration because dihedral_per_atom was not set for allocate() . + memory->destroy(dihedral_type); + memory->destroy(dihedral_atom1); + memory->destroy(dihedral_atom2); + memory->destroy(dihedral_atom3); + memory->destroy(dihedral_atom4); + memory->create(dihedral_type, natoms, dihedral_per_atom, "molecule:dihedral_type"); + memory->create(dihedral_atom1, natoms, dihedral_per_atom, "molecule:dihedral_atom1"); + memory->create(dihedral_atom2, natoms, dihedral_per_atom, "molecule:dihedral_atom2"); + memory->create(dihedral_atom3, natoms, dihedral_per_atom, "molecule:dihedral_atom3"); + memory->create(dihedral_atom4, natoms, dihedral_per_atom, "molecule:dihedral_atom4"); + + memset(num_dihedral, 0, natoms * sizeof(int)); + } + + for (int i = 0; i < ndihedrals; ++i) { + const auto &item = moldata["dihedrals"]["data"][i]; + if (item.size() < 4) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid format of JSON data for dihedral {}: {}", id, + i + 1, to_string(item)); + + if (item[0].is_number_integer()) { // numeric type + itype = int(item[0]) + aoffset; + } else { + const auto &typestr = std::string(item[0]); + if (!atom->labelmapflag) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid dihedral type in \"dihedrals\" JSON section", id, + typestr); + itype = atom->lmap->find(typestr, Atom::DIHEDRAL); + if (itype == -1) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: Unknown dihedral type {} in \"dihedrals\" JSON section", + id, typestr); + } + + atom1 = tagint(item[1]); + atom2 = tagint(item[2]); + atom3 = tagint(item[3]); + atom4 = tagint(item[4]); + + if ((atom1 <= 0) || (atom1 > natoms) || (atom2 <= 0) || (atom2 > natoms) || + (atom3 <= 0) || (atom3 > natoms) || (atom4 <= 0) || (atom4 > natoms) || + (atom1 == atom2) || (atom1 == atom3) || (atom1 == atom4) || (atom2 == atom3) || + (atom2 == atom4) || (atom3 == atom4)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom ID in dihedral {}: {}", id, i + 1, + to_string(item)); + if ((itype <= 0) || (domain->box_exist && (itype > atom->ndihedraltypes))) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid dihedral type in dihedral {}: {}", id, i + 1, + to_string(item)); + if (flag == 0) { + count[atom1 - 1]++; + if (newton_bond == 0) { + count[atom2 - 1]++; + count[atom3 - 1]++; + count[atom4 - 1]++; + } + } else { + m = atom2 - 1; + ndihedraltypes = MAX(ndihedraltypes, itype); + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + if (newton_bond == 0) { + m = atom1 - 1; + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + m = atom3 - 1; + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + m = atom4 - 1; + dihedral_type[m][num_dihedral[m]] = itype; + dihedral_atom1[m][num_dihedral[m]] = atom1; + dihedral_atom2[m][num_dihedral[m]] = atom2; + dihedral_atom3[m][num_dihedral[m]] = atom3; + dihedral_atom4[m][num_dihedral[m]] = atom4; + num_dihedral[m]++; + } + } + } + + // dihedral_per_atom = max of count vector + + if (flag == 0) { + dihedral_per_atom = 0; + for (int i = 0; i < natoms; i++) dihedral_per_atom = MAX(dihedral_per_atom, count[i]); + } + } + } + } + + // impropers + + if (improperflag) { + int itype; + tagint m, atom1, atom2, atom3, atom4; + const int newton_bond = force->newton_bond; + + // must loop over data twice: first time to count, second time to apply + + for (int flag = 0; flag < 2; ++flag) { + secfmt.clear(); + for (int i = 0; i < 5; ++i) secfmt.push_back(moldata["impropers"]["format"][i]); + if ((secfmt[0] == "improper-type") && (secfmt[1] == "atom1") && (secfmt[2] == "atom2") && + (secfmt[3] == "atom3") && (secfmt[4] == "atom4")) { + + if (flag == 0) { + memset(count, 0, natoms * sizeof(int)); + } else { + // must reallocate here in second iteration because improper_per_atom was not set for allocate() . + memory->destroy(improper_type); + memory->destroy(improper_atom1); + memory->destroy(improper_atom2); + memory->destroy(improper_atom3); + memory->destroy(improper_atom4); + memory->create(improper_type, natoms, improper_per_atom, "molecule:improper_type"); + memory->create(improper_atom1, natoms, improper_per_atom, "molecule:improper_atom1"); + memory->create(improper_atom2, natoms, improper_per_atom, "molecule:improper_atom2"); + memory->create(improper_atom3, natoms, improper_per_atom, "molecule:improper_atom3"); + memory->create(improper_atom4, natoms, improper_per_atom, "molecule:improper_atom4"); + + memset(num_improper, 0, natoms * sizeof(int)); + } + + for (int i = 0; i < nimpropers; ++i) { + const auto &item = moldata["impropers"]["data"][i]; + if (item.size() < 4) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid format of JSON data for improper {}: {}", id, + i + 1, to_string(item)); + + if (item[0].is_number_integer()) { // numeric type + itype = int(item[0]) + aoffset; + } else { + const auto &typestr = std::string(item[0]); + if (!atom->labelmapflag) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid improper type in \"impropers\" JSON section", id, + typestr); + itype = atom->lmap->find(typestr, Atom::IMPROPER); + if (itype == -1) + error->all( + FLERR, Error::NOLASTLINE, + "Molecule template {}: Unknown improper type {} in \"impropers\" JSON section", + id, typestr); + } + + atom1 = tagint(item[1]); + atom2 = tagint(item[2]); + atom3 = tagint(item[3]); + atom4 = tagint(item[4]); + + if ((atom1 <= 0) || (atom1 > natoms) || (atom2 <= 0) || (atom2 > natoms) || + (atom3 <= 0) || (atom3 > natoms) || (atom4 <= 0) || (atom4 > natoms) || + (atom1 == atom2) || (atom1 == atom3) || (atom1 == atom4) || (atom2 == atom3) || + (atom2 == atom4) || (atom3 == atom4)) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid atom ID in improper {}: {}", id, i + 1, + to_string(item)); + if ((itype <= 0) || (domain->box_exist && (itype > atom->nimpropertypes))) + error->all(FLERR, Error::NOLASTLINE, + "Molecule template {}: invalid improper type in improper {}: {}", id, i + 1, + to_string(item)); + if (flag == 0) { + count[atom1 - 1]++; + if (newton_bond == 0) { + count[atom2 - 1]++; + count[atom3 - 1]++; + count[atom4 - 1]++; + } + } else { + m = atom2 - 1; + nimpropertypes = MAX(nimpropertypes, itype); + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + if (newton_bond == 0) { + m = atom1 - 1; + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + m = atom3 - 1; + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + m = atom4 - 1; + improper_type[m][num_improper[m]] = itype; + improper_atom1[m][num_improper[m]] = atom1; + improper_atom2[m][num_improper[m]] = atom2; + improper_atom3[m][num_improper[m]] = atom3; + improper_atom4[m][num_improper[m]] = atom4; + num_improper[m]++; + } + } + } + + // improper_per_atom = max of count vector + + if (flag == 0) { + improper_per_atom = 0; + for (int i = 0; i < natoms; i++) improper_per_atom = MAX(improper_per_atom, count[i]); + } + } + } + } + + // special_bond_counts + // special_bonds + + // shake_flags + // shake_atoms + // shake_bond_types + + // body_integers + // body_doubles + + // error checks + + if ((nspecialflag && !specialflag) || (!nspecialflag && specialflag)) + error->all(FLERR, fileiarg, "Molecule file needs both Special Bond sections"); + if (specialflag && !bondflag) + error->all(FLERR, fileiarg, "Molecule file has special flags but no bonds"); + if ((shakeflagflag || shakeatomflag || shaketypeflag) && !shakeflag) + error->all(FLERR, fileiarg, "Molecule file shake info is incomplete"); + if (bodyflag && nibody && ibodyflag == 0) + error->all(FLERR, fileiarg, "Molecule file has no Body Integers section"); + if (bodyflag && ndbody && dbodyflag == 0) + error->all(FLERR, fileiarg, "Molecule file has no Body Doubles section"); + if (nfragments > 0 && !fragmentflag) + error->all(FLERR, fileiarg, "Molecule file has no Fragments section"); + // auto-generate special bonds if needed and not in file + + if (bondflag && specialflag == 0) { + if (domain->box_exist == 0) + error->all(FLERR, fileiarg, + "Cannot auto-generate special bonds before simulation box is defined"); + + special_generate(); + specialflag = 1; + nspecialflag = 1; + } + + // body particle must have natom = 1 + // set radius by having body class compute its own radius + + if (bodyflag) { + radiusflag = 1; + if (natoms != 1) error->all(FLERR, fileiarg, "Molecule natoms must be 1 for body particle"); + if (sizescale != 1.0) + error->all(FLERR, fileiarg, "Molecule sizescale must be 1.0 for body particle"); + radius[0] = avec_body->radius_body(nibody, ndbody, ibodyparams, dbodyparams); + maxradius = radius[0]; + } + + // clean up + + memory->destroy(count); } /* ---------------------------------------------------------------------- */ @@ -416,6 +1546,8 @@ void Molecule::compute_inertia() for (int i = 0; i < natoms; i++) MathExtra::transpose_matvec(ex, ey, ez, dxcom[i], dxbody[i]); } +// clang-format off + /* ---------------------------------------------------------------------- read molecule info from file flag = 0, just scan for sizes of fields @@ -429,8 +1561,18 @@ void Molecule::read(int flag) // skip 1st line of file - if (me == 0) { + if (comm->me == 0) { eof = fgets(line, MAXLINE, fp); + + // check for units keyword in first line and print warning on mismatch + + auto units = Tokenizer(utils::strfind(line, "units = \\w+")).as_vector(); + if (units.size() > 2) { + if (units[2] != update->unit_style) + error->warning(FLERR, "Inconsistent units in data file: current = {}, data file = {}", + update->unit_style, units[2]); + } + if (eof == nullptr) error->one(FLERR, fileiarg, "Unexpected end of molecule file"); } @@ -439,6 +1581,7 @@ void Molecule::read(int flag) // read header lines // skip blank lines or lines that start with "#" // stop when read an unrecognized line + bool has_atoms = false; while (true) { @@ -458,6 +1601,7 @@ void Molecule::read(int flag) if (values.matches("^\\s*\\d+\\s+atoms")) { natoms = values.next_int(); nwant = 2; + has_atoms = true; } else if (values.matches("^\\s*\\d+\\s+bonds")) { nbonds = values.next_int(); nwant = 2; @@ -487,7 +1631,7 @@ void Molecule::read(int flag) com[0] *= sizescale; com[1] *= sizescale; com[2] *= sizescale; - if (domain->dimension == 2 && com[2] != 0.0) + if ((domain->dimension == 2) && (com[2] != 0.0)) error->all(FLERR, fileiarg, "Molecule file z center-of-mass must be 0.0 for 2d systems"); } else if (values.matches("^\\s*\\f+\\s+\\f+\\s+\\f+\\s+\\f+\\s+\\f+\\s+\\f+\\s+inertia")) { inertiaflag = 1; @@ -498,14 +1642,14 @@ void Molecule::read(int flag) itensor[4] = values.next_double(); itensor[5] = values.next_double(); nwant = 7; - const double scale5 = sizescale * sizescale * sizescale * sizescale * sizescale; + const double scale5 = powint(sizescale, 5); itensor[0] *= scale5; itensor[1] *= scale5; itensor[2] *= scale5; itensor[3] *= scale5; itensor[4] *= scale5; itensor[5] *= scale5; - } else if (values.matches("^\\s*\\d+\\s+\\f+\\s+body")) { + } else if (values.matches("^\\s*\\d+\\s+\\d+\\s+body")) { bodyflag = 1; avec_body = dynamic_cast(atom->style_match("body")); if (!avec_body) error->all(FLERR, fileiarg, "Molecule file requires atom style body"); @@ -529,6 +1673,8 @@ void Molecule::read(int flag) // error checks + if (!has_atoms) + error->all(FLERR, fileiarg, "Required \"atoms\" header keyword not found in molecule file"); if (natoms < 1) error->all(FLERR, fileiarg, "No atoms or invalid atom count in molecule file"); if (nbonds < 0) error->all(FLERR, fileiarg, "Invalid bond count in molecule file"); if (nangles < 0) error->all(FLERR, fileiarg, "Invalid angle count in molecule file"); @@ -745,7 +1891,7 @@ void Molecule::coords(char *line) int iatom = values.next_int() - 1; if (iatom < 0 || iatom >= natoms) - error->all(FLERR, fileiarg, "Invalid atom index in Coords section of molecule file"); + error->all(FLERR, fileiarg, "Invalid atom index {} in Coords section of molecule file", iatom); count[iatom]++; x[iatom][0] = values.next_double(); x[iatom][1] = values.next_double(); @@ -970,7 +2116,7 @@ void Molecule::diameters(char *line) if (count[i] == 0) error->all(FLERR, fileiarg, "Atom {} missing in Diameters section of molecule file", i + 1); if (radius[i] < 0.0) - error->all(FLERR, fileiarg, "Invalid atom diameter {} for atom {} in molecule file", radius[i], i + 1); + error->all(FLERR, fileiarg, "Invalid atom diameter {} for atom {} in molecule file", radius[i] * 2.0 / sizescale, i + 1); } } @@ -994,9 +2140,9 @@ void Molecule::dipoles(char *line) error->all(FLERR, fileiarg, "Invalid atom index in Dipoles section of molecule file"); count[iatom]++; - mu[iatom][0] = values.next_double(); - mu[iatom][1] = values.next_double(); - mu[iatom][2] = values.next_double(); + mu[iatom][0] = values.next_double() * sizescale; + mu[iatom][1] = values.next_double() * sizescale; + mu[iatom][2] = values.next_double() * sizescale; } } catch (TokenizerException &e) { error->all(FLERR, fileiarg, "Invalid line in Dipoles section of molecule file: {}\n{}", e.what(), line); @@ -1006,6 +2152,12 @@ void Molecule::dipoles(char *line) if (count[i] == 0) error->all(FLERR, fileiarg, "Atom {} missing in Dipoles section of molecule file", i + 1); } + if (domain->dimension == 2) { + for (int i = 0; i < natoms; i++) + if (mu[i][2] != 0.0) + error->all(FLERR, fileiarg, "Dipole moment z-component in JSON data for atom {} " + "must be 0.0 for 2d-simulation", id, i + 1); + } } /* ---------------------------------------------------------------------- @@ -1038,7 +2190,8 @@ void Molecule::masses(char *line) if (count[i] == 0) error->all(FLERR, fileiarg, "Atom {} missing in Masses section of molecule file", i + 1); if (rmass[i] <= 0.0) - error->all(FLERR, fileiarg, "Invalid atom mass {} for atom {} in molecule file", radius[i], i + 1); + error->all(FLERR, fileiarg, "Invalid atom mass {} for atom {} in molecule file", rmass[i] / sizescale / sizescale + / sizescale, i + 1); } } @@ -1892,8 +3045,9 @@ void Molecule::check_attributes() if (radiusflag && !atom->radius_flag) mismatch = 1; if (rmassflag && !atom->rmass_flag) mismatch = 1; - if (mismatch && me == 0) - error->warning(FLERR, "Molecule attributes do not match system attributes" + utils::errorurl(26)); + if (mismatch && (comm->me == 0)) + error->warning(FLERR, "Molecule attributes do not match system attributes" + + utils::errorurl(26)); // for all atom styles, check nbondtype,etc @@ -1904,7 +3058,8 @@ void Molecule::check_attributes() if (atom->nimpropertypes < nimpropertypes) mismatch = 1; if (mismatch) - error->all(FLERR, fileiarg, "Molecule topology type exceeds system topology type" + utils::errorurl(25)); + error->all(FLERR, fileiarg, "Molecule topology type exceeds system topology type" + + utils::errorurl(25)); // for molecular atom styles, check bond_per_atom,etc + maxspecial // do not check for atom style template, since nothing stored per atom @@ -1923,7 +3078,7 @@ void Molecule::check_attributes() // warn if molecule topology defined but no special settings if (bondflag && !specialflag) - if (me == 0) error->warning(FLERR, "Molecule has bond topology but no special bond settings"); + if (comm->me == 0) error->warning(FLERR, "Molecule has bond topology but no special bond settings"); } /* ---------------------------------------------------------------------- @@ -2133,7 +3288,7 @@ void Molecule::deallocate() void Molecule::readline(char *line) { int n; - if (me == 0) { + if (comm->me == 0) { if (fgets(line, MAXLINE, fp) == nullptr) n = 0; else @@ -2159,7 +3314,7 @@ std::string Molecule::parse_keyword(int flag, char *line) // eof is set to 1 if any read hits end-of-file int eof = 0; - if (me == 0) { + if (comm->me == 0) { if (fgets(line, MAXLINE, fp) == nullptr) eof = 1; while (eof == 0 && strspn(line, " \t\n\r") == strlen(line)) { if (fgets(line, MAXLINE, fp) == nullptr) eof = 1; @@ -2170,7 +3325,7 @@ std::string Molecule::parse_keyword(int flag, char *line) // if eof, set keyword empty and return MPI_Bcast(&eof, 1, MPI_INT, 0, world); - if (eof) { return {""}; } + if (eof) return {""}; // bcast keyword line to all procs @@ -2191,13 +3346,31 @@ void Molecule::skip_lines(int n, char *line, const std::string §ion) for (int i = 0; i < n; i++) { readline(line); if (utils::strmatch(utils::trim(utils::trim_comment(line)), "^[A-Za-z ]+$")) - error->one(FLERR, - "Unexpected line in molecule file while " - "skipping {} section:\n{}", + error->one(FLERR, Error::NOLASTLINE, + "Unexpected line in molecule file while skipping {} section:\n{}", section, line); } } +/* ------------------------------------------------------------------------------ */ + +void Molecule::stats() +{ + if (title.empty()) title = "(no title)"; + if (comm->me == 0) + utils::logmesg(lmp, + "Read molecule template {}:\n{}\n" + " {} molecules\n" + " {} fragments\n" + " {} atoms with max type {}\n" + " {} bonds with max type {}\n" + " {} angles with max type {}\n" + " {} dihedrals with max type {}\n" + " {} impropers with max type {}\n", + id, title, nmolecules, nfragments, natoms, ntypes, nbonds, nbondtypes, nangles, + nangletypes, ndihedrals, ndihedraltypes, nimpropers, nimpropertypes); +} + /* ---------------------------------------------------------------------- proc 0 prints molecule params ------------------------------------------------------------------------- */ diff --git a/src/molecule.h b/src/molecule.h index 210969c1fd..f384c6e003 100644 --- a/src/molecule.h +++ b/src/molecule.h @@ -16,6 +16,8 @@ #include "pointers.h" +#include "json_fwd.h" + namespace LAMMPS_NS { class Molecule : protected Pointers { @@ -122,8 +124,12 @@ class Molecule : protected Pointers { double *quat_external; // orientation imposed by external class // e.g. FixPour or CreateAtoms - Molecule(class LAMMPS *, int, char **, int &); + Molecule(class LAMMPS *); ~Molecule() override; + + void command(int, char **, int &); + void from_json(const std::string &id, const json &); + void compute_center(); void compute_mass(); void compute_com(); @@ -132,11 +138,10 @@ class Molecule : protected Pointers { void check_attributes(); private: - int me; FILE *fp; int *count; int toffset, boffset, aoffset, doffset, ioffset; - int autospecial; + int json_format; double sizescale; void read(int); @@ -168,6 +173,7 @@ class Molecule : protected Pointers { std::string parse_keyword(int, char *); void skip_lines(int, char *, const std::string &); + void stats(); // void print(); }; diff --git a/src/nlohmann/json.hpp b/src/nlohmann/json.hpp index 75014cae41..58bcefba46 100644 --- a/src/nlohmann/json.hpp +++ b/src/nlohmann/json.hpp @@ -134,6 +134,7 @@ #define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) +// LAMMPS CUSTOMIZATION: to avoid namespace clashes we use "nlohmann_lmp" #ifndef NLOHMANN_JSON_NAMESPACE #define NLOHMANN_JSON_NAMESPACE \ nlohmann_lmp::NLOHMANN_JSON_NAMESPACE_CONCAT( \ @@ -160,10 +161,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann // SPDX-License-Identifier: MIT @@ -173,6 +174,9 @@ #include // forward_list #include // inserter, front_inserter, end #include // map +#ifdef JSON_HAS_CPP_17 + #include // optional +#endif #include // string #include // tuple, make_tuple #include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible @@ -183,10 +187,10 @@ // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 +// | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann // SPDX-License-Identifier: MIT @@ -3489,8 +3493,9 @@ NLOHMANN_JSON_NAMESPACE_END // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann // SPDX-License-Identifier: MIT -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ +// LAMMPS customization. We changed the include guard with the namespace name +#ifndef LMP_NLOHMANN_JSON_FWD_HPP_ + #define LMP_NLOHMANN_JSON_FWD_HPP_ #include // int64_t, uint64_t #include // map @@ -3556,7 +3561,7 @@ NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#endif // LMP_NLOHMANN_JSON_FWD_HPP_ NLOHMANN_JSON_NAMESPACE_BEGIN diff --git a/src/nlohmann/json_fwd.hpp b/src/nlohmann/json_fwd.hpp new file mode 100644 index 0000000000..8ebbf1d4e5 --- /dev/null +++ b/src/nlohmann/json_fwd.hpp @@ -0,0 +1,188 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef LMP_NLOHMANN_JSON_FWD_HPP_ +#define LMP_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_DIAGNOSTIC_POSITIONS + #define JSON_DIAGNOSTIC_POSITIONS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_DIAGNOSTIC_POSITIONS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +// LAMMPS CUSTOMIZATION: to avoid namespace clashes we use "nlohmann_lmp". +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann_lmp::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann_lmp \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann_lmp +#endif + + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +NLOHMANN_JSON_NAMESPACE_BEGIN + +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +NLOHMANN_JSON_NAMESPACE_END + +#endif // LMP_NLOHMANN_JSON_FWD_HPP_ diff --git a/src/read_data.cpp b/src/read_data.cpp index cf7b224db2..1afcd76dac 100644 --- a/src/read_data.cpp +++ b/src/read_data.cpp @@ -174,13 +174,13 @@ void ReadData::command(int narg, char **arg) addflag = VALUE; bigint offset = utils::bnumeric(FLERR, arg[iarg + 1], false, lmp); if (offset > MAXTAGINT) - error->all(FLERR, "Read data add IDoffset {} is too big", offset); + error->all(FLERR, iarg, "Read data add IDoffset {} is too big", offset); id_offset = offset; if (atom->molecule_flag) { offset = utils::bnumeric(FLERR, arg[iarg + 2], false, lmp); if (offset > MAXTAGINT) - error->all(FLERR, "Read data add MOLoffset {} is too big", offset); + error->all(FLERR, iarg, "Read data add MOLoffset {} is too big", offset); mol_offset = offset; iarg++; } @@ -195,7 +195,7 @@ void ReadData::command(int narg, char **arg) doffset = utils::inumeric(FLERR, arg[iarg + 4], false, lmp); ioffset = utils::inumeric(FLERR, arg[iarg + 5], false, lmp); if (toffset < 0 || boffset < 0 || aoffset < 0 || doffset < 0 || ioffset < 0) - error->all(FLERR, "Illegal read_data offset value(s)"); + error->all(FLERR, iarg, "Illegal read_data offset value(s)"); iarg += 6; } else if (strcmp(arg[iarg], "shift") == 0) { if (iarg + 4 > narg) utils::missing_cmd_args(FLERR, "read_data shift", error); @@ -204,7 +204,8 @@ void ReadData::command(int narg, char **arg) shift[1] = utils::numeric(FLERR, arg[iarg + 2], false, lmp); shift[2] = utils::numeric(FLERR, arg[iarg + 3], false, lmp); if (domain->dimension == 2 && shift[2] != 0.0) - error->all(FLERR, "Non-zero read_data shift z value for 2d simulation not allowed"); + error->all(FLERR, iarg + 2, + "Non-zero read_data shift z value for 2d simulation not allowed"); iarg += 4; } else if (strcmp(arg[iarg], "nocoeff") == 0) { coeffflag = 0; @@ -213,86 +214,92 @@ void ReadData::command(int narg, char **arg) if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/atom/types", error); extra_atom_types = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (extra_atom_types < 0) - error->all(FLERR, "Illegal read_data extra/atom/types value {}", extra_atom_types); + error->all(FLERR, iarg + 1, "Illegal read_data extra/atom/types value {}", + extra_atom_types); iarg += 2; } else if (strcmp(arg[iarg], "extra/bond/types") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/bond/types", error); if (!atom->avec->bonds_allow) - error->all(FLERR, "No bonds allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No bonds allowed with atom style {}", atom->get_style()); extra_bond_types = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (extra_bond_types < 0) - error->all(FLERR, "Illegal read_data extra/bond/types value {}", extra_bond_types); + error->all(FLERR, iarg + 1, "Illegal read_data extra/bond/types value {}", + extra_bond_types); iarg += 2; } else if (strcmp(arg[iarg], "extra/angle/types") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/angle/types", error); if (!atom->avec->angles_allow) - error->all(FLERR, "No angles allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No angles allowed with atom style {}", atom->get_style()); extra_angle_types = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (extra_angle_types < 0) - error->all(FLERR, "Illegal read_data extra/angle/types value {}", extra_angle_types); + error->all(FLERR, iarg + 1, "Illegal read_data extra/angle/types value {}", + extra_angle_types); iarg += 2; } else if (strcmp(arg[iarg], "extra/dihedral/types") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/dihedral/types", error); if (!atom->avec->dihedrals_allow) - error->all(FLERR, "No dihedrals allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No dihedrals allowed with atom style {}", atom->get_style()); extra_dihedral_types = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (extra_dihedral_types < 0) - error->all(FLERR, "Illegal read_data extra/dihedral/types value {}", extra_dihedral_types); + error->all(FLERR, iarg + 1, "Illegal read_data extra/dihedral/types value {}", + extra_dihedral_types); iarg += 2; } else if (strcmp(arg[iarg], "extra/improper/types") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/improper/types", error); if (!atom->avec->impropers_allow) - error->all(FLERR, "No impropers allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No impropers allowed with atom style {}", atom->get_style()); extra_improper_types = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (extra_improper_types < 0) - error->all(FLERR, "Illegal read_data extra/improper/types value {}", extra_improper_types); + error->all(FLERR, iarg + 1, "Illegal read_data extra/improper/types value {}", + extra_improper_types); iarg += 2; } else if (strcmp(arg[iarg], "extra/bond/per/atom") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/bond/per/atom", error); if (atom->molecular == Atom::ATOMIC) - error->all(FLERR, "No bonds allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No bonds allowed with atom style {}", atom->get_style()); atom->extra_bond_per_atom = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (atom->extra_bond_per_atom < 0) - error->all(FLERR, "Illegal read_data extra/bond/per/atom value {}", + error->all(FLERR, iarg + 1, "Illegal read_data extra/bond/per/atom value {}", atom->extra_bond_per_atom); iarg += 2; } else if (strcmp(arg[iarg], "extra/angle/per/atom") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/angle/per/atom", error); if (atom->molecular == Atom::ATOMIC) - error->all(FLERR, "No angles allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No angles allowed with atom style {}", atom->get_style()); atom->extra_angle_per_atom = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (atom->extra_angle_per_atom < 0) - error->all(FLERR, "Illegal read_data extra/angle/per/atom value {}", + error->all(FLERR, iarg + 1, "Illegal read_data extra/angle/per/atom value {}", atom->extra_angle_per_atom); iarg += 2; } else if (strcmp(arg[iarg], "extra/dihedral/per/atom") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/dihedral/per/atom", error); if (atom->molecular == Atom::ATOMIC) - error->all(FLERR, "No dihedrals allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No dihedrals allowed with atom style {}", atom->get_style()); atom->extra_dihedral_per_atom = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (atom->extra_dihedral_per_atom < 0) - error->all(FLERR, "Illegal read_data extra/dihedral/per/atom value {}", + error->all(FLERR, iarg + 1, "Illegal read_data extra/dihedral/per/atom value {}", atom->extra_dihedral_per_atom); iarg += 2; } else if (strcmp(arg[iarg], "extra/improper/per/atom") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/improper/per/atom", error); if (atom->molecular == Atom::ATOMIC) - error->all(FLERR, "No impropers allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No impropers allowed with atom style {}", atom->get_style()); atom->extra_improper_per_atom = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (atom->extra_improper_per_atom < 0) - error->all(FLERR, "Illegal read_data extra/improper/per/atom value {}", + error->all(FLERR, iarg + 1, "Illegal read_data extra/improper/per/atom value {}", atom->extra_improper_per_atom); iarg += 2; } else if (strcmp(arg[iarg], "extra/special/per/atom") == 0) { if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "read_data extra/special/per/atom", error); if (atom->molecular == Atom::ATOMIC) - error->all(FLERR, "No bonded interactions allowed with atom style {}", atom->get_style()); + error->all(FLERR, iarg + 1, "No bonded interactions allowed with atom style {}", + atom->get_style()); force->special_extra = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); if (force->special_extra < 0) - error->all(FLERR, "Illegal read_data extra/special/per/atom value {}", + error->all(FLERR, iarg + 1, "Illegal read_data extra/special/per/atom value {}", force->special_extra); iarg += 2; } else if (strcmp(arg[iarg], "group") == 0) { @@ -309,12 +316,12 @@ void ReadData::command(int narg, char **arg) fix_section = (char **) memory->srealloc(fix_section, (nfix + 1) * sizeof(char *), "read_data:fix_section"); if (is_data_section(arg[iarg + 3])) - error->all(FLERR, + error->all(FLERR, iarg + 3, "Custom data section name {} for fix {} collides with existing data section", arg[iarg + 3], arg[iarg + 1]); fix_index[nfix] = modify->get_fix_by_id(arg[iarg + 1]); if (!fix_index[nfix]) - error->all(FLERR, "Fix ID {} for read_data does not exist", arg[iarg + 1]); + error->all(FLERR, iarg + 1, "Fix ID {} for read_data does not exist", arg[iarg + 1]); if (strcmp(arg[iarg + 2], "NULL") == 0) fix_header[nfix] = nullptr; else @@ -327,7 +334,7 @@ void ReadData::command(int narg, char **arg) iarg += 4; } else - error->all(FLERR, "Unknown read_data keyword {}", arg[iarg]); + error->all(FLERR, iarg, "Unknown read_data keyword {}", arg[iarg]); } // error checks @@ -339,11 +346,12 @@ void ReadData::command(int narg, char **arg) "Reading a data file with shrinkwrap boundaries is not " "compatible with a MSM KSpace style"); if (domain->box_exist && !addflag) - error->all(FLERR, "Cannot use read_data without add keyword after simulation box is defined" - + utils::errorurl(34)); + error->all(FLERR, + "Cannot use read_data without add keyword after simulation box is defined" + + utils::errorurl(34)); if (!domain->box_exist && addflag) - error->all(FLERR, "Cannot use read_data add before simulation box is defined" - + utils::errorurl(33)); + error->all(FLERR, + "Cannot use read_data add before simulation box is defined" + utils::errorurl(33)); if (offsetflag) { if (addflag == NONE) { error->all(FLERR, "Cannot use read_data offset without add keyword"); @@ -366,7 +374,7 @@ void ReadData::command(int narg, char **arg) // check if data file is available and readable if (!platform::file_is_readable(arg[0])) - error->all(FLERR, "Cannot open file {}: {}", arg[0], utils::getsyserror()); + error->all(FLERR, Error::ARGZERO, "Cannot open file {}: {}", arg[0], utils::getsyserror()); // reset so we can warn about reset image flags exactly once per data file @@ -528,7 +536,7 @@ void ReadData::command(int narg, char **arg) if (tilt_flag) triclinic = 1; } else { if (xloxhi_flag || yloyhi_flag || zlozhi_flag || tilt_flag) - error->all(FLERR, + error->all(FLERR, Error::ARGZERO, "Read_data header cannot specify simulation box lo/hi/tilt and ABC vectors"); triclinic = triclinic_general = 1; } @@ -538,10 +546,11 @@ void ReadData::command(int narg, char **arg) if (domain->dimension == 2) { if (triclinic_general == 0) { if (boxlo[2] >= 0.0 || boxhi[2] <= 0.0) - error->all(FLERR, "Read_data zlo/zhi for 2d simulation must straddle 0.0"); + error->all(FLERR, Error::ARGZERO, + "Read_data zlo/zhi for 2d simulation must straddle 0.0"); } else if (triclinic_general == 1) { if (cvec[0] != 0.0 || cvec[1] != 0.0 || cvec[2] != 1.0 || abc_origin[2] != -0.5) - error->all(FLERR, + error->all(FLERR, Error::ARGZERO, "Read_data cvec and/or abc_origin is invalid for " "2d simulation with general triclinic box"); } @@ -619,7 +628,8 @@ void ReadData::command(int narg, char **arg) if (triclinic_general) { if (!domain->triclinic_general) - error->all(FLERR, "Read_data subsequent file cannot switch to general triclinic"); + error->all(FLERR, Error::ARGZERO, + "Read_data subsequent file cannot switch to general triclinic"); int errflag = 0; if (avec[0] != domain->avec[0] || avec[1] != domain->avec[1] || avec[2] != domain->avec[2]) errflag = 1; @@ -631,7 +641,8 @@ void ReadData::command(int narg, char **arg) abc_origin[2] != domain->boxlo[2]) errflag = 1; if (errflag) - error->all(FLERR, "Read_data subsequent file ABC vectors must be same as first file"); + error->all(FLERR, Error::ARGZERO, + "Read_data subsequent file ABC vectors must be same as first file"); if (shift[0] != 0.0 || shift[1] != 0.0 || shift[2] != 0.0) error->all(FLERR, "Read_data subsequent file with ABC vectors cannot define shift"); @@ -640,13 +651,15 @@ void ReadData::command(int narg, char **arg) } else if (triclinic) { if (!domain->triclinic || domain->triclinic_general) - error->all(FLERR, "Read_data subsequent file cannot switch to restricted triclinic"); + error->all(FLERR, Error::ARGZERO, + "Read_data subsequent file cannot switch to restricted triclinic"); if (xy != domain->xy || xz != domain->xz || yz != domain->yz) error->all(FLERR, "Read_data subsequent file tilt factors must be same as first file"); } else { if (domain->triclinic) - error->all(FLERR, "Read_data subsequent file cannot switch to orthogonal"); + error->all(FLERR, Error::ARGZERO, + "Read_data subsequent file cannot switch to orthogonal"); } double oldboxlo[3] = {domain->boxlo[0], domain->boxlo[1], domain->boxlo[2]}; @@ -715,7 +728,7 @@ void ReadData::command(int narg, char **arg) skip_lines(natoms); } else if (strcmp(keyword, "Velocities") == 0) { - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Velocities"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Velocities"); if (firstpass) velocities(); else @@ -723,32 +736,35 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Bonds") == 0) { topoflag = bondflag = 1; - if (nbonds == 0) error->all(FLERR, "Invalid data file section: Bonds"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Bonds"); + if (nbonds == 0) error->all(FLERR, Error::ARGZERO, "Invalid data file section: Bonds"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Bonds"); bonds(firstpass); } else if (strcmp(keyword, "Angles") == 0) { topoflag = angleflag = 1; - if (nangles == 0) error->all(FLERR, "Invalid data file section: Angles"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Angles"); + if (nangles == 0) error->all(FLERR, Error::ARGZERO, "Invalid data file section: Angles"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Angles"); angles(firstpass); } else if (strcmp(keyword, "Dihedrals") == 0) { topoflag = dihedralflag = 1; - if (ndihedrals == 0) error->all(FLERR, "Invalid data file section: Dihedrals"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Dihedrals"); + if (ndihedrals == 0) + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Dihedrals"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Dihedrals"); dihedrals(firstpass); } else if (strcmp(keyword, "Impropers") == 0) { topoflag = improperflag = 1; - if (nimpropers == 0) error->all(FLERR, "Invalid data file section: Impropers"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Impropers"); + if (nimpropers == 0) + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Impropers"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Impropers"); impropers(firstpass); } else if (strcmp(keyword, "Ellipsoids") == 0) { ellipsoidflag = 1; - if (!avec_ellipsoid) error->all(FLERR, "Invalid data file section: Ellipsoids"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Ellipsoids"); + if (!avec_ellipsoid) + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Ellipsoids"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Ellipsoids"); if (firstpass) bonus(nellipsoids, (AtomVec *) avec_ellipsoid, "ellipsoids"); else @@ -756,8 +772,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Lines") == 0) { lineflag = 1; - if (!avec_line) error->all(FLERR, "Invalid data file section: Lines"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Lines"); + if (!avec_line) error->all(FLERR, Error::ARGZERO, "Invalid data file section: Lines"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Lines"); if (firstpass) bonus(nlines, (AtomVec *) avec_line, "lines"); else @@ -765,8 +781,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Triangles") == 0) { triflag = 1; - if (!avec_tri) error->all(FLERR, "Invalid data file section: Triangles"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Triangles"); + if (!avec_tri) error->all(FLERR, Error::ARGZERO, "Invalid data file section: Triangles"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Triangles"); if (firstpass) bonus(ntris, (AtomVec *) avec_tri, "triangles"); else @@ -774,8 +790,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Bodies") == 0) { bodyflag = 1; - if (!avec_body) error->all(FLERR, "Invalid data file section: Bodies"); - if (atomflag == 0) error->all(FLERR, "Must read Atoms before Bodies"); + if (!avec_body) error->all(FLERR, Error::ARGZERO, "Invalid data file section: Bodies"); + if (atomflag == 0) error->all(FLERR, Error::ARGZERO, "Must read Atoms before Bodies"); bodies(firstpass, (AtomVec *) avec_body); } else if (strcmp(keyword, "Masses") == 0) { @@ -784,7 +800,8 @@ void ReadData::command(int narg, char **arg) else skip_lines(ntypes); } else if (strcmp(keyword, "Pair Coeffs") == 0) { - if (force->pair == nullptr) error->all(FLERR, "Must define pair_style before Pair Coeffs"); + if (force->pair == nullptr) + error->all(FLERR, Error::ARGZERO, "Must define pair_style before Pair Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->pair_style)) error->warning( @@ -795,7 +812,7 @@ void ReadData::command(int narg, char **arg) skip_lines(ntypes); } else if (strcmp(keyword, "PairIJ Coeffs") == 0) { if (force->pair == nullptr) - error->all(FLERR, "Must define pair_style before PairIJ Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define pair_style before PairIJ Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->pair_style)) error->warning( @@ -806,8 +823,9 @@ void ReadData::command(int narg, char **arg) skip_lines(ntypes * (ntypes + 1) / 2); } else if (strcmp(keyword, "Bond Coeffs") == 0) { if (atom->avec->bonds_allow == 0) - error->all(FLERR, "Invalid data file section: Bond Coeffs"); - if (force->bond == nullptr) error->all(FLERR, "Must define bond_style before Bond Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Bond Coeffs"); + if (force->bond == nullptr) + error->all(FLERR, Error::ARGZERO, "Must define bond_style before Bond Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->bond_style)) error->warning( @@ -818,9 +836,9 @@ void ReadData::command(int narg, char **arg) skip_lines(nbondtypes); } else if (strcmp(keyword, "Angle Coeffs") == 0) { if (atom->avec->angles_allow == 0) - error->all(FLERR, "Invalid data file section: Angle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Angle Coeffs"); if (force->angle == nullptr) - error->all(FLERR, "Must define angle_style before Angle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define angle_style before Angle Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->angle_style)) error->warning( @@ -831,9 +849,9 @@ void ReadData::command(int narg, char **arg) skip_lines(nangletypes); } else if (strcmp(keyword, "Dihedral Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: Dihedral Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Dihedral Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before Dihedral Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define dihedral_style before Dihedral Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->dihedral_style)) error->warning( @@ -845,9 +863,9 @@ void ReadData::command(int narg, char **arg) skip_lines(ndihedraltypes); } else if (strcmp(keyword, "Improper Coeffs") == 0) { if (atom->avec->impropers_allow == 0) - error->all(FLERR, "Invalid data file section: Improper Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: Improper Coeffs"); if (force->improper == nullptr) - error->all(FLERR, "Must define improper_style before Improper Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define improper_style before Improper Coeffs"); if (firstpass) { if (me == 0 && !style_match(style, force->improper_style)) error->warning( @@ -860,9 +878,9 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "BondBond Coeffs") == 0) { if (atom->avec->angles_allow == 0) - error->all(FLERR, "Invalid data file section: BondBond Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: BondBond Coeffs"); if (force->angle == nullptr) - error->all(FLERR, "Must define angle_style before BondBond Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define angle_style before BondBond Coeffs"); if (firstpass) anglecoeffs(1); else @@ -870,18 +888,18 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "BondAngle Coeffs") == 0) { if (atom->avec->angles_allow == 0) - error->all(FLERR, "Invalid data file section: BondAngle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: BondAngle Coeffs"); if (force->angle == nullptr) - error->all(FLERR, "Must define angle_style before BondAngle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define angle_style before BondAngle Coeffs"); if (firstpass) anglecoeffs(2); else skip_lines(nangletypes); } else if (strcmp(keyword, "UreyBradley Coeffs") == 0) { if (atom->avec->angles_allow == 0) - error->all(FLERR, "Invalid data file section: UreyBradley Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: UreyBradley Coeffs"); if (force->angle == nullptr) - error->all(FLERR, "Must define angle_style before UreyBradley Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define angle_style before UreyBradley Coeffs"); if (firstpass) anglecoeffs(3); else @@ -889,9 +907,10 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "MiddleBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: MiddleBondTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: MiddleBondTorsion Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before MiddleBondTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, + "Must define dihedral_style before MiddleBondTorsion Coeffs"); if (firstpass) dihedralcoeffs(1); else @@ -899,9 +918,10 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "EndBondTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: EndBondTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: EndBondTorsion Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before EndBondTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, + "Must define dihedral_style before EndBondTorsion Coeffs"); if (firstpass) dihedralcoeffs(2); else @@ -909,9 +929,10 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "AngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: AngleTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: AngleTorsion Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before AngleTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, + "Must define dihedral_style before AngleTorsion Coeffs"); if (firstpass) dihedralcoeffs(3); else @@ -919,9 +940,10 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "AngleAngleTorsion Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: AngleAngleTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: AngleAngleTorsion Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before AngleAngleTorsion Coeffs"); + error->all(FLERR, Error::ARGZERO, + "Must define dihedral_style before AngleAngleTorsion Coeffs"); if (firstpass) dihedralcoeffs(4); else @@ -929,9 +951,9 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "BondBond13 Coeffs") == 0) { if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, "Invalid data file section: BondBond13 Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: BondBond13 Coeffs"); if (force->dihedral == nullptr) - error->all(FLERR, "Must define dihedral_style before BondBond13 Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define dihedral_style before BondBond13 Coeffs"); if (firstpass) dihedralcoeffs(5); else @@ -939,9 +961,9 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "AngleAngle Coeffs") == 0) { if (atom->avec->impropers_allow == 0) - error->all(FLERR, "Invalid data file section: AngleAngle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Invalid data file section: AngleAngle Coeffs"); if (force->improper == nullptr) - error->all(FLERR, "Must define improper_style before AngleAngle Coeffs"); + error->all(FLERR, Error::ARGZERO, "Must define improper_style before AngleAngle Coeffs"); if (firstpass) impropercoeffs(1); else @@ -949,7 +971,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Atom Type Labels") == 0) { if (firstpass) { - if (atomflag == 1) error->all(FLERR, "Must read Atom Type Labels before Atoms"); + if (atomflag == 1) + error->all(FLERR, Error::ARGZERO, "Must read Atom Type Labels before Atoms"); tlabelflag = 1; typelabels(Atom::ATOM); } else @@ -958,7 +981,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Bond Type Labels") == 0) { if (nbondtypes) { if (firstpass) { - if (bondflag == 1) error->all(FLERR, "Must read Bond Type Labels before Bonds"); + if (bondflag == 1) + error->all(FLERR, Error::ARGZERO, "Must read Bond Type Labels before Bonds"); blabelflag = 1; typelabels(Atom::BOND); } else @@ -968,7 +992,8 @@ void ReadData::command(int narg, char **arg) } else if (strcmp(keyword, "Angle Type Labels") == 0) { if (nangletypes) { if (firstpass) { - if (angleflag == 1) error->all(FLERR, "Must read Angle Type Labels before Angles"); + if (angleflag == 1) + error->all(FLERR, Error::ARGZERO, "Must read Angle Type Labels before Angles"); alabelflag = 1; typelabels(Atom::ANGLE); } else @@ -979,7 +1004,7 @@ void ReadData::command(int narg, char **arg) if (ndihedraltypes) { if (firstpass) { if (dihedralflag == 1) - error->all(FLERR, "Must read Dihedral Type Labels before Dihedrals"); + error->all(FLERR, Error::ARGZERO, "Must read Dihedral Type Labels before Dihedrals"); dlabelflag = 1; typelabels(Atom::DIHEDRAL); } else @@ -990,7 +1015,7 @@ void ReadData::command(int narg, char **arg) if (nimpropertypes) { if (firstpass) { if (improperflag == 1) - error->all(FLERR, "Must read Improper Type Labels before Impropers"); + error->all(FLERR, Error::ARGZERO, "Must read Improper Type Labels before Impropers"); ilabelflag = 1; typelabels(Atom::IMPROPER); } else @@ -1013,17 +1038,20 @@ void ReadData::command(int narg, char **arg) break; } if (i == nfix) - error->all(FLERR, "Unknown identifier in data file: {}{}", keyword, utils::errorurl(1)); + error->all(FLERR, Error::ARGZERO, "Unknown identifier in data file: {}{}", keyword, + utils::errorurl(1)); } else - error->all(FLERR, "Unknown identifier in data file: {}{}", keyword, utils::errorurl(1)); + error->all(FLERR, Error::ARGZERO, "Unknown identifier in data file: {}{}", keyword, + utils::errorurl(1)); parse_keyword(0); } // error if natoms > 0 yet no atoms were read - if (natoms > 0 && atomflag == 0) error->all(FLERR, "No valid atoms found in data file"); + if (natoms > 0 && atomflag == 0) + error->all(FLERR, Error::ARGZERO, "No valid atoms found in data file"); // close file diff --git a/src/set.cpp b/src/set.cpp index 7ae41e9246..f42dd175ab 100644 --- a/src/set.cpp +++ b/src/set.cpp @@ -42,739 +42,418 @@ using namespace LAMMPS_NS; using namespace MathConst; +enum{SETCOMMAND,FIXSET}; // also used in FixSet class + enum{ATOM_SELECT,MOL_SELECT,TYPE_SELECT,GROUP_SELECT,REGION_SELECT}; -enum{TYPE,TYPE_FRACTION,TYPE_RATIO,TYPE_SUBSET, - MOLECULE,X,Y,Z,VX,VY,VZ,CHARGE,MASS,SHAPE,LENGTH,TRI, - DIPOLE,DIPOLE_RANDOM,SPIN_ATOM,SPIN_RANDOM,SPIN_ELECTRON,RADIUS_ELECTRON, - QUAT,QUAT_RANDOM,THETA,THETA_RANDOM,ANGMOM,OMEGA,TEMPERATURE, - DIAMETER,RADIUS_ATOM,DENSITY,VOLUME,IMAGE,BOND,ANGLE,DIHEDRAL,IMPROPER, - RHEO_STATUS,SPH_E,SPH_CV,SPH_RHO,EDPD_TEMP,EDPD_CV,CC,SMD_MASS_DENSITY, - SMD_CONTACT_RADIUS,DPDTHETA,EPSILON,IVEC,DVEC,IARRAY,DARRAY}; +enum{ANGLE,ANGMOM,BOND,CC,CHARGE,DENSITY,DIAMETER,DIHEDRAL,DIPOLE, + DIPOLE_RANDOM,DPD_THETA,EDPD_CV,EDPD_TEMP,EPSILON,IMAGE,IMPROPER,LENGTH, + MASS,MOLECULE,OMEGA,QUAT,QUAT_RANDOM,RADIUS_ELECTRON,SHAPE, + SMD_CONTACT_RADIUS,SMD_MASS_DENSITY,SPH_CV,SPH_E,SPH_RHO, + SPIN_ATOM,SPIN_ATOM_RANDOM,SPIN_ELECTRON,TEMPERATURE,THETA,THETA_RANDOM, + TRI,TYPE,TYPE_FRACTION,TYPE_RATIO,TYPE_SUBSET,VOLUME,VX,VY,VZ,X,Y,Z, + IVEC,DVEC,IARRAY,DARRAY}; + +#define DELTA 4 + +/* ---------------------------------------------------------------------- */ + +Set::Set(class LAMMPS *lmp) : + Command(lmp), id(nullptr), region(nullptr), actions(nullptr), invoke_choice(nullptr), + vec1(nullptr), vec2(nullptr), vec3(nullptr), vec4(nullptr), select(nullptr) +{ + maxselect = maxvariable = 0; +} + +/* ---------------------------------------------------------------------- */ + +Set::~Set() +{ + memory->sfree(actions); + memory->sfree(invoke_choice); + + memory->destroy(select); + memory->destroy(vec1); + memory->destroy(vec2); + memory->destroy(vec3); + memory->destroy(vec4); +} /* ---------------------------------------------------------------------- */ void Set::command(int narg, char **arg) { if (domain->box_exist == 0) - error->all(FLERR, Error::NOLASTLINE, "Set command before simulation box is defined" - + utils::errorurl(0)); + error->all(FLERR, Error::NOLASTLINE, + "Set command before simulation box is defined" + utils::errorurl(0)); if (atom->natoms == 0) error->all(FLERR, Error::NOLASTLINE, "Set command on system without atoms"); if (narg < 4) error->all(FLERR, 1, "Illegal set command: need at least four arguments"); - // style and ID info + process_args(SETCOMMAND, narg, arg); - if (strcmp(arg[0],"atom") == 0) style = ATOM_SELECT; - else if (strcmp(arg[0],"mol") == 0) style = MOL_SELECT; - else if (strcmp(arg[0],"type") == 0) style = TYPE_SELECT; - else if (strcmp(arg[0],"group") == 0) style = GROUP_SELECT; - else if (strcmp(arg[0],"region") == 0) style = REGION_SELECT; - else error->all(FLERR, Error::ARGZERO, "Unknown set command style: {}", arg[0]); + if (comm->me == 0) utils::logmesg(lmp, "Setting atom values ...\n"); + + // select which atoms to act on - id = utils::strdup(arg[1]); - select = nullptr; selection(atom->nlocal); - // loop over keyword/value pairs - // call appropriate routine to reset attributes + // loop over list of actions to reset atom attributes - if (comm->me == 0) utils::logmesg(lmp,"Setting atom values ...\n"); + invoke_actions(); - int allcount,origarg; + // print stats for each action + // for CC option, include species index + + bigint bcount, allcount; + + for (int i = 0; i < naction; i++) { + Action *action = &actions[i]; + int iarg = action->argindex; + + if (action->count_action < 0) { + bcount = action->count_select; + } else { + bcount = action->count_action; + } + MPI_Allreduce(&bcount, &allcount, 1, MPI_LMP_BIGINT, MPI_SUM, world); + + if (comm->me == 0) { + if (strcmp(arg[iarg], "cc") == 0) { + utils::logmesg(lmp, " {} settings made for {} index {}\n", allcount, arg[iarg], + arg[iarg + 1]); + } else { + utils::logmesg(lmp, " {} settings made for {}\n", allcount, arg[iarg]); + } + } + } +} + +/* ---------------------------------------------------------------------- + process args of set command or fix set command +------------------------------------------------------------------------- */ + +void Set::process_args(int caller_flag, int narg, char **arg) +{ + caller = caller_flag; + + // style and ID info + + id = utils::strdup(arg[1]); + + if (strcmp(arg[0], "atom") == 0) { + style = ATOM_SELECT; + if (atom->tag_enable == 0) error->all(FLERR, "Cannot use set atom with no atom IDs defined"); + utils::bounds(FLERR, id, 1, MAXTAGINT, nlobig, nhibig, error); + + } else if (strcmp(arg[0], "mol") == 0) { + style = MOL_SELECT; + if (atom->molecule_flag == 0) + error->all(FLERR, "Cannot use set mol with no molecule IDs defined"); + utils::bounds(FLERR, id, 1, MAXTAGINT, nlobig, nhibig, error); + + } else if (strcmp(arg[0], "type") == 0) { + style = TYPE_SELECT; + if (char *typestr = utils::expand_type(FLERR, id, Atom::ATOM, lmp)) { + delete[] id; + id = typestr; + } + utils::bounds(FLERR, id, 1, atom->ntypes, nlo, nhi, error); + + } else if (strcmp(arg[0], "group") == 0) { + style = GROUP_SELECT; + int igroup = group->find(id); + if (igroup == -1) error->all(FLERR, "Could not find set group ID {}", id); + groupbit = group->bitmask[igroup]; + + } else if (strcmp(arg[0], "region") == 0) { + style = REGION_SELECT; + region = domain->get_region_by_id(id); + if (!region) error->all(FLERR, "Set region {} does not exist", id); + + } else + error->all(FLERR, "Unknown set or fix set command style: {}", arg[0]); + + delete[] id; + + // loop over remaining keyword/value pairs to create list of actions + // one action = keyword/value pair + + naction = maxaction = 0; + actions = nullptr; + Action *action; int iarg = 2; while (iarg < narg) { - varflag = varflag1 = varflag2 = varflag3 = varflag4 = 0; - count = 0; - origarg = iarg; - if (strcmp(arg[iarg],"type") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set type", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1], 1); - else ivalue = utils::expand_type_int(FLERR, arg[iarg+1], Atom::ATOM, lmp); - set(TYPE); - iarg += 2; + // grow list of actions if needed - } else if (strcmp(arg[iarg],"type/fraction") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/fraction", error); - newtype = utils::expand_type_int(FLERR, arg[iarg+1], Atom::ATOM, lmp); - fraction = utils::numeric(FLERR, arg[iarg+2], false, lmp); - ivalue = utils::inumeric(FLERR, arg[iarg+3], false, lmp); - if (newtype <= 0 || newtype > atom->ntypes) - error->all(FLERR, iarg + 1, "Invalid type value {} in set type/fraction command", newtype); - if (fraction < 0.0 || fraction > 1.0) - error->all(FLERR, iarg + 2, "Invalid fraction value {} in set type/fraction command", - fraction); - if (ivalue <= 0) - error->all(FLERR, iarg + 3, "Invalid random number seed {} in set type/fraction command", - ivalue); - setrandom(TYPE_FRACTION); - iarg += 4; - - } else if (strcmp(arg[iarg],"type/ratio") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/ratio", error); - newtype = utils::expand_type_int(FLERR, arg[iarg+1], Atom::ATOM, lmp); - fraction = utils::numeric(FLERR, arg[iarg+2], false, lmp); - ivalue = utils::inumeric(FLERR, arg[iarg+3], false, lmp); - if (newtype <= 0 || newtype > atom->ntypes) - error->all(FLERR, iarg + 1, "Invalid type value {} in set type/ratio command", newtype); - if (fraction < 0.0 || fraction > 1.0) - error->all(FLERR, iarg + 2, "Invalid fraction value {} in set type/ratio command", - fraction); - if (ivalue <= 0) - error->all(FLERR, iarg + 3, "Invalid random number seed {} in set type/ratio command", - ivalue); - setrandom(TYPE_RATIO); - iarg += 4; - - } else if (strcmp(arg[iarg],"type/subset") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/subset", error); - newtype = utils::expand_type_int(FLERR, arg[iarg+1], Atom::ATOM, lmp); - nsubset = utils::bnumeric(FLERR, arg[iarg+2], false, lmp); - ivalue = utils::inumeric(FLERR, arg[iarg+3], false, lmp); - if (newtype <= 0 || newtype > atom->ntypes) - error->all(FLERR, iarg + 1, "Invalid type value {} in set type/subset command", newtype); - if (nsubset < 0) - error->all(FLERR, iarg + 2, "Invalid subset size {} in set type/subset command", nsubset); - if (ivalue <= 0) - error->all(FLERR, iarg + 3, "Invalid random number seed {} in set type/subset command", - ivalue); - setrandom(TYPE_SUBSET); - iarg += 4; - - } else if (strcmp(arg[iarg],"mol") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set mol", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->molecule_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(MOLECULE); - iarg += 2; - - } else if (strcmp(arg[iarg],"x") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set x", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(X); - iarg += 2; - - } else if (strcmp(arg[iarg],"y") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set y", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(Y); - iarg += 2; - - } else if (strcmp(arg[iarg],"z") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set z", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(Z); - iarg += 2; - - } else if (strcmp(arg[iarg],"vx") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vx", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(VX); - iarg += 2; - - } else if (strcmp(arg[iarg],"vy") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vy", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(VY); - iarg += 2; - - } else if (strcmp(arg[iarg],"vz") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vz", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - set(VZ); - iarg += 2; - - } else if (strcmp(arg[iarg],"charge") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set charge", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->q_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(CHARGE); - iarg += 2; - - } else if (strcmp(arg[iarg],"mass") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set mass", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->rmass_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(MASS); - iarg += 2; - - } else if (strcmp(arg[iarg],"shape") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set shape", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else xvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (!atom->ellipsoid_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SHAPE); - iarg += 4; - - } else if (strcmp(arg[iarg],"length") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set length", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->line_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(LENGTH); - iarg += 2; - - } else if (strcmp(arg[iarg],"tri") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set tri", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->tri_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(TRI); - iarg += 2; - - } else if (strcmp(arg[iarg],"dipole") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set dipole", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else xvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (!atom->mu_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(DIPOLE); - iarg += 4; - - } else if (strcmp(arg[iarg],"dipole/random") == 0) { - if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "set dipole/random", error); - ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - dvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (!atom->mu_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0) - error->all(FLERR, iarg + 1, "Invalid random number seed in set command"); - if (dvalue <= 0.0) - error->all(FLERR, iarg + 2, "Invalid dipole length in set command"); - setrandom(DIPOLE_RANDOM); - iarg += 3; - - } else if ((strcmp(arg[iarg],"spin") == 0) || (strcmp(arg[iarg],"spin/atom") == 0)) { - if ((strcmp(arg[iarg],"spin") == 0) && (comm->me == 0)) - error->warning(FLERR, "Set attribute spin is deprecated. Please use spin/atom instead."); - if (iarg+5 > narg) utils::missing_cmd_args(FLERR, "set spin/atom", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else xvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else yvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (utils::strmatch(arg[iarg+4],"^v_")) varparse(arg[iarg+4],4); - else zvalue = utils::numeric(FLERR,arg[iarg+4],false,lmp); - if ((xvalue == 0.0) && (yvalue == 0.0) && (zvalue == 0.0)) - error->all(FLERR, Error::NOPOINTER, "At least one spin vector component must be non-zero"); - if (!atom->sp_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (dvalue <= 0.0) - error->all(FLERR, iarg + 1, "Invalid spin magnitude {} in set {} command", dvalue, - arg[iarg]); - set(SPIN_ATOM); - iarg += 5; - - } else if ((strcmp(arg[iarg],"spin/random") == 0) || - (strcmp(arg[iarg],"spin/atom/random") == 0)) { - if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "set spin/atom/random", error); - ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - dvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if ((strcmp(arg[iarg],"spin/random") == 0) && (comm->me == 0)) - error->warning(FLERR, "Set attribute spin/random is deprecated. " - "Please use spin/atom/random instead."); - if (!atom->sp_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0) - error->all(FLERR, iarg + 1, "Invalid random number seed {} in set {} command", ivalue, - arg[iarg]); - if (dvalue <= 0.0) - error->all(FLERR, iarg + 2, "Invalid spin magnitude {} in set {} command", dvalue, - arg[iarg]); - setrandom(SPIN_RANDOM); - iarg += 3; - - } else if (strcmp(arg[iarg],"radius/electron") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set radius/electron", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->eradius_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(RADIUS_ELECTRON); - iarg += 2; - - } else if (strcmp(arg[iarg],"spin/electron") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set spin/electron", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->spin_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SPIN_ELECTRON); - iarg += 2; - - } else if (strcmp(arg[iarg],"quat") == 0) { - if (iarg+5 > narg) utils::missing_cmd_args(FLERR, "set quat", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else xvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (utils::strmatch(arg[iarg+4],"^v_")) varparse(arg[iarg+4],4); - else wvalue = utils::numeric(FLERR,arg[iarg+4],false,lmp); - if (!atom->ellipsoid_flag && !atom->tri_flag && !atom->body_flag && !atom->quat_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(QUAT); - iarg += 5; - - } else if (strcmp(arg[iarg],"quat/random") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set quat/random", error); - ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->ellipsoid_flag && !atom->tri_flag && !atom->body_flag && !atom->quat_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0) - error->all(FLERR, iarg + 1, "Invalid random number seed in set command"); - setrandom(QUAT_RANDOM); - iarg += 2; - - } else if (strcmp(arg[iarg],"theta") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set theta", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = DEG2RAD * utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->line_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(THETA); - iarg += 2; - - } else if (strcmp(arg[iarg],"theta/random") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set theta/random", error); - ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->line_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0) - error->all(FLERR, iarg + 1, "Invalid random number seed in set command"); - set(THETA_RANDOM); - iarg += 2; - - } else if (strcmp(arg[iarg],"angmom") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set angmom", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else xvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (!atom->angmom_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(ANGMOM); - iarg += 4; - - } else if (strcmp(arg[iarg],"omega") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set omega", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else xvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zvalue = utils::numeric(FLERR,arg[iarg+3],false,lmp); - if (!atom->omega_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(OMEGA); - iarg += 4; - - } else if (strcmp(arg[iarg],"radius/atom") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set radius/atom", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->radius_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(RADIUS_ATOM); - iarg += 2; - - } else if (strcmp(arg[iarg],"diameter") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set diameter", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->radius_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(DIAMETER); - iarg += 2; - - } else if (strcmp(arg[iarg],"density") == 0 || - (strcmp(arg[iarg],"density/disc") == 0)) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set density", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->rmass_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (dvalue <= 0.0) error->all(FLERR, iarg + 1, "Invalid density in set command"); - discflag = 0; - if (strcmp(arg[iarg],"density/disc") == 0) { - discflag = 1; - if (domain->dimension != 2) - error->all(FLERR, Error::NOLASTLINE, "Density/disc option requires 2d simulation"); - } - set(DENSITY); - iarg += 2; - - } else if (strcmp(arg[iarg],"temperature") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal set command"); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->temperature_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(TEMPERATURE); - iarg += 2; - - } else if (strcmp(arg[iarg],"volume") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set volume", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->vfrac_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (dvalue <= 0.0) error->all(FLERR, iarg + 1, "Invalid volume in set command"); - set(VOLUME); - iarg += 2; - - } else if (strcmp(arg[iarg],"image") == 0) { - if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set image", error); - ximageflag = yimageflag = zimageflag = 0; - if (strcmp(arg[iarg+1],"NULL") != 0) { - ximageflag = 1; - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else ximage = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - } - if (strcmp(arg[iarg+2],"NULL") != 0) { - yimageflag = 1; - if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2); - else yimage = utils::inumeric(FLERR,arg[iarg+2],false,lmp); - } - if (strcmp(arg[iarg+3],"NULL") != 0) { - zimageflag = 1; - if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3); - else zimage = utils::inumeric(FLERR,arg[iarg+3],false,lmp); - } - if (ximageflag && ximage && !domain->xperiodic) - error->all(FLERR, iarg + 1,"Cannot set non-zero image flag for non-periodic dimension"); - if (yimageflag && yimage && !domain->yperiodic) - error->all(FLERR, iarg + 2, "Cannot set non-zero image flag for non-periodic dimension"); - if (zimageflag && zimage && !domain->zperiodic) - error->all(FLERR, iarg + 3, "Cannot set non-zero image flag for non-periodic dimension"); - set(IMAGE); - iarg += 4; - - } else if (strcmp(arg[iarg],"bond") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set bond", error); - ivalue = utils::expand_type_int(FLERR, arg[iarg+1], Atom::BOND, lmp); - if (atom->avec->bonds_allow == 0) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0 || ivalue > atom->nbondtypes) - error->all(FLERR, iarg + 1, "Invalid value {} in set bond command", ivalue); - topology(BOND); - iarg += 2; - - } else if (strcmp(arg[iarg],"angle") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set angle", error); - ivalue = utils::expand_type_int(FLERR, arg[iarg+1], Atom::ANGLE, lmp); - if (atom->avec->angles_allow == 0) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0 || ivalue > atom->nangletypes) - error->all(FLERR, iarg + 1, "Invalid value {} in set angle command", ivalue); - topology(ANGLE); - iarg += 2; - - } else if (strcmp(arg[iarg],"dihedral") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set dihedral", error); - ivalue = utils::expand_type_int(FLERR, arg[iarg+1], Atom::DIHEDRAL, lmp); - if (atom->avec->dihedrals_allow == 0) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0 || ivalue > atom->ndihedraltypes) - error->all(FLERR, iarg + 1, "Invalid value {} in set dihedral command", ivalue); - topology(DIHEDRAL); - iarg += 2; - - } else if (strcmp(arg[iarg],"improper") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set improper", error); - ivalue = utils::expand_type_int(FLERR, arg[iarg+1], Atom::IMPROPER, lmp); - if (atom->avec->impropers_allow == 0) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - if (ivalue <= 0 || ivalue > atom->nimpropertypes) - error->all(FLERR, iarg + 1, "Invalid value {} in set improper command", ivalue); - topology(IMPROPER); - iarg += 2; - - } else if (strcmp(arg[iarg],"rheo/rho") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set rheo/rho", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->rho_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SPH_RHO); - iarg += 2; - - } else if (strcmp(arg[iarg],"rheo/status") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set rheo/status", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->rheo_status_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(RHEO_STATUS); - iarg += 2; - - } else if (strcmp(arg[iarg],"sph/e") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/e", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->esph_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SPH_E); - iarg += 2; - - } else if (strcmp(arg[iarg],"sph/cv") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/cv", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->cv_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SPH_CV); - iarg += 2; - - } else if (strcmp(arg[iarg],"sph/rho") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/rho", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->rho_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SPH_RHO); - iarg += 2; - - } else if (strcmp(arg[iarg],"edpd/temp") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set edpd/temp", error); - if (strcmp(arg[iarg+1],"NULL") == 0) dvalue = -1.0; - else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else { - dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (dvalue < 0.0) - error->all(FLERR, iarg + 1, "Invalid value {} in set edpd/temp command", dvalue); - } - if (!atom->edpd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(EDPD_TEMP); - iarg += 2; - - } else if (strcmp(arg[iarg],"edpd/cv") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set edpd/cv", error); - if (strcmp(arg[iarg+1],"NULL") == 0) dvalue = -1.0; - else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else { - dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (dvalue < 0.0) - error->all(FLERR, iarg + 1, "Invalid value {} in set edpd/cv command", dvalue); - } - if (!atom->edpd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(EDPD_CV); - iarg += 2; - - } else if (strcmp(arg[iarg],"cc") == 0) { - if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "set cc", error); - if (strcmp(arg[iarg+1],"NULL") == 0) dvalue = -1.0; - else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else { - cc_index = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - dvalue = utils::numeric(FLERR,arg[iarg+2],false,lmp); - if (cc_index < 1) error->all(FLERR,"Illegal set command"); - error->all(FLERR, iarg + 1, "Invalid index value {} in set cc command", cc_index); - } - if (!atom->tdpd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(CC); - iarg += 3; - - } else if (strcmp(arg[iarg],"smd/mass/density") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set smd/mass/density", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->smd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SMD_MASS_DENSITY); - iarg += 2; - - } else if (strcmp(arg[iarg],"smd/contact/radius") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set smd/contact/radius", error); - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (!atom->smd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(SMD_CONTACT_RADIUS); - iarg += 2; - - } else if (strcmp(arg[iarg],"dpd/theta") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set dpd/theta", error); - if (strcmp(arg[iarg+1],"NULL") == 0) dvalue = -1.0; - else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else { - dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (dvalue < 0.0) - error->all(FLERR, iarg + 1, "Invalid value {} in set dpd/theta command", dvalue); - } - if (!atom->dpd_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(DPDTHETA); - iarg += 2; - - } else if (strcmp(arg[iarg],"epsilon") == 0) { - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set epsilon", error); - if (strcmp(arg[iarg+1],"NULL") == 0) dvalue = -1.0; - else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else { - dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (dvalue < 0.0) - error->all(FLERR, iarg + 1, "Invalid value {} in set epsilon command", dvalue); - } - if (!atom->dielectric_flag) - error->all(FLERR, iarg, "Cannot set attribute {} for atom style {}", arg[iarg], - atom->get_style()); - set(EPSILON); - iarg += 2; - - } else { - - // set custom per-atom vector or array or error out - - int flag,cols; - ArgInfo argi(arg[iarg],ArgInfo::DNAME|ArgInfo::INAME); - const char *pname = argi.get_name(); - if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set", error); - index_custom = atom->find_custom(argi.get_name(),flag,cols); - if (index_custom < 0) - error->all(FLERR, iarg, "Set keyword or custom property {} does not exist", pname); - - switch (argi.get_type()) { - - case ArgInfo::INAME: - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else ivalue = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - if (flag != 0) - error->all(FLERR, iarg + 1, "Set command custom property {} is not integer", pname); - - if (argi.get_dim() == 0) { - if (cols > 0) - error->all(FLERR, iarg, "Set command custom integer property {} is not a vector", - pname); - set(IVEC); - } else if (argi.get_dim() == 1) { - if (cols == 0) - error->all(FLERR, iarg, "Set command custom integer property {} is not an array", - pname); - icol_custom = argi.get_index1(); - if (icol_custom <= 0 || icol_custom > cols) - error->all(FLERR, iarg, "Set command per-atom custom integer array {} is accessed " - "out-of-range{}", pname, utils::errorurl(20)); - set(IARRAY); - } else error->all(FLERR,"Illegal set command"); - break; - - case ArgInfo::DNAME: - if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1); - else dvalue = utils::numeric(FLERR,arg[iarg+1],false,lmp); - if (flag != 1) error->all(FLERR,"Custom property {} is not floating-point",argi.get_name()); - - if (argi.get_dim() == 0) { - if (cols > 0) - error->all(FLERR, iarg, "Set command custom double property {} is not a vector", pname); - set(DVEC); - } else if (argi.get_dim() == 1) { - if (cols == 0) - error->all(FLERR, iarg, "Set command custom double property {} is not an array", pname); - icol_custom = argi.get_index1(); - if (icol_custom <= 0 || icol_custom > cols) - error->all(FLERR, iarg, "Set command per-atom custom double array {} is accessed " - "out-of-range{}", pname, utils::errorurl(20)); - set(DARRAY); - } else error->all(FLERR,"Illegal set command"); - break; - - default: - error->all(FLERR,"Illegal set command"); - break; - } - iarg += 2; + if (naction == maxaction) { + maxaction += DELTA; + actions = (Action *) memory->srealloc(actions,maxaction*sizeof(Action),"set:actions"); + invoke_choice = (FnPtrPack *) + memory->srealloc(invoke_choice,maxaction*sizeof(FnPtrPack),"set:invoke_choice"); } - // statistics - // for CC option, include species index + action = &actions[naction]; + action->argindex = iarg; + action->varflag = 0; + action->varflag1 = action->varflag2 = action->varflag3 = action->varflag4 = 0; - MPI_Allreduce(&count,&allcount,1,MPI_INT,MPI_SUM,world); + if (strcmp(arg[iarg],"angle") == 0) { + action->keyword = ANGLE; + process_angle(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_angle; + } else if (strcmp(arg[iarg],"angmom") == 0) { + action->keyword = ANGMOM; + process_angmom(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_angmom; + } else if (strcmp(arg[iarg],"bond") == 0) { + action->keyword = BOND; + process_bond(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_bond; + } else if (strcmp(arg[iarg],"cc") == 0) { + action->keyword = CC; + process_cc(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_cc; + } else if (strcmp(arg[iarg],"charge") == 0) { + action->keyword = CHARGE; + process_charge(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_charge; + } else if (strcmp(arg[iarg],"density") == 0 ||(strcmp(arg[iarg],"density/disc") == 0)) { + action->keyword = DENSITY; + process_density(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_density; + } else if (strcmp(arg[iarg],"diameter") == 0) { + action->keyword = DIAMETER; + process_diameter(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_diameter; + } else if (strcmp(arg[iarg],"dihedral") == 0) { + action->keyword = DIHEDRAL; + process_dihedral(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_dihedral; + } else if (strcmp(arg[iarg],"dipole") == 0) { + action->keyword = DIPOLE; + process_dipole(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_dipole; + } else if (strcmp(arg[iarg],"dipole/random") == 0) { + action->keyword = DIPOLE_RANDOM; + process_dipole_random(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_dipole_random; + } else if (strcmp(arg[iarg],"dpd/theta") == 0) { + action->keyword = DPD_THETA; + process_dpd_theta(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_dpd_theta; + } else if (strcmp(arg[iarg],"edpd/cv") == 0) { + action->keyword = EDPD_CV; + process_edpd_cv(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_edpd_cv; + } else if (strcmp(arg[iarg],"edpd/temp") == 0) { + action->keyword = EDPD_TEMP; + process_edpd_temp(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_edpd_temp; + } else if (strcmp(arg[iarg],"epsilon") == 0) { + action->keyword = EPSILON; + process_epsilon(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_epsilon; + } else if (strcmp(arg[iarg],"image") == 0) { + action->keyword = IMAGE; + process_image(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_image; + } else if (strcmp(arg[iarg],"improper") == 0) { + action->keyword = IMPROPER; + process_improper(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_improper; + } else if (strcmp(arg[iarg],"length") == 0) { + action->keyword = LENGTH; + process_length(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_length; + } else if (strcmp(arg[iarg],"mass") == 0) { + action->keyword = MASS; + process_mass(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_mass; + } else if (strcmp(arg[iarg],"mol") == 0) { + action->keyword = MOLECULE; + process_mol(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_mol; + } else if (strcmp(arg[iarg],"omega") == 0) { + action->keyword = OMEGA; + process_omega(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_omega; + } else if (strcmp(arg[iarg],"quat") == 0) { + action->keyword = QUAT; + process_quat(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_quat; + } else if (strcmp(arg[iarg],"quat/random") == 0) { + action->keyword = QUAT_RANDOM; + process_quat_random(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_quat_random; + } else if (strcmp(arg[iarg],"radius/electron") == 0) { + action->keyword = RADIUS_ELECTRON; + process_radius_election(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_radius_election; + } else if (strcmp(arg[iarg],"shape") == 0) { + action->keyword = SHAPE; + process_shape(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_shape; + } else if (strcmp(arg[iarg],"smd/contact/radius") == 0) { + action->keyword = SMD_CONTACT_RADIUS; + process_smd_contact_radius(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_smd_contact_radius; + } else if (strcmp(arg[iarg],"smd/mass/density") == 0) { + action->keyword = SMD_MASS_DENSITY; + process_smd_mass_density(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_smd_mass_density; + } else if (strcmp(arg[iarg],"sph/cv") == 0) { + action->keyword = SPH_CV; + process_sph_cv(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_sph_cv; + } else if (strcmp(arg[iarg],"sph/e") == 0) { + action->keyword = SPH_E; + process_sph_e(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_sph_e; + } else if (strcmp(arg[iarg],"sph/rho") == 0) { + action->keyword = SPH_RHO; + process_sph_rho(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_sph_rho; + } else if ((strcmp(arg[iarg],"spin/atom") == 0) || (strcmp(arg[iarg],"spin") == 0)) { + action->keyword = SPIN_ATOM; + process_spin_atom(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_spin_atom; + } else if ((strcmp(arg[iarg],"spin/atom/random") == 0) || (strcmp(arg[iarg],"spin/random") == 0)) { + action->keyword = SPIN_ATOM_RANDOM; + process_spin_atom_random(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_spin_atom_random; + } else if (strcmp(arg[iarg],"spin/electron") == 0) { + action->keyword = SPIN_ELECTRON; + process_spin_electron(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_spin_electron; + } else if (strcmp(arg[iarg],"temperature") == 0) { + action->keyword = TEMPERATURE; + process_temperature(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_temperature; + } else if (strcmp(arg[iarg],"theta") == 0) { + action->keyword = THETA; + process_theta(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_theta; + } else if (strcmp(arg[iarg],"theta/random") == 0) { + action->keyword = THETA_RANDOM; + process_theta_random(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_theta_random; + } else if (strcmp(arg[iarg],"tri") == 0) { + action->keyword = TRI; + process_tri(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_tri; + } else if (strcmp(arg[iarg],"type") == 0) { + action->keyword = TYPE; + process_type(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_type; + } else if (strcmp(arg[iarg],"type/fraction") == 0) { + action->keyword = TYPE_FRACTION; + process_type_fraction(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_type_fraction; + } else if (strcmp(arg[iarg],"type/ratio") == 0) { + action->keyword = TYPE_RATIO; + process_type_ratio(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_type_ratio; + } else if (strcmp(arg[iarg],"type/subset") == 0) { + action->keyword = TYPE_SUBSET; + process_type_subset(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_type_subset; + } else if (strcmp(arg[iarg],"volume") == 0) { + action->keyword = VOLUME; + process_volume(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_volume; + } else if (strcmp(arg[iarg],"vx") == 0) { + action->keyword = VX; + process_vx(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_vx; + } else if (strcmp(arg[iarg],"vy") == 0) { + action->keyword = VY; + process_vy(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_vy; + } else if (strcmp(arg[iarg],"vz") == 0) { + action->keyword = VZ; + process_vz(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_vz; + } else if (strcmp(arg[iarg],"x") == 0) { + action->keyword = X; + process_x(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_x; + } else if (strcmp(arg[iarg],"y") == 0) { + action->keyword = Y; + process_y(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_y; + } else if (strcmp(arg[iarg],"z") == 0) { + action->keyword = Z; + process_z(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_z; - if (comm->me == 0) { - if (strcmp(arg[origarg],"cc") == 0) - utils::logmesg(lmp," {} settings made for {} index {}\n", - allcount,arg[origarg],arg[origarg+1]); - else - utils::logmesg(lmp," {} settings made for {}\n", - allcount,arg[origarg]); + } else if (utils::strmatch(arg[iarg],"^[id]2?_")) { + process_custom(iarg,narg,arg,action); + invoke_choice[naction++] = &Set::invoke_custom; + + // unrecognized keyword + + } else error->all(FLERR,"Unrecognized set or fix set command keyword {}",arg[iarg]); + } + + // varflag = 1 if any action uses a per-atom variable + // varflag1234 = 1 if any action uses a specific per-atom variable + + varflag = 0; + varflag1 = varflag2 = varflag3 = varflag4 = 0; + for (int i = 0; i < naction; i++) { + action = &actions[i]; + if (action->varflag) varflag = 1; + if (action->varflag1) varflag1 = 1; + if (action->varflag2) varflag2 = 1; + if (action->varflag3) varflag3 = 1; + if (action->varflag4) varflag4 = 1; + } + + // error if any action of fix set command does not use a per-atom variable + // b/c fix set is then effectivly a no-op + + if (caller == FIXSET) { + for (int i = 0; i < naction; i++) { + action = &actions[i]; + if (!action->varflag) + error->all(FLERR,"Fix set command keyword {} does not invoke a per-atom variable", + arg[action->argindex]); } } - // free local memory + // warn if a keyword sets properties for atoms in rigid bodies + // assume no conflict for properties not in this list of cases - delete[] id; - delete[] select; + for (int i = 0; i < naction; i++) { + switch (actions[i].keyword) { + case X: + case Y: + case Z: + case MOLECULE: + case MASS: + case ANGMOM: + case SHAPE: + case DIAMETER: + case DENSITY: + case TEMPERATURE: + case QUAT: + case IMAGE: + if (modify->check_rigid_list_overlap(select)) + error->warning(FLERR,"Setting a property of atoms in rigid bodies " + "that has no effect unless rigid bodies are re-initialized"); + break; + default: + break; + } + } + + // in future, could return index to next arg to process + // i.e. if fix set command appends its own options + + // return iarg; } /* ---------------------------------------------------------------------- @@ -784,468 +463,143 @@ void Set::command(int narg, char **arg) void Set::selection(int n) { - delete[] select; - select = new int[n]; - int nlo,nhi; + // reallocate select vector if needed + // this method could be called many times by fix set command + + if (n > maxselect) { + memory->destroy(select); + memory->create(select,n,"set:select"); + maxselect = n; + } if (style == ATOM_SELECT) { - if (atom->tag_enable == 0) - error->all(FLERR, Error::NOLASTLINE, "Cannot use set atom with no atom IDs defined"); - bigint nlobig,nhibig; - utils::bounds(FLERR,id,1,MAXTAGINT,nlobig,nhibig,error); - tagint *tag = atom->tag; for (int i = 0; i < n; i++) if (tag[i] >= nlobig && tag[i] <= nhibig) select[i] = 1; else select[i] = 0; } else if (style == MOL_SELECT) { - if (atom->molecule_flag == 0) - error->all(FLERR, Error::NOLASTLINE, "Cannot use set mol with no molecule IDs defined"); - bigint nlobig,nhibig; - utils::bounds(FLERR,id,1,MAXTAGINT,nlobig,nhibig,error); - tagint *molecule = atom->molecule; for (int i = 0; i < n; i++) if (molecule[i] >= nlobig && molecule[i] <= nhibig) select[i] = 1; else select[i] = 0; } else if (style == TYPE_SELECT) { - utils::bounds_typelabel(FLERR,id,1,atom->ntypes,nlo,nhi,lmp,Atom::ATOM); - int *type = atom->type; for (int i = 0; i < n; i++) if (type[i] >= nlo && type[i] <= nhi) select[i] = 1; else select[i] = 0; } else if (style == GROUP_SELECT) { - int igroup = group->find(id); - if (igroup == -1) error->all(FLERR, Error::NOLASTLINE, "Could not find set group ID {}", id); - int groupbit = group->bitmask[igroup]; - int *mask = atom->mask; for (int i = 0; i < n; i++) if (mask[i] & groupbit) select[i] = 1; else select[i] = 0; } else if (style == REGION_SELECT) { - auto region = domain->get_region_by_id(id); - if (!region) error->all(FLERR, Error::NOLASTLINE, "Set region {} does not exist", id); region->prematch(); - double **x = atom->x; for (int i = 0; i < n; i++) - if (region->match(x[i][0],x[i][1],x[i][2])) - select[i] = 1; + if (region->match(x[i][0],x[i][1],x[i][2])) select[i] = 1; else select[i] = 0; } + + // count_select = count of selected owned atoms + + count_select = 0; + for (int i = 0; i < n; i++) + if (select[i]) count_select++; } /* ---------------------------------------------------------------------- - set owned atom properties directly - either scalar or per-atom values from atom-style variable(s) + loop over list of actions + perform each action on all selected atoms via call to invoke_choice() method ------------------------------------------------------------------------- */ -void Set::set(int keyword) +void Set::invoke_actions() { - // evaluate atom-style variable(s) if necessary + // reallocate per-atom variable storage if needed - vec1 = vec2 = vec3 = vec4 = nullptr; - - if (varflag) { - int nlocal = atom->nlocal; + if (varflag && atom->nlocal > maxvariable) { + maxvariable = atom->nlocal; if (varflag1) { - memory->create(vec1,nlocal,"set:vec1"); - input->variable->compute_atom(ivar1,0,vec1,1,0); + memory->destroy(vec1); + memory->create(vec1,maxvariable,"set:var1"); } if (varflag2) { - memory->create(vec2,nlocal,"set:vec2"); - input->variable->compute_atom(ivar2,0,vec2,1,0); + memory->destroy(vec2); + memory->create(vec2,maxvariable,"set:var2"); } if (varflag3) { - memory->create(vec3,nlocal,"set:vec3"); - input->variable->compute_atom(ivar3,0,vec3,1,0); + memory->destroy(vec3); + memory->create(vec3,maxvariable,"set:var3"); } if (varflag4) { - memory->create(vec4,nlocal,"set:vec4"); - input->variable->compute_atom(ivar4,0,vec4,1,0); + memory->destroy(vec4); + memory->create(vec4,maxvariable,"set:var4"); } } - // check if properties of atoms in rigid bodies are updated - // that are cached as per-body data. - switch (keyword) { - case X: - case Y: - case Z: - case MOLECULE: - case MASS: - case ANGMOM: - case SHAPE: - case DIAMETER: - case DENSITY: - case TEMPERATURE: - case QUAT: - case IMAGE: - if (modify->check_rigid_list_overlap(select)) - error->warning(FLERR,"Changing a property of atoms in rigid bodies " - "that has no effect unless rigid bodies are rebuild"); - break; - default: // assume no conflict for all other properties - break; - } + // loop over actions - // loop over selected atoms + for (int i = 0; i < naction; i++) { - auto avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); - auto avec_line = dynamic_cast(atom->style_match("line")); - auto avec_tri = dynamic_cast(atom->style_match("tri")); - auto avec_body = dynamic_cast(atom->style_match("body")); + Action *action = &actions[i]; - int nlocal = atom->nlocal; - for (int i = 0; i < nlocal; i++) { - if (!select[i]) continue; + // use count_action to optionally override count_select + // if stays -1, count_select is used by caller + // if overwritten by an invoke method, count_action is used + // only a handful of invoke methods tally their own count - // overwrite dvalue, ivalue, xyzw value if variables defined - // else the input script scalar value remains in place + count_action = -1; - if (varflag) { - if (varflag1) { - dvalue = xvalue = vec1[i]; - ivalue = static_cast (dvalue); + // evaluate atom-style variable(s) if necessary + + if (action->varflag) { + if (action->varflag1) { + input->variable->compute_atom(action->ivar1,0,vec1,1,0); } - if (varflag2) yvalue = vec2[i]; - if (varflag3) zvalue = vec3[i]; - if (varflag4) wvalue = vec4[i]; - } - - // set values in per-atom arrays - // error check here in case atom-style variables generated bogus value - - if (keyword == TYPE) { - if (ivalue <= 0 || ivalue > atom->ntypes) - error->one(FLERR, Error::NOLASTLINE, "Invalid value {} in set type command", ivalue); - atom->type[i] = ivalue; - } - else if (keyword == MOLECULE) atom->molecule[i] = ivalue; - else if (keyword == X) atom->x[i][0] = dvalue; - else if (keyword == Y) atom->x[i][1] = dvalue; - else if (keyword == Z) atom->x[i][2] = dvalue; - else if (keyword == VX) atom->v[i][0] = dvalue; - else if (keyword == VY) atom->v[i][1] = dvalue; - else if (keyword == VZ) atom->v[i][2] = dvalue; - else if (keyword == CHARGE) { - atom->q[i] = dvalue; - // ensure that scaled charges are consistent the new charge value - if (atom->epsilon) atom->q_scaled[i] = dvalue / atom->epsilon[i]; - } else if (keyword == MASS) { - if (dvalue <= 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid mass {} in set command", dvalue); - atom->rmass[i] = dvalue; - } - else if (keyword == DIAMETER) { - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid diameter {} in set command", dvalue); - atom->radius[i] = 0.5 * dvalue; - } - else if (keyword == VOLUME) { - if (dvalue <= 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid volume {} in set command", dvalue); - atom->vfrac[i] = dvalue; - } - - else if (keyword == RHEO_STATUS) { - if (ivalue != 0 && ivalue != 1) - error->one(FLERR, Error::NOLASTLINE, "Invalid value {} in set command for rheo/status", - ivalue); - atom->rheo_status[i] = ivalue; - } - - else if (keyword == SPH_E) atom->esph[i] = dvalue; - else if (keyword == SPH_CV) atom->cv[i] = dvalue; - else if (keyword == SPH_RHO) atom->rho[i] = dvalue; - - else if (keyword == EDPD_TEMP) atom->edpd_temp[i] = dvalue; - else if (keyword == EDPD_CV) atom->edpd_cv[i] = dvalue; - else if (keyword == CC) atom->cc[i][cc_index-1] = dvalue; - - else if (keyword == SMD_MASS_DENSITY) { - // set mass from volume and supplied mass density - atom->rmass[i] = atom->vfrac[i] * dvalue; - } - else if (keyword == SMD_CONTACT_RADIUS) atom->contact_radius[i] = dvalue; - - else if (keyword == DPDTHETA) { - if (dvalue >= 0.0) atom->dpdTheta[i] = dvalue; - else { - double onemass; - if (atom->rmass) onemass = atom->rmass[i]; - else onemass = atom->mass[atom->type[i]]; - double vx = atom->v[i][0]; - double vy = atom->v[i][1]; - double vz = atom->v[i][2]; - double tfactor = force->mvv2e / (domain->dimension * force->boltz); - atom->dpdTheta[i] = tfactor * onemass * (vx*vx + vy*vy + vz*vz); + if (action->varflag2) { + input->variable->compute_atom(action->ivar2,0,vec2,1,0); + } + if (action->varflag3) { + input->variable->compute_atom(action->ivar3,0,vec3,1,0); + } + if (action->varflag4) { + input->variable->compute_atom(action->ivar4,0,vec4,1,0); } } - // set shape of ellipsoidal particle + // invoke the action to reset per-atom or per-topology values - else if (keyword == SHAPE) { - if (xvalue < 0.0 || yvalue < 0.0 || zvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid ellipsoid shape {} {} {} in set command", - xvalue, yvalue, zvalue); - if (xvalue > 0.0 || yvalue > 0.0 || zvalue > 0.0) { - if (xvalue == 0.0 || yvalue == 0.0 || zvalue == 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid ellipsoid shape {} {} {} in set command", - xvalue, yvalue, zvalue); - } - avec_ellipsoid->set_shape(i,0.5*xvalue,0.5*yvalue,0.5*zvalue); - } + (this->*invoke_choice[i])(action); - // set length of line particle - - else if (keyword == LENGTH) { - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid length {} in set command", dvalue); - avec_line->set_length(i,dvalue); - } - - // set corners of tri particle - - else if (keyword == TRI) { - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid length {} in set command", dvalue); - avec_tri->set_equilateral(i,dvalue); - } - - // set rmass via density - // if radius > 0.0, treat as sphere or disc - // if shape > 0.0, treat as ellipsoid (or ellipse, when uncomment below) - // if length > 0.0, treat as line - // if area > 0.0, treat as tri - // else set rmass to density directly - - else if (keyword == DENSITY) { - if (dvalue <= 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid density {} in set command", dvalue); - if (atom->radius_flag && atom->radius[i] > 0.0) - if (discflag) - atom->rmass[i] = MY_PI*atom->radius[i]*atom->radius[i] * dvalue; - else - atom->rmass[i] = 4.0*MY_PI/3.0 * - atom->radius[i]*atom->radius[i]*atom->radius[i] * dvalue; - else if (atom->ellipsoid_flag && atom->ellipsoid[i] >= 0) { - double *shape = avec_ellipsoid->bonus[atom->ellipsoid[i]].shape; - // enable 2d ellipse (versus 3d ellipsoid) when time integration - // options (fix nve/asphere, fix nh/asphere) are also implemented - // if (discflag) - // atom->rmass[i] = MY_PI*shape[0]*shape[1] * dvalue; - // else - atom->rmass[i] = 4.0*MY_PI/3.0 * shape[0]*shape[1]*shape[2] * dvalue; - } else if (atom->line_flag && atom->line[i] >= 0) { - double length = avec_line->bonus[atom->line[i]].length; - atom->rmass[i] = length * dvalue; - } else if (atom->tri_flag && atom->tri[i] >= 0) { - double *c1 = avec_tri->bonus[atom->tri[i]].c1; - double *c2 = avec_tri->bonus[atom->tri[i]].c2; - double *c3 = avec_tri->bonus[atom->tri[i]].c3; - double c2mc1[3],c3mc1[3]; - MathExtra::sub3(c2,c1,c2mc1); - MathExtra::sub3(c3,c1,c3mc1); - double norm[3]; - MathExtra::cross3(c2mc1,c3mc1,norm); - double area = 0.5 * MathExtra::len3(norm); - atom->rmass[i] = area * dvalue; - } else atom->rmass[i] = dvalue; - } - - // set dipole moment - - else if (keyword == DIPOLE) { - double **mu = atom->mu; - mu[i][0] = xvalue; - mu[i][1] = yvalue; - mu[i][2] = zvalue; - mu[i][3] = sqrt(mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1] + - mu[i][2]*mu[i][2]); - } - - // set magnetic moments - - else if (keyword == SPIN_ATOM) { - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Incorrect value for atomic spin magnitude: {}", - dvalue); - double **sp = atom->sp; - double inorm = 1.0/sqrt(xvalue*xvalue+yvalue*yvalue+zvalue*zvalue); - sp[i][0] = inorm*xvalue; - sp[i][1] = inorm*yvalue; - sp[i][2] = inorm*zvalue; - sp[i][3] = dvalue; - } - - // set electron radius - - else if (keyword == RADIUS_ELECTRON) { - atom->eradius[i] = dvalue; - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Incorrect value for electron radius: {}", dvalue); - } - - // set electron spin - - else if (keyword == SPIN_ELECTRON) { - if ((dvalue == -1) || (dvalue == 1) || (dvalue == 0) || (dvalue == 2) || (dvalue == 3)) - atom->spin[i] = (int)dvalue; - else - error->one(FLERR, Error::NOLASTLINE, "Incorrect value for electron spin: {}", dvalue); - } - - // set quaternion orientation of ellipsoid or tri or body particle or sphere/bpm - // enforce quat rotation vector in z dir for 2d systems - - else if (keyword == QUAT) { - double *quat = nullptr; - double **quat2 = nullptr; - if (avec_ellipsoid && atom->ellipsoid[i] >= 0) - quat = avec_ellipsoid->bonus[atom->ellipsoid[i]].quat; - else if (avec_tri && atom->tri[i] >= 0) - quat = avec_tri->bonus[atom->tri[i]].quat; - else if (avec_body && atom->body[i] >= 0) - quat = avec_body->bonus[atom->body[i]].quat; - else if (atom->quat_flag) - quat2 = atom->quat; - else - error->one(FLERR, Error::NOLASTLINE, "Cannot set quaternion for atom that has none"); - if (domain->dimension == 2 && (xvalue != 0.0 || yvalue != 0.0)) - error->one(FLERR, Error::NOLASTLINE, - "Cannot set quaternion with xy components for 2d system"); - - const double theta2 = MY_PI2 * wvalue/180.0; - const double sintheta2 = sin(theta2); - double temp[4]; - temp[0] = cos(theta2); - temp[1] = xvalue * sintheta2; - temp[2] = yvalue * sintheta2; - temp[3] = zvalue * sintheta2; - MathExtra::qnormalize(temp); - if (atom->quat_flag) { - quat2[i][0] = temp[0]; - quat2[i][1] = temp[1]; - quat2[i][2] = temp[2]; - quat2[i][3] = temp[3]; - } else { - quat[0] = temp[0]; - quat[1] = temp[1]; - quat[2] = temp[2]; - quat[3] = temp[3]; - } - } - - // set theta of line particle - - else if (keyword == THETA) { - if (atom->line[i] < 0) - error->one(FLERR, Error::NOLASTLINE, "Cannot set theta for atom that is not a line"); - avec_line->bonus[atom->line[i]].theta = dvalue; - } - - // set angmom or omega of particle - - else if (keyword == ANGMOM) { - atom->angmom[i][0] = xvalue; - atom->angmom[i][1] = yvalue; - atom->angmom[i][2] = zvalue; - } - - else if (keyword == OMEGA) { - atom->omega[i][0] = xvalue; - atom->omega[i][1] = yvalue; - atom->omega[i][2] = zvalue; - } - - // set temperature of particle - - else if (keyword == TEMPERATURE) { - if (dvalue < 0.0) - error->one(FLERR, Error::NOLASTLINE, "Invalid temperature {} in set command", dvalue); - atom->temperature[i] = dvalue; - } - - // reset any or all of 3 image flags - - else if (keyword == IMAGE) { - int xbox = (atom->image[i] & IMGMASK) - IMGMAX; - int ybox = (atom->image[i] >> IMGBITS & IMGMASK) - IMGMAX; - int zbox = (atom->image[i] >> IMG2BITS) - IMGMAX; - if (varflag1) ximage = static_cast(xvalue); - if (varflag2) yimage = static_cast(yvalue); - if (varflag3) zimage = static_cast(zvalue); - if (ximageflag) xbox = ximage; - if (yimageflag) ybox = yimage; - if (zimageflag) zbox = zimage; - atom->image[i] = ((imageint) (xbox + IMGMAX) & IMGMASK) | - (((imageint) (ybox + IMGMAX) & IMGMASK) << IMGBITS) | - (((imageint) (zbox + IMGMAX) & IMGMASK) << IMG2BITS); - } - - // set the local dielectric constant - - else if (keyword == EPSILON) { - if (dvalue >= 0.0) { - - // assign the new local dielectric constant - // update both the scaled charge value - - atom->epsilon[i] = dvalue; - atom->q_scaled[i] = atom->q[i] / dvalue; - } - } - - // set value for custom property vector or array - - else if (keyword == IVEC) { - atom->ivector[index_custom][i] = ivalue; - } - - else if (keyword == DVEC) { - atom->dvector[index_custom][i] = dvalue; - } - - else if (keyword == IARRAY) { - atom->iarray[index_custom][i][icol_custom-1] = ivalue; - } - - else if (keyword == DARRAY) { - atom->darray[index_custom][i][icol_custom-1] = dvalue; - } - - count++; + action->count_select = count_select; + action->count_action = count_action; } +} - // update bonus data numbers +/* ---------------------------------------------------------------------- */ - if (keyword == SHAPE) { - bigint nlocal_bonus = avec_ellipsoid->nlocal_bonus; - MPI_Allreduce(&nlocal_bonus,&atom->nellipsoids,1, - MPI_LMP_BIGINT,MPI_SUM,world); +void Set::varparse(const char *name, int m, Action *action) +{ + int ivar = input->variable->find(name+2); + if (ivar < 0) + error->all(FLERR,"Variable name {} for set command does not exist", name); + if (!input->variable->atomstyle(ivar)) + error->all(FLERR,"Variable {} for set command is invalid style", name); + + action->varflag = 1; + + if (m == 1) { + action->varflag1 = 1; action->ivar1 = ivar; + } else if (m == 2) { + action->varflag2 = 1; action->ivar2 = ivar; + } else if (m == 3) { + action->varflag3 = 1; action->ivar3 = ivar; + } else if (m == 4) { + action->varflag4 = 1; action->ivar4 = ivar; } - if (keyword == LENGTH) { - bigint nlocal_bonus = avec_line->nlocal_bonus; - MPI_Allreduce(&nlocal_bonus,&atom->nlines,1,MPI_LMP_BIGINT,MPI_SUM,world); - } - if (keyword == TRI) { - bigint nlocal_bonus = avec_tri->nlocal_bonus; - MPI_Allreduce(&nlocal_bonus,&atom->ntris,1,MPI_LMP_BIGINT,MPI_SUM,world); - } - - // clear up per-atom memory if allocated - - memory->destroy(vec1); - memory->destroy(vec2); - memory->destroy(vec3); - memory->destroy(vec4); } /* ---------------------------------------------------------------------- @@ -1254,7 +608,7 @@ void Set::set(int keyword) make atom result independent of what proc owns it ------------------------------------------------------------------------- */ -void Set::setrandom(int keyword) +void Set::setrandom(int keyword, Action *action) { int i; @@ -1264,7 +618,10 @@ void Set::setrandom(int keyword) auto avec_body = dynamic_cast(atom->style_match("body")); double **x = atom->x; - int seed = ivalue; + + // seed is always set to ivalue1 in process() methods + + int seed = action->ivalue1; auto ranpark = new RanPark(lmp,1); auto ranmars = new RanMars(lmp,seed + comm->me); @@ -1273,6 +630,9 @@ void Set::setrandom(int keyword) if (keyword == TYPE_FRACTION) { int nlocal = atom->nlocal; + double fraction = action->dvalue1; + int newtype = action->ivalue2; + int count = 0; for (i = 0; i < nlocal; i++) if (select[i]) { @@ -1282,44 +642,45 @@ void Set::setrandom(int keyword) count++; } + count_action = count; + // set exact count of atom types to newtype // for TYPE_RATIO, exact = fraction out of total eligible // for TYPE_SUBSET, exact = nsubset out of total eligible } else if (keyword == TYPE_RATIO || keyword == TYPE_SUBSET) { int nlocal = atom->nlocal; + int newtype = action->ivalue2; - // count = number of eligible atoms I own + // convert specified fraction to nsubset of all selected atoms - count = 0; - for (i = 0; i < nlocal; i++) - if (select[i]) count++; - - // convert specified fraction to nsubset - - bigint bcount = count; + bigint bcount = count_select; bigint allcount; MPI_Allreduce(&bcount,&allcount,1,MPI_LMP_BIGINT,MPI_SUM,world); + bigint nsubset; if (keyword == TYPE_RATIO) { + double fraction = action->dvalue1; nsubset = static_cast (fraction * allcount); } else if (keyword == TYPE_SUBSET) { + nsubset = action->bvalue1; if (nsubset > allcount) - error->all(FLERR, Error::NOLASTLINE, "Set type/subset value exceeds eligible atoms"); + error->all(FLERR,"Set type/subset value exceeds eligible atoms"); } // make selection - int *flag = memory->create(flag,count,"set:flag"); - int *work = memory->create(work,count,"set:work"); + int *flag = memory->create(flag,count_select,"set:flag"); + int *work = memory->create(work,count_select,"set:work"); - ranmars->select_subset(nsubset,count,flag,work); + ranmars->select_subset(nsubset,count_select,flag,work); // change types of selected atoms // flag vector from select_subset() is only for eligible atoms - count = 0; + int count = 0; int eligible = 0; + for (i = 0; i < nlocal; i++) { if (!select[i]) continue; if (flag[eligible]) { @@ -1329,6 +690,8 @@ void Set::setrandom(int keyword) eligible++; } + count_action = count; + // clean up memory->destroy(flag); @@ -1340,7 +703,7 @@ void Set::setrandom(int keyword) } else if (keyword == DIPOLE_RANDOM) { double **mu = atom->mu; int nlocal = atom->nlocal; - + double dmag = action->dvalue1; double msq,scale; if (domain->dimension == 3) { @@ -1351,12 +714,11 @@ void Set::setrandom(int keyword) mu[i][1] = ranpark->uniform() - 0.5; mu[i][2] = ranpark->uniform() - 0.5; msq = mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1] + mu[i][2]*mu[i][2]; - scale = dvalue/sqrt(msq); + scale = dmag/sqrt(msq); mu[i][0] *= scale; mu[i][1] *= scale; mu[i][2] *= scale; - mu[i][3] = dvalue; - count++; + mu[i][3] = dmag; } } else { @@ -1367,22 +729,20 @@ void Set::setrandom(int keyword) mu[i][1] = ranpark->uniform() - 0.5; mu[i][2] = 0.0; msq = mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1]; - scale = dvalue/sqrt(msq); + scale = dmag/sqrt(msq); mu[i][0] *= scale; mu[i][1] *= scale; - mu[i][3] = dvalue; - count++; + mu[i][3] = dmag; } } - // set spin moments to random orientations in 3d or 2d // spin length is fixed to unity - } else if (keyword == SPIN_RANDOM) { + } else if (keyword == SPIN_ATOM_RANDOM) { double **sp = atom->sp; int nlocal = atom->nlocal; - + double dlen = action->dvalue1; double sp_sq,scale; if (domain->dimension == 3) { @@ -1397,8 +757,7 @@ void Set::setrandom(int keyword) sp[i][0] *= scale; sp[i][1] *= scale; sp[i][2] *= scale; - sp[i][3] = dvalue; - count++; + sp[i][3] = dlen; } } else { @@ -1412,32 +771,35 @@ void Set::setrandom(int keyword) scale = 1.0/sqrt(sp_sq); sp[i][0] *= scale; sp[i][1] *= scale; - sp[i][3] = dvalue; - count++; + sp[i][3] = dlen; } } // set quaternions to random orientations in 3d and 2d } else if (keyword == QUAT_RANDOM) { + int *ellipsoid = atom->ellipsoid; + int *tri = atom->tri; + int *body = atom->body; + double **quat = atom->quat; int nlocal = atom->nlocal; - double *quat; - double **quat2; + int quat_flag = atom->quat_flag; + double *quat_one; if (domain->dimension == 3) { double s,t1,t2,theta1,theta2; for (i = 0; i < nlocal; i++) if (select[i]) { - if (avec_ellipsoid && atom->ellipsoid[i] >= 0) - quat = avec_ellipsoid->bonus[atom->ellipsoid[i]].quat; - else if (avec_tri && atom->tri[i] >= 0) - quat = avec_tri->bonus[atom->tri[i]].quat; - else if (avec_body && atom->body[i] >= 0) - quat = avec_body->bonus[atom->body[i]].quat; - else if (atom->quat_flag) - quat2 = atom->quat; + if (avec_ellipsoid && ellipsoid[i] >= 0) + quat_one = avec_ellipsoid->bonus[ellipsoid[i]].quat; + else if (avec_tri && tri[i] >= 0) + quat_one = avec_tri->bonus[tri[i]].quat; + else if (avec_body && body[i] >= 0) + quat_one = avec_body->bonus[body[i]].quat; + else if (quat_flag) + quat_one = quat[i]; else - error->one(FLERR, Error::NOLASTLINE, "Cannot set quaternion for atom that has none"); + error->one(FLERR,"Cannot set quaternion for atom that has none"); ranpark->reset(seed,x[i]); s = ranpark->uniform(); @@ -1445,61 +807,46 @@ void Set::setrandom(int keyword) t2 = sqrt(s); theta1 = 2.0*MY_PI*ranpark->uniform(); theta2 = 2.0*MY_PI*ranpark->uniform(); - if (atom->quat_flag) { - quat2[i][0] = cos(theta2)*t2; - quat2[i][1] = sin(theta1)*t1; - quat2[i][2] = cos(theta1)*t1; - quat2[i][3] = sin(theta2)*t2; - } else { - quat[0] = cos(theta2)*t2; - quat[1] = sin(theta1)*t1; - quat[2] = cos(theta1)*t1; - quat[3] = sin(theta2)*t2; - } - count++; + quat_one[0] = cos(theta2)*t2; + quat_one[1] = sin(theta1)*t1; + quat_one[2] = cos(theta1)*t1; + quat_one[3] = sin(theta2)*t2; } } else { double theta2; for (i = 0; i < nlocal; i++) if (select[i]) { - if (avec_ellipsoid && atom->ellipsoid[i] >= 0) - quat = avec_ellipsoid->bonus[atom->ellipsoid[i]].quat; - else if (avec_body && atom->body[i] >= 0) - quat = avec_body->bonus[atom->body[i]].quat; - else if (atom->quat_flag) - quat2 = atom->quat; + if (avec_ellipsoid && ellipsoid[i] >= 0) + quat_one = avec_ellipsoid->bonus[ellipsoid[i]].quat; + else if (avec_body && body[i] >= 0) + quat_one = avec_body->bonus[body[i]].quat; + else if (quat_flag) + quat_one = quat[i]; else - error->one(FLERR, Error::NOLASTLINE, "Cannot set quaternion for atom that has none"); + error->one(FLERR,"Cannot set quaternion for atom that has none"); ranpark->reset(seed,x[i]); theta2 = MY_PI*ranpark->uniform(); - if (atom->quat_flag) { - quat2[i][0] = cos(theta2); - quat2[i][1] = 0.0; - quat2[i][2] = 0.0; - quat2[i][3] = sin(theta2); - } else { - quat[0] = cos(theta2); - quat[1] = 0.0; - quat[2] = 0.0; - quat[3] = sin(theta2); - } - count++; + quat_one[0] = cos(theta2); + quat_one[1] = 0.0; + quat_one[2] = 0.0; + quat_one[3] = sin(theta2); } } // set theta to random orientation in 2d } else if (keyword == THETA_RANDOM) { + int *line = atom->line; int nlocal = atom->nlocal; + for (i = 0; i < nlocal; i++) { if (select[i]) { - if (atom->line[i] < 0) - error->one(FLERR, Error::NOLASTLINE, "Cannot set theta for atom that is not a line"); + if (line[i] < 0) + error->one(FLERR,"Cannot set theta for atom that is not a line"); ranpark->reset(seed,x[i]); - avec_line->bonus[atom->line[i]].theta = MY_2PI*ranpark->uniform(); - count++; + avec_line->bonus[line[i]].theta = MY_2PI*ranpark->uniform(); } } } @@ -1510,14 +857,14 @@ void Set::setrandom(int keyword) /* ---------------------------------------------------------------------- */ -void Set::topology(int keyword) +void Set::topology(int keyword, Action *action) { int m,atom1,atom2,atom3,atom4; // error check if (atom->molecular == Atom::TEMPLATE) - error->all(FLERR, Error::NOLASTLINE, "Cannot set bond topology types for atom style template"); + error->all(FLERR,"Cannot set bond topology types for atom style template"); // border swap to acquire ghost atom info // enforce PBC before in case atoms are outside box @@ -1539,18 +886,23 @@ void Set::topology(int keyword) selection(atom->nlocal + atom->nghost); + int count = 0; + // for BOND, each of 2 atoms must be in group if (keyword == BOND) { + int *num_bond = atom->num_bond; + int **bond_type = atom->bond_type; + tagint **bond_atom = atom->bond_atom; int nlocal = atom->nlocal; + + int itype = action->ivalue1; for (int i = 0; i < nlocal; i++) - for (m = 0; m < atom->num_bond[i]; m++) { - atom1 = atom->map(atom->bond_atom[i][m]); - if (atom1 == -1) - error->one(FLERR, Error::NOLASTLINE, "Bond atom missing in set command" - + utils::errorurl(5)); + for (m = 0; m < num_bond[i]; m++) { + atom1 = atom->map(bond_atom[i][m]); + if (atom1 == -1) error->one(FLERR,"Bond atom missing in set command"); if (select[i] && select[atom1]) { - atom->bond_type[i][m] = ivalue; + bond_type[i][m] = itype; count++; } } @@ -1559,17 +911,23 @@ void Set::topology(int keyword) // for ANGLE, each of 3 atoms must be in group if (keyword == ANGLE) { + int *num_angle = atom->num_angle; + int **angle_type = atom->angle_type; + tagint **angle_atom1 = atom->angle_atom1; + tagint **angle_atom2 = atom->angle_atom2; + tagint **angle_atom3 = atom->angle_atom3; int nlocal = atom->nlocal; + + int itype = action->ivalue1; for (int i = 0; i < nlocal; i++) - for (m = 0; m < atom->num_angle[i]; m++) { - atom1 = atom->map(atom->angle_atom1[i][m]); - atom2 = atom->map(atom->angle_atom2[i][m]); - atom3 = atom->map(atom->angle_atom3[i][m]); + for (m = 0; m < num_angle[i]; m++) { + atom1 = atom->map(angle_atom1[i][m]); + atom2 = atom->map(angle_atom2[i][m]); + atom3 = atom->map(angle_atom3[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1) - error->one(FLERR, Error::NOLASTLINE, "Angle atom missing in set command" - + utils::errorurl(5)); + error->one(FLERR,"Angle atom missing in set command"); if (select[atom1] && select[atom2] && select[atom3]) { - atom->angle_type[i][m] = ivalue; + angle_type[i][m] = itype; count++; } } @@ -1578,18 +936,25 @@ void Set::topology(int keyword) // for DIHEDRAL, each of 4 atoms must be in group if (keyword == DIHEDRAL) { + int *num_dihedral = atom->num_dihedral; + int **dihedral_type = atom->dihedral_type; + tagint **dihedral_atom1 = atom->dihedral_atom1; + tagint **dihedral_atom2 = atom->dihedral_atom2; + tagint **dihedral_atom3 = atom->dihedral_atom3; + tagint **dihedral_atom4 = atom->dihedral_atom4; int nlocal = atom->nlocal; + + int itype = action->ivalue1; for (int i = 0; i < nlocal; i++) - for (m = 0; m < atom->num_dihedral[i]; m++) { - atom1 = atom->map(atom->dihedral_atom1[i][m]); - atom2 = atom->map(atom->dihedral_atom2[i][m]); - atom3 = atom->map(atom->dihedral_atom3[i][m]); - atom4 = atom->map(atom->dihedral_atom4[i][m]); + for (m = 0; m < num_dihedral[i]; m++) { + atom1 = atom->map(dihedral_atom1[i][m]); + atom2 = atom->map(dihedral_atom2[i][m]); + atom3 = atom->map(dihedral_atom3[i][m]); + atom4 = atom->map(dihedral_atom4[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) - error->one(FLERR, Error::NOLASTLINE, "Dihedral atom missing in set command" - + utils::errorurl(5)); + error->one(FLERR,"Dihedral atom missing in set command"); if (select[atom1] && select[atom2] && select[atom3] && select[atom4]) { - atom->dihedral_type[i][m] = ivalue; + dihedral_type[i][m] = itype; count++; } } @@ -1598,43 +963,1976 @@ void Set::topology(int keyword) // for IMPROPER, each of 4 atoms must be in group if (keyword == IMPROPER) { + int *num_improper = atom->num_improper; + int **improper_type = atom->improper_type; + tagint **improper_atom1 = atom->improper_atom1; + tagint **improper_atom2 = atom->improper_atom2; + tagint **improper_atom3 = atom->improper_atom3; + tagint **improper_atom4 = atom->improper_atom4; int nlocal = atom->nlocal; + + int itype = action->ivalue1; for (int i = 0; i < nlocal; i++) - for (m = 0; m < atom->num_improper[i]; m++) { - atom1 = atom->map(atom->improper_atom1[i][m]); - atom2 = atom->map(atom->improper_atom2[i][m]); - atom3 = atom->map(atom->improper_atom3[i][m]); - atom4 = atom->map(atom->improper_atom4[i][m]); + for (m = 0; m < num_improper[i]; m++) { + atom1 = atom->map(improper_atom1[i][m]); + atom2 = atom->map(improper_atom2[i][m]); + atom3 = atom->map(improper_atom3[i][m]); + atom4 = atom->map(improper_atom4[i][m]); if (atom1 == -1 || atom2 == -1 || atom3 == -1 || atom4 == -1) - error->one(FLERR, Error::NOLASTLINE, "Improper atom missing in set command" - + utils::errorurl(5)); + error->one(FLERR,"Improper atom missing in set command"); if (select[atom1] && select[atom2] && select[atom3] && select[atom4]) { - atom->improper_type[i][m] = ivalue; + improper_type[i][m] = itype; count++; } } } + + // set count_action for all topology actions + + count_action = count; +} + +// ---------------------------------------------------------------------- +// pairs of process/invoke methods for each keyword +// process method reads args, stores parameters in Action instance +// invoke method resets atoms properties using Action instance +// separate two operations so can be called by either set or fix set command +// ---------------------------------------------------------------------- + +/* ---------------------------------------------------------------------- */ + +void Set::process_angle(int &iarg, int narg, char **arg, Action *action) +{ + if (atom->avec->angles_allow == 0) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set angle", error); + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::ANGLE,lmp); + action->ivalue1 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue1 <= 0 || action->ivalue1 > atom->nangletypes) + error->all(FLERR,"Invalid angle type in set command"); + iarg += 2; +} + +void Set::invoke_angle(Action *action) +{ + topology(ANGLE,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_angmom(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->angmom_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set angmom", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + + iarg += 4; +} + +void Set::invoke_angmom(Action *action) +{ + int nlocal = atom->nlocal; + double **angmom = atom->angmom; + + int varflag = action->varflag; + double xvalue,yvalue,zvalue; + if (!action->varflag1) xvalue = action->dvalue1; + if (!action->varflag2) yvalue = action->dvalue2; + if (!action->varflag3) zvalue = action->dvalue3; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) xvalue = vec1[i]; + if (action->varflag1) yvalue = vec2[i]; + if (action->varflag1) zvalue = vec3[i]; + } + + angmom[i][0] = xvalue; + angmom[i][1] = yvalue; + angmom[i][2] = zvalue; + } } /* ---------------------------------------------------------------------- */ -void Set::varparse(const char *name, int m) +void Set::process_bond(int &iarg, int narg, char **arg, Action *action) { - varflag = 1; - int ivar = input->variable->find(name+2); + if (atom->avec->bonds_allow == 0) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set bond", error); - if (ivar < 0) - error->all(FLERR, Error::NOLASTLINE, "Variable name {} for set command does not exist", name); - if (!input->variable->atomstyle(ivar)) - error->all(FLERR, Error::NOLASTLINE, "Variable {} for set command is invalid style", name); + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::BOND,lmp); + action->ivalue1 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue1 <= 0 || action->ivalue1 > atom->nbondtypes) + error->all(FLERR,"Invalid bond type in set command"); - if (m == 1) { - varflag1 = 1; ivar1 = ivar; - } else if (m == 2) { - varflag2 = 1; ivar2 = ivar; - } else if (m == 3) { - varflag3 = 1; ivar3 = ivar; - } else if (m == 4) { - varflag4 = 1; ivar4 = ivar; + iarg += 2; +} + +void Set::invoke_bond(Action *action) +{ + topology(BOND,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_cc(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->tdpd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "set cc", error); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + if (action->ivalue1 < 1) error->all(FLERR,"Invalid cc index in set command"); + + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (action->dvalue1 < 0.0) error->all(FLERR,"Invalid cc value in set command"); + } + + iarg += 3; +} + +void Set::invoke_cc(Action *action) +{ + int nlocal = atom->nlocal; + double **cc = atom->cc; + + int cc_index = action->ivalue1 - 1; + // NOTE: need to check if cc_index exceeds cc array allocation + + int varflag = action->varflag; + double ccvalue; + if (!action->varflag1) ccvalue = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + ccvalue = vec1[i]; + if (ccvalue < 0.0) error->all(FLERR,"Invalid cc value in set command"); + } + + cc[i][cc_index] = ccvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_charge(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->q_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set charge", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_charge(Action *action) +{ + int nlocal = atom->nlocal; + double *q = atom->q; + double *q_scaled = atom->q_scaled; + double *epsilon = atom->epsilon; + + int varflag = action->varflag; + double qvalue; + if (!action->varflag1) qvalue = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) qvalue = vec1[i]; + q[i] = qvalue; + + // ensure scaled charges are consistent with new charge value + + if (epsilon) q_scaled[i] = qvalue / epsilon[i]; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_density(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->rmass_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set density", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) error->all(FLERR,"Invalid density in set command"); + } + + action->ivalue1 = 0; + if (strcmp(arg[iarg],"density/disc") == 0) { + action->ivalue1 = 1; + if (domain->dimension != 2) error->all(FLERR,"Set density/disc requires 2d simulation"); + } + + iarg += 2; +} + +void Set::invoke_density(Action *action) +{ + int nlocal = atom->nlocal; + double *rmass = atom->rmass; + double *radius = atom->radius; + int *ellipsoid = atom->ellipsoid; + int *line = atom->line; + int *tri = atom->tri; + + int radius_flag = atom->radius_flag; + int ellipsoid_flag = atom->ellipsoid_flag; + int line_flag = atom->line_flag; + int tri_flag = atom->tri_flag; + + auto avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); + auto avec_line = dynamic_cast(atom->style_match("line")); + auto avec_tri = dynamic_cast(atom->style_match("tri")); + + int varflag = action->varflag; + double density; + if (!action->varflag1) density = action->dvalue1; + int discflag = action->ivalue1; + + // set rmass via density + // if radius > 0.0, treat as sphere or disc + // if shape > 0.0, treat as ellipsoid (or ellipse, when uncomment below) + // if length > 0.0, treat as line + // if area > 0.0, treat as tri + // else set rmass to density directly + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + density = vec1[i]; + if (density <= 0.0) error->one(FLERR,"Invalid density in set command"); + } + + if (radius_flag && radius[i] > 0.0) + if (discflag) rmass[i] = MY_PI*radius[i]*radius[i] * density; + else rmass[i] = 4.0*MY_PI/3.0 * radius[i]*radius[i]*radius[i] * density; + + else if (ellipsoid_flag && ellipsoid[i] >= 0) { + double *shape = avec_ellipsoid->bonus[ellipsoid[i]].shape; + // could enable 2d ellipse (versus 3d ellipsoid) when time integration + // options (fix nve/asphere, fix nh/asphere) are also implemented + // if (discflag) + // atom->rmass[i] = MY_PI*shape[0]*shape[1] * dvalue; + // else + rmass[i] = 4.0*MY_PI/3.0 * shape[0]*shape[1]*shape[2] * density; + + } else if (line_flag && line[i] >= 0) { + double length = avec_line->bonus[line[i]].length; + rmass[i] = length * density; + + } else if (tri_flag && tri[i] >= 0) { + double *c1 = avec_tri->bonus[tri[i]].c1; + double *c2 = avec_tri->bonus[tri[i]].c2; + double *c3 = avec_tri->bonus[tri[i]].c3; + double c2mc1[3],c3mc1[3]; + MathExtra::sub3(c2,c1,c2mc1); + MathExtra::sub3(c3,c1,c3mc1); + double norm[3]; + MathExtra::cross3(c2mc1,c3mc1,norm); + double area = 0.5 * MathExtra::len3(norm); + rmass[i] = area * density; + + } else rmass[i] = density; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_diameter(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->radius_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set diameter", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid diameter in set command"); + } + + iarg += 2; +} + +void Set::invoke_diameter(Action *action) +{ + int nlocal = atom->nlocal; + double *radius = atom->radius; + + int varflag = action->varflag; + double diam; + if (!action->varflag1) diam = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + diam = vec1[i]; + if (diam < 0.0) error->one(FLERR,"Invalid diameter in set command"); + } + + radius[i] = 0.5 * diam; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_dihedral(int &iarg, int narg, char **arg, Action *action) +{ + if (atom->avec->dihedrals_allow == 0) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set dihedral", error); + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::DIHEDRAL,lmp); + action->ivalue1 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue1 <= 0 || action->ivalue1 > atom->ndihedraltypes) + error->all(FLERR,"Invalid dihedral type in set command"); + + iarg += 2; +} + +void Set::invoke_dihedral(Action *action) +{ + topology(DIHEDRAL,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_dipole(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->mu_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set dipole", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + + iarg += 4; +} + +void Set::invoke_dipole(Action *action) +{ + int nlocal = atom->nlocal; + double **mu = atom->mu; + + int varflag = action->varflag; + double xvalue,yvalue,zvalue; + if (!action->varflag1) xvalue = action->dvalue1; + if (!action->varflag2) yvalue = action->dvalue2; + if (!action->varflag3) zvalue = action->dvalue3; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) xvalue = vec1[i]; + if (action->varflag1) yvalue = vec2[i]; + if (action->varflag1) zvalue = vec3[i]; + } + + mu[i][0] = xvalue; + mu[i][1] = yvalue; + mu[i][2] = zvalue; + mu[i][3] = sqrt(mu[i][0]*mu[i][0] + mu[i][1]*mu[i][1] + mu[i][2]*mu[i][2]); + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_dipole_random(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->mu_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "set dipole/random", error); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + action->dvalue1 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (action->ivalue1 <= 0) + error->all(FLERR,"Invalid random number seed in set command"); + if (action->dvalue1 <= 0.0) + error->all(FLERR,"Invalid dipole length in set command"); + + iarg += 3; +} + +void Set::invoke_dipole_random(Action *action) +{ + setrandom(DIPOLE_RANDOM,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_dpd_theta(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->dpd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set dpd/theta", error); + + if (strcmp(arg[iarg+1],"NULL") == 0) action->dvalue1 = -1.0; + else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->all(FLERR,"Invalid dpd/theta value in set command"); + } + + iarg += 2; +} + +void Set::invoke_dpd_theta(Action *action) +{ + int nlocal = atom->nlocal; + int *type = atom->type; + double **v = atom->v; + double *mass = atom->mass; + double *rmass = atom->rmass; + double *dpdTheta = atom->dpdTheta; + + double tfactor = force->mvv2e / (domain->dimension * force->boltz); + double onemass; + double vx,vy,vz; + + int varflag = action->varflag; + double theta; + if (!action->varflag1) theta = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + theta = vec1[i]; + if (theta < 0.0) error->one(FLERR,"Invalid dpd/theta value in set command"); + } + + // if theta is negative, NULL was used, set dpdTheta to KE of particle + + if (theta >= 0.0) dpdTheta[i] = theta; + else { + if (rmass) onemass = rmass[i]; + else onemass = mass[type[i]]; + vx = v[i][0]; + vy = v[i][1]; + vz = v[i][2]; + dpdTheta[i] = tfactor * onemass * (vx*vx + vy*vy + vz*vz); + } + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_edpd_cv(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->edpd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set edpd/cv", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->all(FLERR,"Invalid edpd/cv value in set command"); + } + + iarg += 2; +} + +void Set::invoke_edpd_cv(Action *action) +{ + int nlocal = atom->nlocal; + double *edpd_cv = atom->edpd_cv; + + int varflag = action->varflag; + double cv; + if (!action->varflag1) cv = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + cv = vec1[i]; + if (cv < 0.0) error->one(FLERR,"Invalid edpd/cv value in set command"); + } + + edpd_cv[i] = cv; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_edpd_temp(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->edpd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set edpd/temp", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->all(FLERR,"Invalid edpd/temp value in set command"); + } + iarg += 2; +} + +void Set::invoke_edpd_temp(Action *action) +{ + int nlocal = atom->nlocal; + double *edpd_temp = atom->edpd_temp; + + int varflag = action->varflag; + double temp; + if (!action->varflag1) temp = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + temp = vec1[i]; + if (temp < 0.0) error->one(FLERR,"Invalid edpd/temp value in set command"); + } + + edpd_temp[i] = temp; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_epsilon(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->dielectric_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set epsilon", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) error->all(FLERR,"Invalid epsilon in set command"); + } + + iarg += 2; +} + +void Set::invoke_epsilon(Action *action) +{ + int nlocal = atom->nlocal; + double *epsilon = atom->epsilon; + double *q = atom->q; + double *q_scaled = atom->q_scaled; + + int varflag = action->varflag; + double eps; + if (!action->varflag1) eps = action->dvalue1; + + // assign local dielectric constant + // also update scaled charge value + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + eps = vec1[i]; + if (eps <= 0.0) error->one(FLERR,"Invalid epsilon in set command"); + } + + epsilon[i] = eps; + q_scaled[i] = q[i] / eps; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_image(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set image", error); + + if (strcmp(arg[iarg+1],"NULL") == 0) action->ivalue4 = 0; + else { + action->ivalue4 = 1; + if (utils::strmatch(arg[iarg+1],"^v_")) { + if (!domain->xperiodic) + error->all(FLERR,"Cannot set variable image flag for non-periodic dimension"); + varparse(arg[iarg+1],1,action); + } else { + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + if (action->ivalue1 && !domain->xperiodic) + error->all(FLERR,"Cannot set non-zero image flag for non-periodic dimension"); + } + } + + if (strcmp(arg[iarg+2],"NULL") == 0) action->ivalue5 = 0; + else { + action->ivalue5 = 1; + if (utils::strmatch(arg[iarg+2],"^v_")) { + if (!domain->yperiodic) + error->all(FLERR,"Cannot set variable image flag for non-periodic dimension"); + varparse(arg[iarg+2],2,action); + } else { + action->ivalue2 = utils::inumeric(FLERR,arg[iarg+2],false,lmp); + if (action->ivalue2 && !domain->yperiodic) + error->all(FLERR,"Cannot set non-zero image flag for non-periodic dimension"); + } + } + + if (strcmp(arg[iarg+1],"NULL") == 0) action->ivalue6 = 0; + else { + action->ivalue6 = 1; + if (utils::strmatch(arg[iarg+3],"^v_")) { + if (!domain->zperiodic) + error->all(FLERR,"Cannot set variable image flag for non-periodic dimension"); + varparse(arg[iarg+3],3,action); + } else { + action->ivalue3 = utils::inumeric(FLERR,arg[iarg+3],false,lmp); + if (action->ivalue3 && !domain->zperiodic) + error->all(FLERR,"Cannot set non-zero image flag for non-periodic dimension"); + } + } + + iarg += 4; +} + +void Set::invoke_image(Action *action) +{ + int nlocal = atom->nlocal; + imageint *image = atom->image; + int xbox,ybox,zbox; + + int ximageflag = action->ivalue4; + int yimageflag = action->ivalue5; + int zimageflag = action->ivalue6; + + int varflag = action->varflag; + int ximage,yimage,zimage; + if (!action->varflag1) ximage = action->ivalue1; + if (!action->varflag2) yimage = action->ivalue2; + if (!action->varflag3) zimage = action->ivalue3; + + // reset any or all of 3 image flags + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) ximage = static_cast (vec1[i]); + if (action->varflag2) yimage = static_cast (vec2[i]); + if (action->varflag3) zimage = static_cast (vec3[i]); + } + + xbox = (image[i] & IMGMASK) - IMGMAX; + ybox = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; + zbox = (image[i] >> IMG2BITS) - IMGMAX; + if (ximageflag) xbox = ximage; + if (yimageflag) ybox = yimage; + if (zimageflag) zbox = zimage; + image[i] = ((imageint) (xbox + IMGMAX) & IMGMASK) | + (((imageint) (ybox + IMGMAX) & IMGMASK) << IMGBITS) | + (((imageint) (zbox + IMGMAX) & IMGMASK) << IMG2BITS); + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_improper(int &iarg, int narg, char **arg, Action *action) +{ + if (atom->avec->impropers_allow == 0) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set improper", error); + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::IMPROPER,lmp); + action->ivalue1 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue1 <= 0 || action->ivalue1 > atom->nimpropertypes) + error->all(FLERR,"Invalid value in set command"); + + iarg += 2; +} + +void Set::invoke_improper(Action *action) +{ + topology(IMPROPER,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_length(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->line_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set length", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid length in set command"); + } + + iarg += 2; +} + +void Set::invoke_length(Action *action) +{ + int nlocal = atom->nlocal; + auto avec_line = dynamic_cast(atom->style_match("line")); + + int varflag = action->varflag; + double length; + if (!action->varflag1) length = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + length = vec1[i]; + if (length < 0.0) error->one(FLERR,"Invalid length in set command"); + } + + avec_line->set_length(i,length); + } + + // update global line count + + bigint nlocal_bonus = avec_line->nlocal_bonus; + MPI_Allreduce(&nlocal_bonus,&atom->nlines,1,MPI_LMP_BIGINT,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_mass(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->rmass_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set mass", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) error->one(FLERR,"Invalid mass in set command"); + } + + iarg += 2; +} + +void Set::invoke_mass(Action *action) +{ + int nlocal = atom->nlocal; + double *rmass = atom->rmass; + + int varflag = action->varflag; + double mass_one; + if (!action->varflag1) mass_one = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + mass_one = vec1[i]; + if (mass_one < 0.0) error->one(FLERR,"Invalid mass in set command"); + } + + rmass[i] = mass_one; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_mol(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->molecule_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set mol", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->tvalue1 = utils::tnumeric(FLERR,arg[iarg+1],false,lmp); + if (action->tvalue1 < 0) error->one(FLERR,"Invalid molecule ID in set command"); + } + + iarg += 2; +} + +void Set::invoke_mol(Action *action) +{ + int nlocal = atom->nlocal; + tagint *molecule = atom->molecule; + + int varflag = action->varflag; + tagint molID; + if (!action->varflag1) molID = action->tvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + molID = vec1[i]; + if (molID < 0) error->one(FLERR,"Invalid molecule ID in set command"); + } + + molecule[i] = molID; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_omega(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->omega_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set omega", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + + iarg += 4; +} + +void Set::invoke_omega(Action *action) +{ + int nlocal = atom->nlocal; + double **omega = atom->omega; + + int varflag = action->varflag; + double xvalue,yvalue,zvalue; + if (!action->varflag1) xvalue = action->dvalue1; + if (!action->varflag2) yvalue = action->dvalue2; + if (!action->varflag3) zvalue = action->dvalue3; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) xvalue = vec1[i]; + if (action->varflag2) yvalue = vec2[i]; + if (action->varflag3) zvalue = vec3[i]; + } + + omega[i][0] = xvalue; + omega[i][1] = yvalue; + omega[i][2] = zvalue; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_quat(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->ellipsoid_flag && !atom->tri_flag && !atom->body_flag && !atom->quat_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+5 > narg) utils::missing_cmd_args(FLERR, "set quat", error); + int dimension = domain->dimension; + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (dimension == 2 && action->dvalue1 != 0.0) + error->one(FLERR,"Cannot set quaternion with xy components for 2d system"); + } + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else { + action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (dimension == 2 && action->dvalue2 != 0.0) + error->one(FLERR,"Cannot set quaternion with xy components for 2d system"); + } + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + if (utils::strmatch(arg[iarg+4],"^v_")) varparse(arg[iarg+4],4,action); + else action->dvalue4 = utils::numeric(FLERR,arg[iarg+4],false,lmp); + + iarg += 5; +} + +void Set::invoke_quat(Action *action) +{ + int nlocal = atom->nlocal; + int *ellipsoid = atom->ellipsoid; + int *tri = atom->tri; + int *body = atom->body; + double **quat = atom->quat; + int quat_flag = atom->quat_flag; + + auto avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); + auto avec_tri = dynamic_cast(atom->style_match("tri")); + auto avec_body = dynamic_cast(atom->style_match("body")); + + int dimension = domain->dimension; + double radians,sintheta; + double *quat_one; + + int varflag = action->varflag; + double xvalue,yvalue,zvalue,theta; + if (!action->varflag1) xvalue = action->dvalue1; + if (!action->varflag2) yvalue = action->dvalue2; + if (!action->varflag3) zvalue = action->dvalue3; + if (!action->varflag4) theta = action->dvalue4; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (avec_ellipsoid && ellipsoid[i] >= 0) + quat_one = avec_ellipsoid->bonus[ellipsoid[i]].quat; + else if (avec_tri && tri[i] >= 0) + quat_one = avec_tri->bonus[tri[i]].quat; + else if (avec_body && body[i] >= 0) + quat_one = avec_body->bonus[body[i]].quat; + else if (quat_flag) + quat_one = quat[i]; + else + error->one(FLERR,"Cannot set quaternion for atom that has none"); + + if (varflag) { + if (action->varflag1) xvalue = vec1[i]; + if (action->varflag2) yvalue = vec2[i]; + if (action->varflag3) zvalue = vec3[i]; + if (action->varflag4) theta = vec4[i]; + if (dimension == 2 && (xvalue != 0.0 || yvalue != 0.0)) + error->one(FLERR,"Cannot set quaternion with xy components for 2d system"); + } + + radians = MY_PI2 * theta/180.0; + sintheta = sin(radians); + quat_one[0] = cos(radians); + quat_one[1] = xvalue * sintheta; + quat_one[2] = yvalue * sintheta; + quat_one[3] = zvalue * sintheta; + MathExtra::qnormalize(quat_one); + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_quat_random(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->ellipsoid_flag && !atom->tri_flag && !atom->body_flag && !atom->quat_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set quat/random", error); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + if (action->ivalue1 <= 0) error->all(FLERR,"Invalid random number seed in set command"); + + iarg += 2; +} + +void Set::invoke_quat_random(Action *action) +{ + setrandom(QUAT_RANDOM,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_radius_election(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->eradius_flag) + error->all(FLERR, "Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "set radius/electron", error); + + if (utils::strmatch(arg[iarg + 1], "^v_")) + varparse(arg[iarg + 1], 1, action); + else { + action->dvalue1 = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + if (action->dvalue1 < 0.0) + error->one(FLERR, iarg + 1, "Invalid electron radius {} in set radius/electron command", + action->dvalue1); + } + + iarg += 2; +} + +void Set::invoke_radius_election(Action *action) +{ + int nlocal = atom->nlocal; + double *eradius = atom->eradius; + + int varflag = action->varflag; + double radius; + if (!action->varflag1) radius = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + radius = vec1[i]; + if (radius < 0.0) error->one(FLERR,"Invalid electron radius in set command"); + } + + eradius[i] = radius; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_shape(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->ellipsoid_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set shape", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid shape in set command"); + } + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else { + action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (action->dvalue2 < 0.0) error->one(FLERR,"Invalid shape in set command"); + } + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else { + action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + if (action->dvalue3 < 0.0) error->one(FLERR,"Invalid shape in set command"); + } + + iarg += 4; +} + +void Set::invoke_shape(Action *action) +{ + int nlocal = atom->nlocal; + auto avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); + + int varflag = action->varflag; + double xvalue,yvalue,zvalue; + if (!action->varflag1) xvalue = action->dvalue1; + if (!action->varflag2) yvalue = action->dvalue2; + if (!action->varflag3) zvalue = action->dvalue3; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) xvalue = vec1[i]; + if (action->varflag1) yvalue = vec2[i]; + if (action->varflag1) zvalue = vec3[i]; + if (xvalue < 0.0 || yvalue < 0.0 || zvalue < 0.0) + error->one(FLERR,"Invalid shape in set command"); + } + + if (xvalue > 0.0 || yvalue > 0.0 || zvalue > 0.0) + if (xvalue == 0.0 || yvalue == 0.0 || zvalue == 0.0) + error->one(FLERR,"Invalid shape in set command"); + + avec_ellipsoid->set_shape(i,0.5*xvalue,0.5*yvalue,0.5*zvalue); + } + + // update global ellipsoid count + + bigint nlocal_bonus = avec_ellipsoid->nlocal_bonus; + MPI_Allreduce(&nlocal_bonus,&atom->nellipsoids,1,MPI_LMP_BIGINT,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_smd_contact_radius(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->smd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set smd/contact/radius", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid smd/contact/radius in set command"); + } + + iarg += 2; +} + +void Set::invoke_smd_contact_radius(Action *action) +{ + int nlocal = atom->nlocal; + double *contact_radius = atom->contact_radius; + + int varflag = action->varflag; + double radius; + if (!action->varflag1) radius = action->dvalue1; + + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + radius = vec1[i]; + if (radius < 0.0) error->one(FLERR,"Invalid smd/contact/radius in set command"); + } + + contact_radius[i] = radius; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_smd_mass_density(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->smd_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set smd/mass/density", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) error->one(FLERR,"Invalid smd/mass/density in set command"); + } + + iarg += 2; +} + +void Set::invoke_smd_mass_density(Action *action) +{ + int nlocal = atom->nlocal; + double *rmass = atom->rmass; + double *vfrac = atom->vfrac; + + int varflag = action->varflag; + double density; + if (!action->varflag1) density = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + density = vec1[i]; + if (density < 0.0) error->one(FLERR,"Invalid smd/mass/density in set command"); + } + + rmass[i] = vfrac[i] * density; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_sph_cv(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->cv_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/cv", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_sph_cv(Action *action) +{ + int nlocal = atom->nlocal; + double *cv = atom->cv; + + int varflag = action->varflag; + double sph_cv; + if (!action->varflag1) sph_cv = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) sph_cv = vec1[i]; + cv[i] = sph_cv; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_sph_e(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->esph_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/e", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_sph_e(Action *action) +{ + int nlocal = atom->nlocal; + double *esph = atom->esph; + + int varflag = action->varflag; + double sph_e; + if (!action->varflag1) sph_e = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) sph_e = vec1[i]; + esph[i] = sph_e; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_sph_rho(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->rho_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set sph/rho", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_sph_rho(Action *action) +{ + int nlocal = atom->nlocal; + double *rho = atom->rho; + + int varflag = action->varflag; + double sph_rho; + if (!action->varflag1) sph_rho = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) sph_rho = vec1[i]; + rho[i] = sph_rho; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_spin_atom(int &iarg, int narg, char **arg, Action *action) +{ + if ((strcmp(arg[iarg],"spin") == 0) && (comm->me == 0)) + error->warning(FLERR, "Set attribute spin is deprecated -- use spin/atom instead"); + if (!atom->sp_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+5 > narg) utils::missing_cmd_args(FLERR, "set spin/atom", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) + error->all(FLERR,"Invalid spin magnitude {} in set {} command", action->dvalue1, arg[iarg]); + } + + if (utils::strmatch(arg[iarg+2],"^v_")) varparse(arg[iarg+2],2,action); + else action->dvalue2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (utils::strmatch(arg[iarg+3],"^v_")) varparse(arg[iarg+3],3,action); + else action->dvalue3 = utils::numeric(FLERR,arg[iarg+3],false,lmp); + if (utils::strmatch(arg[iarg+4],"^v_")) varparse(arg[iarg+4],4,action); + else action->dvalue4 = utils::numeric(FLERR,arg[iarg+4],false,lmp); + + iarg += 5; +} + +void Set::invoke_spin_atom(Action *action) +{ + int nlocal = atom->nlocal; + double **sp = atom->sp; + double norm; + + int varflag = action->varflag; + double magnitude,xvalue,yvalue,zvalue; + if (!action->varflag1) magnitude = action->dvalue1; + if (!action->varflag2) xvalue = action->dvalue2; + if (!action->varflag3) yvalue = action->dvalue3; + if (!action->varflag4) zvalue = action->dvalue4; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + if (action->varflag1) magnitude = vec1[i]; + if (magnitude < 0.0) + error->one(FLERR,"Invalid spin magnitude in set command"); + if (action->varflag2) xvalue = vec2[i]; + if (action->varflag3) yvalue = vec3[i]; + if (action->varflag4) zvalue = vec4[i]; + } + + if ((xvalue == 0.0) && (yvalue == 0.0) && (zvalue == 0.0)) + error->all(FLERR,"At least one spin vector component must be non-zero"); + + norm = 1.0/sqrt(xvalue*xvalue+yvalue*yvalue+zvalue*zvalue); + sp[i][0] = norm*xvalue; + sp[i][1] = norm*yvalue; + sp[i][2] = norm*zvalue; + sp[i][3] = magnitude; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_spin_atom_random(int &iarg, int narg, char **arg, Action *action) +{ + if ((strcmp(arg[iarg], "spin/random") == 0) && (comm->me == 0)) + error->warning(FLERR, + "Set attribute spin/random is deprecated -- use spin/atom/random instead"); + if (!atom->sp_flag) + error->all(FLERR, "Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg + 3 > narg) utils::missing_cmd_args(FLERR, "set spin/atom/random", error); + + action->ivalue1 = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + action->dvalue1 = utils::numeric(FLERR, arg[iarg + 2], false, lmp); + + if (action->ivalue1 <= 0) + error->all(FLERR, iarg + 1, "Invalid random number seed {} in set {} command", action->ivalue1, + arg[iarg]); + if (action->dvalue1 <= 0.0) + error->all(FLERR, iarg + 2, "Invalid spin magnitude {} in set {} command", action->dvalue1, + arg[iarg]); + iarg += 3; +} + +void Set::invoke_spin_atom_random(Action *action) +{ + setrandom(SPIN_ATOM_RANDOM, action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_spin_electron(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->spin_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set spin/electron", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + if (action->ivalue1 < -1 || action->ivalue1 > 3) + error->one(FLERR,"Invalid electron spin {} in set command", action->ivalue1); + } + + iarg += 2; +} + +void Set::invoke_spin_electron(Action *action) +{ + int nlocal = atom->nlocal; + int *spin = atom->spin; + + int varflag = action->varflag; + int ispin; + if (!action->varflag1) ispin = action->ivalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + ispin = static_cast (vec1[i]); + if (ispin < -1 || ispin > 3) + error->one(FLERR,"Invalid electron spin in set command"); + } + + atom->spin[i] = ispin; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_temperature(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->temperature_flag) + error->all(FLERR,"Cannot set this attribute for this atom style"); + if (iarg+2 > narg) error->all(FLERR,"Illegal set command"); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid temperature in set command"); + } + + iarg += 2; +} + +void Set::invoke_temperature(Action *action) +{ + int nlocal = atom->nlocal; + double *temperature = atom->temperature; + + int varflag = action->varflag; + double temp; + if (!action->varflag1) temp = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + temp = vec1[i]; + if (temp < 0.0) error->one(FLERR,"Invalid temperature in set command"); + } + + temperature[i] = temp; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_theta(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->line_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set theta", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = DEG2RAD * utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_theta(Action *action) +{ + int nlocal = atom->nlocal; + int *line = atom->line; + + auto avec_line = dynamic_cast(atom->style_match("line")); + + int varflag = action->varflag; + double theta; + if (!action->varflag1) theta = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (line[i] < 0) error->one(FLERR,"Cannot set theta for atom which is not a line"); + if (varflag) theta = vec1[i]; + avec_line->bonus[atom->line[i]].theta = theta; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_theta_random(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->line_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set theta/random", error); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + if (action->ivalue1 <= 0) error->all(FLERR,"Invalid random number seed in set command"); + + iarg += 2; +} + +void Set::invoke_theta_random(Action *action) +{ + setrandom(THETA_RANDOM,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_tri(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->tri_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set tri", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 < 0.0) error->one(FLERR,"Invalid tri size in set command"); + } + + iarg += 2; +} + +void Set::invoke_tri(Action *action) +{ + int nlocal = atom->nlocal; + int *tri = atom->tri; + + auto avec_tri = dynamic_cast(atom->style_match("tri")); + + int varflag = action->varflag; + double trisize; + if (!action->varflag1) trisize = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; +#if 0 + // AK: this seems wrong. Isn't the set command supposed *make* this a triangle? + if (tri[i] < 0) error->one(FLERR,"Cannot set tri for atom which is not a triangle"); +#endif + + if (varflag) { + trisize = vec1[i]; + if (trisize < 0.0) error->one(FLERR,"Invalid tri size in set command"); + } + + avec_tri->set_equilateral(i,trisize); + } + + // update bonus tri count + + bigint nlocal_bonus = avec_tri->nlocal_bonus; + MPI_Allreduce(&nlocal_bonus,&atom->ntris,1,MPI_LMP_BIGINT,MPI_SUM,world); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_type(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set type", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::ATOM,lmp); + action->ivalue1 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue1 <= 0 || action->ivalue1 > atom->ntypes) + error->one(FLERR,"Invalid atom type in set command"); + } + + iarg += 2; +} + +void Set::invoke_type(Action *action) +{ + int nlocal = atom->nlocal; + int *type = atom->type; + + int varflag = action->varflag; + int itype; + if (!action->varflag1) itype = action->ivalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (action->varflag) { + itype = static_cast (vec1[i]); + if (itype <= 0 || itype > atom->ntypes) + error->one(FLERR,"Invalid atom type in set command"); + } + + type[i] = itype; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_type_fraction(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/fraction", error); + + // random seed must be ivalue1 for use in setrandom() + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::ATOM,lmp); + action->ivalue2 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue2 <= 0 || action->ivalue2 > atom->ntypes) + error->one(FLERR,"Invalid atom type in set command"); + + action->dvalue1 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (action->dvalue1 < 0.0 || action->dvalue1 > 1.0) + error->all(FLERR,"Invalid fraction in set command"); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+3],false,lmp); + if (action->ivalue1 <= 0) + error->all(FLERR,"Invalid random number seed in set command"); + + iarg += 4; +} + +void Set::invoke_type_fraction(Action *action) +{ + setrandom(TYPE_FRACTION,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_type_ratio(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/ratio", error); + + // random seed must be ivalue1 for use in setrandom() + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::ATOM,lmp); + action->ivalue2 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue2 <= 0 || action->ivalue2 > atom->ntypes) + error->all(FLERR,"Invalid atom type in set command"); + + action->dvalue1 = utils::numeric(FLERR,arg[iarg+2],false,lmp); + if (action->dvalue1 < 0.0 || action->dvalue1 > 1.0) + error->all(FLERR,"Invalid fraction in set command"); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+3],false,lmp); + if (action->ivalue1 <= 0) + error->all(FLERR,"Invalid random number seed in set command"); + + iarg += 4; +} + +void Set::invoke_type_ratio(Action *action) +{ + setrandom(TYPE_RATIO,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_type_subset(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+4 > narg) utils::missing_cmd_args(FLERR, "set type/subset", error); + + // random seed must be ivalue1 for use in setrandom() + + char *typestr = utils::expand_type(FLERR,arg[iarg+1],Atom::ATOM,lmp); + action->ivalue2 = utils::inumeric(FLERR,typestr?typestr:arg[iarg+1],false,lmp); + delete[] typestr; + if (action->ivalue2 <= 0 || action->ivalue2 > atom->ntypes) + error->all(FLERR,"Invalid atom type in set command"); + + action->bvalue1 = utils::bnumeric(FLERR,arg[iarg+2],false,lmp); + if (action->bvalue1 < 0) + error->all(FLERR,"Invalid subset size in set command"); + + action->ivalue1 = utils::inumeric(FLERR,arg[iarg+3],false,lmp); + if (action->ivalue1 <= 0) + error->all(FLERR,"Invalid random number seed in set command"); + + iarg += 4; +} + +void Set::invoke_type_subset(Action *action) +{ + setrandom(TYPE_SUBSET,action); +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_volume(int &iarg, int narg, char **arg, Action *action) +{ + if (!atom->vfrac_flag) + error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style()); + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set volume", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else { + action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + if (action->dvalue1 <= 0.0) error->all(FLERR,"Invalid volume in set command"); + } + + iarg += 2; +} + +void Set::invoke_volume(Action *action) +{ + int nlocal = atom->nlocal; + double *vfrac = atom->vfrac; + + int varflag = action->varflag; + double vol; + if (!action->varflag1) vol = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + + if (varflag) { + vol = vec1[i]; + if (vol < 0.0) error->one(FLERR,"Invalid volume in set command"); + } + + vfrac[i] = vol; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_vx(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vx", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_vx(Action *action) +{ + int nlocal = atom->nlocal; + double **v = atom->v; + + int varflag = action->varflag; + double vx; + if (!action->varflag1) vx = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) vx = vec1[i]; + v[i][0] = vx; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_vy(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vy", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_vy(Action *action) +{ + int nlocal = atom->nlocal; + double **v = atom->v; + + int varflag = action->varflag; + double vy; + if (!action->varflag1) vy = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) vy = vec1[i]; + v[i][1] = vy; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_vz(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set vz", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_vz(Action *action) +{ + int nlocal = atom->nlocal; + double **v = atom->v; + + int varflag = action->varflag; + double vz; + if (!action->varflag1) vz = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) vz = vec1[i]; + v[i][2] = vz; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_x(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set x", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_x(Action *action) +{ + int nlocal = atom->nlocal; + double **x = atom->x; + + int varflag = action->varflag; + double coord; + if (!action->varflag1) coord = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) coord = vec1[i]; + x[i][0] = coord; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_y(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set y", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_y(Action *action) +{ + int nlocal = atom->nlocal; + double **x = atom->x; + + int varflag = action->varflag; + double coord; + if (!action->varflag1) coord = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) coord = vec1[i]; + x[i][1] = coord; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_z(int &iarg, int narg, char **arg, Action *action) +{ + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set z", error); + + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + iarg += 2; +} + +void Set::invoke_z(Action *action) +{ + int nlocal = atom->nlocal; + double **x = atom->x; + + int varflag = action->varflag; + double coord; + if (!action->varflag1) coord = action->dvalue1; + + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) coord = vec1[i]; + x[i][2] = coord; + } +} + +/* ---------------------------------------------------------------------- */ + +void Set::process_custom(int &iarg, int narg, char **arg, Action *action) +{ + int flag,cols; + ArgInfo argi(arg[iarg],ArgInfo::DNAME|ArgInfo::INAME); + const char *pname = argi.get_name(); + + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set", error); + int index_custom = atom->find_custom(argi.get_name(),flag,cols); + if (index_custom < 0) + error->all(FLERR,"Set keyword or custom property {} does not exist",pname); + action->ivalue2 = index_custom; + + switch (argi.get_type()) { + + case ArgInfo::INAME: + if (flag != 0) error->all(FLERR,"Set command custom property {} is not integer",pname); + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->ivalue1 = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + + if (argi.get_dim() == 0) { + if (cols > 0) + error->all(FLERR,"Set command custom integer property {} is not a vector",pname); + action->keyword = IVEC; + } else if (argi.get_dim() == 1) { + if (cols == 0) + error->all(FLERR,"Set command custom integer property {} is not an array",pname); + int icol_custom = argi.get_index1(); + if (icol_custom <= 0 || icol_custom > cols) + error->all(FLERR,"Set command per-atom custom integer array {} is accessed " + "out-of-range",pname); + action->ivalue3 = icol_custom; + action->keyword = IARRAY; + } else error->all(FLERR,"Illegal set command"); + break; + + case ArgInfo::DNAME: + if (flag != 1) error->all(FLERR,"Custom property {} is not floating-point",argi.get_name()); + if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action); + else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); + + if (argi.get_dim() == 0) { + if (cols > 0) + error->all(FLERR,"Set command custom double property {} is not a vector",pname); + action->keyword = DVEC; + } else if (argi.get_dim() == 1) { + if (cols == 0) + error->all(FLERR,"Set command custom double property {} is not an array",pname); + int icol_custom = argi.get_index1(); + if (icol_custom <= 0 || icol_custom > cols) + error->all(FLERR,"Set command per-atom custom double array {} is " + "accessed out-of-range",pname); + action->ivalue3 = icol_custom; + action->keyword = DARRAY; + } else error->all(FLERR,"Illegal set command"); + break; + + default: + error->all(FLERR,"Illegal set command"); + break; + } + + iarg += 2; +} + +void Set::invoke_custom(Action *action) +{ + int nlocal = atom->nlocal; + int ivalue; + double dvalue; + + int varflag = action->varflag; + int index_custom = action->ivalue2; + + if (action->keyword == IVEC) { + if (!varflag) ivalue = action->ivalue1; + int *ivector = atom->ivector[index_custom]; + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) ivalue = static_cast (vec1[i]); + ivector[i] = ivalue; + } + + } else if (action->keyword == DVEC) { + if (!varflag) dvalue = action->dvalue1; + double *dvector = atom->dvector[index_custom]; + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) dvalue = vec1[i]; + dvector[i] = dvalue; + } + + } else if (action->keyword == IARRAY) { + if (!varflag) ivalue = action->ivalue1; + int **iarray = atom->iarray[index_custom]; + int icol_custom = action->ivalue3 - 1; + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) ivalue = static_cast (vec1[i]); + iarray[i][icol_custom] = ivalue; + } + + } else if (action->keyword == DARRAY) { + if (!varflag) dvalue = action->dvalue1; + double **darray = atom->darray[index_custom]; + int icol_custom = action->ivalue3 - 1; + for (int i = 0; i < nlocal; i++) { + if (!select[i]) continue; + if (varflag) dvalue = vec1[i]; + darray[i][icol_custom] = dvalue; + } } } diff --git a/src/set.h b/src/set.h index e2be5d5c1e..65489d9932 100644 --- a/src/set.h +++ b/src/set.h @@ -26,29 +26,168 @@ namespace LAMMPS_NS { class Set : public Command { public: - Set(class LAMMPS *lmp) : Command(lmp) {}; + Set(class LAMMPS *lmp); + ~Set() override; + void command(int, char **) override; - private: - char *id; - int *select; - int style, ivalue, newtype, count, index_custom, icol_custom; - int ximage, yimage, zimage, ximageflag, yimageflag, zimageflag; - int cc_index; - bigint nsubset; - double dvalue, xvalue, yvalue, zvalue, wvalue, fraction; - - int varflag, varflag1, varflag2, varflag3, varflag4; - int ivar1, ivar2, ivar3, ivar4; - double *vec1, *vec2, *vec3, *vec4; - - int discflag; - + void process_args(int, int, char **); void selection(int); - void set(int); - void setrandom(int); - void topology(int); - void varparse(const char *, int); + void invoke_actions(); + + private: + int caller; // SETCOMMAND or FIXSET + + // params for atom selection + + int style; + char *id; + int nlo, nhi; + bigint nlobig, nhibig; + int groupbit; + class Region *region; + + // one Action = one keyword/value pair + + struct Action { + int keyword; + int argindex; + int count_select, count_action; + int varflag; + int varflag1, varflag2, varflag3, varflag4; + int ivar1, ivar2, ivar3, ivar4; + int ivalue1, ivalue2, ivalue3, ivalue4, ivalue5, ivalue6; + tagint tvalue1; + bigint bvalue1; + double dvalue1, dvalue2, dvalue3, dvalue4; + }; + + int naction, maxaction; + Action *actions; + + typedef void (Set::*FnPtrPack)(Action *); + FnPtrPack *invoke_choice; // list of ptrs to invoke functions + + double *vec1, *vec2, *vec3, *vec4; // storage for evaluated peratom variables + int varflag; // 1 if any action uses a per-atom variable + int varflag1, varflag2, varflag3, varflag4; // 1 if any action uses this variable + int maxvariable; // size of peratom variable vectors + + int *select; // flag for selected atoms + int maxselect; // size of select vector + + int count_select; // count of selected atoms on this proc + int count_action; // count of actions on this proc, only if different than selected + + // private functions + + void varparse(const char *, int, Action *); + void setrandom(int, Action *); + void topology(int, Action *); + + // customize by adding a process method + + void process_angle(int &, int, char **, Action *); + void process_angmom(int &, int, char **, Action *); + void process_bond(int &, int, char **, Action *); + void process_cc(int &, int, char **, Action *); + void process_charge(int &, int, char **, Action *); + void process_density(int &, int, char **, Action *); + void process_diameter(int &, int, char **, Action *); + void process_dihedral(int &, int, char **, Action *); + void process_dipole(int &, int, char **, Action *); + void process_dipole_random(int &, int, char **, Action *); + void process_dpd_theta(int &, int, char **, Action *); + void process_edpd_cv(int &, int, char **, Action *); + void process_edpd_temp(int &, int, char **, Action *); + void process_epsilon(int &, int, char **, Action *); + void process_image(int &, int, char **, Action *); + void process_improper(int &, int, char **, Action *); + void process_length(int &, int, char **, Action *); + void process_mass(int &, int, char **, Action *); + void process_mol(int &, int, char **, Action *); + void process_omega(int &, int, char **, Action *); + void process_quat(int &, int, char **, Action *); + void process_quat_random(int &, int, char **, Action *); + void process_radius_election(int &, int, char **, Action *); + void process_shape(int &, int, char **, Action *); + void process_smd_contact_radius(int &, int, char **, Action *); + void process_smd_mass_density(int &, int, char **, Action *); + void process_sph_cv(int &, int, char **, Action *); + void process_sph_e(int &, int, char **, Action *); + void process_sph_rho(int &, int, char **, Action *); + void process_spin_atom(int &, int, char **, Action *); + void process_spin_atom_random(int &, int, char **, Action *); + void process_spin_electron(int &, int, char **, Action *); + void process_temperature(int &, int, char **, Action *); + void process_theta(int &, int, char **, Action *); + void process_theta_random(int &, int, char **, Action *); + void process_tri(int &, int, char **, Action *); + void process_type(int &, int, char **, Action *); + void process_type_fraction(int &, int, char **, Action *); + void process_type_ratio(int &, int, char **, Action *); + void process_type_subset(int &, int, char **, Action *); + void process_volume(int &, int, char **, Action *); + void process_vx(int &, int, char **, Action *); + void process_vy(int &, int, char **, Action *); + void process_vz(int &, int, char **, Action *); + void process_x(int &, int, char **, Action *); + void process_y(int &, int, char **, Action *); + void process_z(int &, int, char **, Action *); + + void process_custom(int &, int, char **, Action *); + + // customize by adding an invoke method + + void invoke_angle(Action *); + void invoke_angmom(Action *); + void invoke_bond(Action *); + void invoke_cc(Action *); + void invoke_charge(Action *); + void invoke_density(Action *); + void invoke_diameter(Action *); + void invoke_dihedral(Action *); + void invoke_dipole(Action *); + void invoke_dipole_random(Action *); + void invoke_dpd_theta(Action *); + void invoke_edpd_cv(Action *); + void invoke_edpd_temp(Action *); + void invoke_epsilon(Action *); + void invoke_image(Action *); + void invoke_improper(Action *); + void invoke_length(Action *); + void invoke_mass(Action *); + void invoke_mol(Action *); + void invoke_omega(Action *); + void invoke_quat(Action *); + void invoke_quat_random(Action *); + void invoke_radius_election(Action *); + void invoke_shape(Action *); + void invoke_smd_contact_radius(Action *); + void invoke_smd_mass_density(Action *); + void invoke_sph_cv(Action *); + void invoke_sph_e(Action *); + void invoke_sph_rho(Action *); + void invoke_spin_atom(Action *); + void invoke_spin_atom_random(Action *); + void invoke_spin_electron(Action *); + void invoke_temperature(Action *); + void invoke_theta(Action *); + void invoke_theta_random(Action *); + void invoke_tri(Action *); + void invoke_type(Action *); + void invoke_type_fraction(Action *); + void invoke_type_ratio(Action *); + void invoke_type_subset(Action *); + void invoke_volume(Action *); + void invoke_vx(Action *); + void invoke_vy(Action *); + void invoke_vz(Action *); + void invoke_x(Action *); + void invoke_y(Action *); + void invoke_z(Action *); + + void invoke_custom(Action *); }; } // namespace LAMMPS_NS diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a2b95ab89e..bca80f7442 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -19,6 +19,8 @@ #include "fmt/format.h" #include "utils.h" +#include +#include #include using namespace LAMMPS_NS; @@ -365,6 +367,11 @@ double ValueTokenizer::next_double() return val; // rethrow exceptions from std::stod() } catch (std::out_of_range const &) { + // could be a denormal number. try again with std::strtod(). + char *end; + auto val = std::strtod(current.c_str(), &end); + // return value of denormal + if ((val > -HUGE_VAL) && (val < HUGE_VAL)) return val; throw InvalidFloatException(current); } catch (std::invalid_argument const &) { throw InvalidFloatException(current); diff --git a/src/update.cpp b/src/update.cpp index 4140a31731..950ac59b40 100644 --- a/src/update.cpp +++ b/src/update.cpp @@ -99,6 +99,13 @@ Update::Update(LAMMPS *lmp) : Update::~Update() { + // restore default styles to avoid segfaults from plugins + char *str = (char *) "verlet"; + create_integrate(1, &str, 1); + + str = (char *) "cg"; + create_minimize(1, &str, 1); + delete[] unit_style; delete[] integrate_style; diff --git a/src/utils.cpp b/src/utils.cpp index 1c26dcf3b0..1c0f1112b6 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -272,6 +274,11 @@ void utils::print(FILE *fp, const std::string &mesg) fputs(mesg.c_str(), fp); } +void utils::print(const std::string &mesg) +{ + fputs(mesg.c_str(), stdout); +} + void utils::fmtargs_print(FILE *fp, fmt::string_view format, fmt::format_args args) { print(fp, fmt::vformat(format, args)); @@ -529,7 +536,7 @@ double utils::numeric(const char *file, int line, const std::string &str, bool d lmp->error->all(file, line, msg); } - double rv = 0; + double rv = 0.0; auto msg = fmt::format("Floating point number {} in input script or data file is invalid", buf); try { std::size_t endpos; @@ -546,6 +553,12 @@ double utils::numeric(const char *file, int line, const std::string &str, bool d else lmp->error->all(file, line, msg); } catch (std::out_of_range const &) { + // could be a denormal number. try again with std::strtod(). + char *end; + rv = std::strtod(buf.c_str(), &end); + // return value if denormal + if ((rv > -HUGE_VAL) && (rv < HUGE_VAL)) return rv; + msg = fmt::format("Floating point number {} in input script or data file is out of range", buf); if (do_abort) lmp->error->one(file, line, msg); @@ -902,7 +915,7 @@ int utils::expand_args(const char *file, int line, int narg, char **arg, int mod // match grids if (strmatch(word, "^[cf]_\\w+:\\w+:\\w+\\[\\d*\\*\\d*\\]")) { - auto gridid = utils::parse_grid_id(FLERR, word, lmp->error); + auto gridid = utils::parse_grid_id(file, line, word, lmp->error); size_t first = gridid[2].find('['); size_t second = gridid[2].find(']', first + 1); @@ -1046,6 +1059,9 @@ int utils::expand_args(const char *file, int line, int narg, char **arg, int mod if (nhi < MAXSMALLINT) { nmax = nhi; expandflag = 1; + } else { + lmp->error->all(file, line, ioffset + iarg, + "Upper bound required to expand vector style variable {}", id); } } } diff --git a/src/utils.h b/src/utils.h index b403d78061..d57395d6d8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -151,7 +151,7 @@ output are compressed to a single blank by calling :cpp:func:`strcompress()` \endverbatim * - * This function implements a version of fprintf() that uses {fmt} formatting + * This function implements a version of (f)printf() that uses {fmt} formatting * * \param fp stdio FILE pointer * \param format format string of message to be printed @@ -163,12 +163,32 @@ output are compressed to a single blank by calling :cpp:func:`strcompress()` } /*! \overload + * + * Print to stdout without specifying the FILE pointer. + * + * \param mesg string with message to be printed */ + template void print(const std::string &format, Args &&...args) + { + fmtargs_print(stdout, format, fmt::make_format_args(args...)); + } + + /*! \overload + * + * Print string message without format * * \param fp stdio FILE pointer * \param mesg string with message to be printed */ void print(FILE *fp, const std::string &mesg); + /*! \overload + * + * Print string message without format to stdout + * + * \param mesg string with message to be printed */ + + void print(const std::string &mesg); + /*! Return text redirecting the user to a specific paragraph in the manual * * The LAMMPS manual contains detailed explanations for errors and diff --git a/src/variable.cpp b/src/variable.cpp index ee2114998c..b6cf99d363 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -81,6 +81,7 @@ enum{DONE,ADD,SUBTRACT,MULTIPLY,DIVIDE,CARAT,MODULO,UNARY, RAMP,STAGGER,LOGFREQ,LOGFREQ2,LOGFREQ3,STRIDE,STRIDE2, VDISPLACE,SWIGGLE,CWIGGLE,SIGN,GMASK,RMASK, GRMASK,IS_ACTIVE,IS_DEFINED,IS_AVAILABLE,IS_FILE,EXTRACT_SETTING, + PYWRAPPER, VALUE,ATOMARRAY,TYPEARRAY,INTARRAY,BIGINTARRAY,VECTORARRAY}; // customize by adding a special function @@ -116,6 +117,7 @@ Variable::Variable(LAMMPS *lmp) : Pointers(lmp) num = nullptr; which = nullptr; pad = nullptr; + pyindex = nullptr; reader = nullptr; data = nullptr; dvalue = nullptr; @@ -162,6 +164,7 @@ Variable::~Variable() memory->destroy(num); memory->destroy(which); memory->destroy(pad); + memory->destroy(pyindex); memory->sfree(reader); memory->sfree(data); memory->sfree(dvalue); @@ -594,6 +597,7 @@ void Variable::set(int narg, char **arg) num[nvar] = 2; which[nvar] = 1; pad[nvar] = 0; + pyindex[nvar] = -1; data[nvar] = new char *[num[nvar]]; data[nvar][0] = utils::strdup(arg[2]); data[nvar][1] = new char[VALUELENGTH]; @@ -956,13 +960,12 @@ void Variable::python_command(int narg, char **arg) int Variable::equalstyle(int ivar) { - if (style[ivar] == EQUAL || style[ivar] == TIMER || - style[ivar] == INTERNAL) return 1; + if (style[ivar] == EQUAL || style[ivar] == TIMER || style[ivar] == INTERNAL) return 1; if (style[ivar] == PYTHON) { - int ifunc = python->variable_match(data[ivar][0],names[ivar],1); - if (ifunc < 0) return 0; - else return 1; + pyindex[ivar] = python->function_match(data[ivar][0], names[ivar], 1, error); + if (pyindex[ivar] >= 0) return 1; } + return 0; } @@ -989,7 +992,7 @@ int Variable::vectorstyle(int ivar) } /* ---------------------------------------------------------------------- - check if variable with name is PYTHON and matches funcname + check if variable with name is PYTHON style and matches funcname called by Python class before it invokes a Python function return data storage so Python function can return a value for this variable return nullptr if not a match @@ -1006,7 +1009,7 @@ char *Variable::pythonstyle(char *name, char *funcname) /* ---------------------------------------------------------------------- return 1 if variable is INTERNAL style, 0 if not - this is checked before call to set_internal() to assure it can be set + this is checked before call to internal_set() to ensure it can be set ------------------------------------------------------------------------- */ int Variable::internalstyle(int ivar) @@ -1082,23 +1085,8 @@ char *Variable::retrieve(const char *name) str = data[ivar][1] = utils::strdup(result); } else if (style[ivar] == PYTHON) { - int ifunc = python->variable_match(data[ivar][0],name,0); - if (ifunc < 0) { - if (ifunc == -1) { - error->all(FLERR, "Could not find Python function {} linked to variable {}", - data[ivar][0], name); - } else if (ifunc == -2) { - error->all(FLERR, "Python function {} for variable {} does not have a return value", - data[ivar][0], name); - } else if (ifunc == -3) { - error->all(FLERR,"Python variable {} does not match variable name registered with " - "Python function {}", name, data[ivar][0]); - } else { - error->all(FLERR, "Unknown error verifying function {} linked to python style variable {}", - data[ivar][0],name); - } - } - python->invoke_function(ifunc,data[ivar][1]); + int ifunc = python->function_match(data[ivar][0],name,0,error); + python->invoke_function(ifunc,data[ivar][1],nullptr); str = data[ivar][1]; // if Python func returns a string longer than VALUELENGTH @@ -1157,17 +1145,7 @@ double Variable::compute_equal(int ivar) if (style[ivar] == EQUAL) value = evaluate(data[ivar][0],nullptr,ivar); else if (style[ivar] == TIMER) value = dvalue[ivar]; else if (style[ivar] == INTERNAL) value = dvalue[ivar]; - else if (style[ivar] == PYTHON) { - int ifunc = python->find(data[ivar][0]); - if (ifunc < 0) - print_var_error(FLERR,fmt::format("cannot find python function {}",data[ivar][0]),ivar); - python->invoke_function(ifunc,data[ivar][1]); - try { - value = std::stod(data[ivar][1]); - } catch (std::exception &e) { - print_var_error(FLERR,"has an invalid value", ivar); - } - } + else if (style[ivar] == PYTHON) python->invoke_function(pyindex[ivar],nullptr,&value); // round to zero on underflow if (fabs(value) < std::numeric_limits::min()) value = 0.0; @@ -1334,6 +1312,30 @@ void Variable::internal_set(int ivar, double value) dvalue[ivar] = value; } +/* ---------------------------------------------------------------------- + create an INTERNAL style variable with name, set to value +------------------------------------------------------------------------- */ + +void Variable::internal_create(char *name, double value) +{ + if (find(name) >= 0) + error->all(FLERR,"Creation of internal-style variable {} which already exists", name); + + if (nvar == maxvar) grow(); + style[nvar] = INTERNAL; + num[nvar] = 1; + which[nvar] = 0; + pad[nvar] = 0; + data[nvar] = new char *[num[nvar]]; + data[nvar][0] = new char[VALUELENGTH]; + dvalue[nvar] = value; + + if (!utils::is_id(name)) + error->all(FLERR, "Variable name '{}' must have only letters, numbers, or underscores", name); + names[nvar] = utils::strdup(name); + nvar++; +} + /* ---------------------------------------------------------------------- remove Nth variable from list and compact list delete reader explicitly if it exists @@ -1381,6 +1383,7 @@ void Variable::grow() memory->grow(num,maxvar,"var:num"); memory->grow(which,maxvar,"var:which"); memory->grow(pad,maxvar,"var:pad"); + memory->grow(pyindex,maxvar,"var:pyindex"); reader = (VarReader **) memory->srealloc(reader,maxvar*sizeof(VarReader *),"var:reader"); @@ -1424,6 +1427,7 @@ void Variable::copy(int narg, char **from, char **to) sin(x),cos(x),tan(x),asin(x),atan2(y,x),... group function = count(group), mass(group), xcm(group,x), ... special function = sum(x),min(x), ... + python function wrapper = py_varname(x,y,z,...) (up to MAXFUNCARG) atom value = x[i], y[i], vx[i], ... atom vector = x, y, vx, ... custom atom property = i/d_name, i/d_name[i], i/d2_name[i], i/d2_name[i][j] @@ -2361,13 +2365,14 @@ double Variable::evaluate(char *str, Tree **tree, int ivar) } // ---------------- - // math/group/special/labelmap function or atom value/vector or constant or thermo keyword + // math/group/region/special/feature function or atom value/vector or constant or thermo keyword // ---------------- } else { // ---------------- - // math or group or special function + // math or group/region or special or feature function + // math_function() includes Python function wrapper // ---------------- if (str[i] == '(') { @@ -2625,7 +2630,8 @@ double Variable::evaluate(char *str, Tree **tree, int ivar) atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y),swiggle(x,y,z), - cwiggle(x,y,z),sign(x),gmask(x),rmask(x),grmask(x,y) + cwiggle(x,y,z),sign(x),py_varname(x,y,z,...), + gmask(x),rmask(x),grmask(x,y) ---------------------------------------------------------------------- */ double Variable::collapse_tree(Tree *tree) @@ -3183,6 +3189,30 @@ double Variable::collapse_tree(Tree *tree) return tree->value; } + if (tree->type == PYWRAPPER) { + int narg = tree->argcount; + int *argvars = tree->argvars; + double arg; + for (int iarg = 0; iarg < narg; iarg++) { + if (iarg == 0) arg = collapse_tree(tree->first); + else if (iarg == 1) arg = collapse_tree(tree->second); + else arg = collapse_tree(tree->extra[iarg-2]); + internal_set(argvars[iarg],arg); + } + for (int iarg = 0; iarg < narg; iarg++) { + if (iarg == 0) { + if (tree->first->type != VALUE) return 0.0; + } else if (iarg == 1) { + if (tree->second->type != VALUE) return 0.0; + } else { + if (tree->extra[iarg-2]->type != VALUE) return 0.0; + } + } + tree->type = VALUE; + tree->value = compute_equal(tree->pyvar); + return tree->value; + } + // mask functions do not become a single collapsed value if (tree->type == GMASK) return 0.0; @@ -3196,12 +3226,14 @@ double Variable::collapse_tree(Tree *tree) evaluate an atom-style or vector-style variable parse tree index I = atom I or vector index I tree was created by one-time parsing of formula string via evaluate() + followed by collapse_tree() operation to streamline tree as much as possible customize by adding a function: sqrt(),exp(),ln(),log(),sin(),cos(),tan(),asin(),acos(),atan(), atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(x,y,z), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y), - swiggle(x,y,z),cwiggle(x,y,z),sign(x),gmask(x),rmask(x),grmask(x,y) + swiggle(x,y,z),cwiggle(x,y,z),sign(x),py_varname(x,y,z,...), + gmask(x),rmask(x),grmask(x,y) ---------------------------------------------------------------------- */ double Variable::eval_tree(Tree *tree, int i) @@ -3518,7 +3550,19 @@ double Variable::eval_tree(Tree *tree, int i) } if (tree->type == SIGN) - return (eval_tree(tree->first,i) >= 0.0) ? 1.0 : -1.0; // sign(eval_tree(tree->first,i)); + return (eval_tree(tree->first,i) >= 0.0) ? 1.0 : -1.0; // sign(eval_tree(tree->first,i)); + + if (tree->type == PYWRAPPER) { + int narg = tree->argcount; + for (int iarg = 0; iarg < narg; iarg++) { + if (iarg == 0) arg = eval_tree(tree->first,i); + else if (iarg == 1) arg = eval_tree(tree->second,i); + else arg = eval_tree(tree->extra[iarg-2],i); + internal_set(tree->argvars[iarg],arg); + } + arg = compute_equal(tree->pyvar); + return arg; + } if (tree->type == GMASK) { if (atom->mask[i] & tree->ivalue) return 1.0; @@ -3583,6 +3627,7 @@ void Variable::free_tree(Tree *tree) for (int i = 0; i < tree->nextra; i++) free_tree(tree->extra[i]); delete[] tree->extra; } + if (tree->argvars) delete[] tree->argvars; if (tree->selfalloc) memory->destroy(tree->array); delete tree; @@ -3685,7 +3730,7 @@ tagint Variable::int_between_brackets(char *&ptr, int varallow) /* ---------------------------------------------------------------------- process a math function in formula push result onto tree or arg stack - word = math function + word = math function name contents = str between parentheses with comma-separated args return 0 if not a match, 1 if successfully processed customize by adding a math function: @@ -3693,7 +3738,7 @@ tagint Variable::int_between_brackets(char *&ptr, int varallow) atan2(y,x),random(x,y,z),normal(x,y,z),ceil(),floor(),round(),ternary(), ramp(x,y),stagger(x,y),logfreq(x,y,z),logfreq2(x,y,z), logfreq3(x,y,z),stride(x,y,z),stride2(x,y,z,a,b,c),vdisplace(x,y), - swiggle(x,y,z),cwiggle(x,y,z),sign(x) + swiggle(x,y,z),cwiggle(x,y,z),sign(x),py_varname(x,y,z,...) ------------------------------------------------------------------------- */ int Variable::math_function(char *word, char *contents, Tree **tree, Tree **treestack, @@ -3711,7 +3756,8 @@ int Variable::math_function(char *word, char *contents, Tree **tree, Tree **tree strcmp(word,"logfreq") != 0 && strcmp(word,"logfreq2") != 0 && strcmp(word,"logfreq3") != 0 && strcmp(word,"stride") != 0 && strcmp(word,"stride2") != 0 && strcmp(word,"vdisplace") != 0 && - strcmp(word,"swiggle") != 0 && strcmp(word,"cwiggle") != 0 && strcmp(word,"sign") != 0) + strcmp(word,"swiggle") != 0 && strcmp(word,"cwiggle") != 0 && strcmp(word,"sign") != 0 && + strstr(word,"py_") != word) return 0; // parse contents for comma-separated args @@ -4106,11 +4152,51 @@ int Variable::math_function(char *word, char *contents, Tree **tree, Tree **tree double value = value1 + value2*(1.0-cos(omega*delta*update->dt)); argstack[nargstack++] = value; } + } else if (strcmp(word,"sign") == 0) { if (narg != 1) print_var_error(FLERR,"Invalid math function in variable formula",ivar); if (tree) newtree->type = SIGN; else argstack[nargstack++] = (value1 >= 0.0) ? 1.0 : -1.0; // sign(value1); + + // Python wrapper function tied to python-style variable + // text following py_ = python-style variable name tied to Python function + // narg arguments are tied to internal variables defined by python command + + } else if (strstr(word,"py_") == word) { + + // pyvar = index of python-style variable which invokes Python function + + int pyvar = find(&word[3]); + if (style[pyvar] != PYTHON) + print_var_error(FLERR,"Invalid python function variable name",ivar); + + // check that wrapper matches Python function + // jvars = returned indices of narg internal variables used by Python function + + int *jvars = new int[narg]; + pyindex[pyvar] = python->wrapper_match(data[pyvar][0],names[pyvar],narg,jvars,error); + + // if tree: store python variable and arg info in tree for later eval + // else: one-time eval of python-coded function now via python variable + + if (tree) { + newtree->type = PYWRAPPER; + newtree->pyvar = pyvar; + newtree->argcount = narg; + newtree->argvars = new int[narg]; + for (int iarg = 0; iarg < narg; iarg++) + newtree->argvars[iarg] = jvars[iarg]; + } else { + for (int iarg = 0; iarg < narg; iarg++) { + if (iarg == 0) internal_set(jvars[iarg],value1); + else if (iarg == 1) internal_set(jvars[iarg],value2); + else internal_set(jvars[iarg],values[iarg-2]); + } + argstack[nargstack++] = compute_equal(pyvar); + } + + delete[] jvars; } // delete stored args @@ -4377,7 +4463,7 @@ Region *Variable::region_function(char *id, int ivar) customize by adding a special function: sum(x),min(x),max(x),ave(x),trap(x),slope(x), gmask(x),rmask(x),grmask(x,y),next(x),is_file(x),is_os(x), - extract_setting(x),label2type(x,y),is_tpelabel(x,y) + extract_setting(x),label2type(x,y),is_typelabel(x,y) is_timeout() ------------------------------------------------------------------------- */ @@ -5296,7 +5382,11 @@ void Variable::print_var_error(const std::string &srcfile, const int lineno, void Variable::print_tree(Tree *tree, int level) { - printf("TREE %d: %d %g\n",level,tree->type,tree->value); + if (tree->type == VALUE) { + printf("TREE %d: %d %g\n",level,tree->type,tree->value); + return; + } + printf("TREE %d: %d\n",level,tree->type); if (tree->first) print_tree(tree->first,level+1); if (tree->second) print_tree(tree->second,level+1); if (tree->nextra) diff --git a/src/variable.h b/src/variable.h index c71d21ca63..25afe55e1b 100644 --- a/src/variable.h +++ b/src/variable.h @@ -49,6 +49,7 @@ class Variable : protected Pointers { void compute_atom(int, int, double *, int, int); int compute_vector(int, double **); void internal_set(int, double); + void internal_create(char *, double); tagint int_between_brackets(char *&, int); double evaluate_boolean(char *); @@ -87,6 +88,7 @@ class Variable : protected Pointers { int *num; // # of values for each variable int *which; // next available value for each variable int *pad; // 1 = pad loop/uloop variables with 0s, 0 = no pad + int *pyindex; // indices to Python funcs for python-style vars class VarReader **reader; // variable that reads from file char ***data; // str value of each variable's values double *dvalue; // single numeric value for internal variables @@ -123,9 +125,13 @@ class Variable : protected Pointers { Tree *first, *second; // ptrs further down tree for first 2 args Tree **extra; // ptrs further down tree for nextra args + int pyvar; // index of Python variable invoked as py_name() + int argcount; // # of args to associated Python function + int *argvars; // indices of internal variables for each arg + Tree() : array(nullptr), iarray(nullptr), barray(nullptr), selfalloc(0), ivalue(0), nextra(0), - region(nullptr), first(nullptr), second(nullptr), extra(nullptr) + region(nullptr), first(nullptr), second(nullptr), extra(nullptr), argvars(nullptr) { } }; diff --git a/src/write_dump.cpp b/src/write_dump.cpp index 71d955dc1f..d0918ec39f 100644 --- a/src/write_dump.cpp +++ b/src/write_dump.cpp @@ -85,7 +85,7 @@ void WriteDump::command(int narg, char **arg) if (strcmp(arg[1], "image") == 0) (dynamic_cast(dump))->multifile_override = 1; if (strcmp(arg[1], "cfg") == 0) (dynamic_cast(dump))->multifile_override = 1; if ((update->first_update == 0) && (comm->me == 0) && (noinitwarn == 0)) - error->warning(FLERR, "Calling write_dump before a full system init."); + error->warning(FLERR, "Calling write_dump before a full system init"); dump->init(); dump->write(); diff --git a/tools/README b/tools/README index c1f49c48c6..a28dd8d75f 100644 --- a/tools/README +++ b/tools/README @@ -27,6 +27,7 @@ emacs add-ons to EMACS editor for editing LAMMPS input scripts fep scripts for free-energy perturbation with FEP pkg i-pi Python wrapper for performing path-integral MD (PIMD) ipp input pre-processor Perl tool for creating input scripts +json tools and files for using JSON files in LAMMPS kate add-ons to Kate editor for editing LAMMPS input scripts lmp2arc convert LAMMPS output to Accelrys Insight format lmp2cfg convert LAMMPS output to CFG files for AtomEye viz diff --git a/tools/json/README.md b/tools/json/README.md new file mode 100644 index 0000000000..a4b2a11f1f --- /dev/null +++ b/tools/json/README.md @@ -0,0 +1,61 @@ +This folder contains files and tools for creating, modifying, and validating +files in JSON format. This is work in progress while we are adding JSON +support + +# JSON file format validation. + +## JSON-Schema files + +We provide schema files for the file formats that LAMMPS supports following +the specifications available on [JSON-Schema](https://json-schema.org) webpage. +The following files are currently available. + +- `molecule-schema.json` Schema file for the JSON format molecule files. + +These files provide a concise description of the hierarchy and supported fields +in JSON file formats. Thus they provide a detailed documentation and can also +be used for validating JSON files. + +## Validation of JSON files + +There are multiple tools for JSON file validation available. Here are instructions +for how to use a tool called `check-jsonschema` which is available via +[PyPi](https://pypi.org/). + +``` bash +# Installation into a virtual environment. +# Once installed only the activation should be needed +python3 -m venv validate-json +source validate-json/bin/activate +pip install --upgrade pip +pip install check-jsonschema + +# Validation of two molecule files "rxn1.json" and "twomols.json" with "molecule-schema.json" +check-jsonschema --schemafile molecule-schema.json rxn1.json twomols.json +``` + +If the files are conforming there should be the output: +``` +ok -- validation done +``` +Otherwise details about the non-conforming fields are given. + +# JSON file format normalization + +There are extensions to the strict JSON format that allow for comments +or ignore additional (dangling) commas. The ``reformat-json.cpp`` tool +will read JSON files in relaxed format, but write it out in strict format. +It is also possible to change the level of indentation from -1 (all data +one long line) to any positive integer value. The original file will be +backed up (.bak added to file name) and then overwritten. + +Manual compilation (it will be automatically included in the CMake build +if building tools is requested during CMake configuration): + +```bash +g++ -I -o reformat-json reformat-json.cpp +``` + +------- + +updated by Axel Kohlmeyer, 2025-05-23 diff --git a/tools/json/molecule-schema.json b/tools/json/molecule-schema.json new file mode 100644 index 0000000000..de212942b6 --- /dev/null +++ b/tools/json/molecule-schema.json @@ -0,0 +1,306 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://download.lammps.org/json/molecule-schema.json", + "title": "JSON schema for LAMMPS 'molecule' files and objects", + "description": "Version 0.1; last updated 2025-06-12", + "type": "object", + "properties": { + "application": { + "type": "string", + "const": "LAMMPS" + }, + "format": { + "type": "string", + "const": "molecule" + }, + "revision": { + "type": "integer", + "minimum": 1, + "maximum": 1 + }, + "title": {"type": "string"}, + "schema": {"type": "string"}, + "units": { + "enum": ["lj", "real", "metal", "si", "cgs", "electron", "micro", "nano" ] + }, + "com": {"type": "array", + "items": { "type": "number"}, + "minItems": 3, + "maxItems": 3 + }, + "masstotal": {"type": "number"}, + "body": {"type": "array", + "items": { "type": "integer"}, + "minItems": 2, + "maxItems": 2 + }, + "inertia": {"type": "array", + "items": { "type": "number"}, + "minItems": 6, + "maxItems": 6 + }, + "coords": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "x", "y", "z"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "integer"}, + {"type": "number"}, + {"type": "number"}, + {"type": "number"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "types": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "type"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "number"}, + {"type": ["number", "string"]} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "molecules": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "molecule-id"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 2, + "maxItems": 2 + } + } + }, + "required": ["format", "data"] + }, + "fragments": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["fragment-id", "atom-id-list"] + }, + "data": { + "type": "array", + "items": { + "type": ["string", "array"], + "minItems": 2, + "maxItems": 2 + } + } + }, + "required": ["format", "data"] + }, + "charges": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "charge"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "integer"}, + {"type": "number"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "dipoles": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "mux", "muy", "muz"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "integer"}, + {"type": "number"}, + {"type": "number"}, + {"type": "number"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "diameters": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "diameter"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "integer"}, + {"type": "number"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "masses": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["atom-id", "mass"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "integer"}, + {"type": "number"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "bonds": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["bond-type", "atom1", "atom2"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": ["integer", "string"]}, + {"type": "integer"}, + {"type": "integer"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "angles": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["angle-type", "atom1", "atom2", "atom3"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": ["integer", "string"]}, + {"type": "integer"}, + {"type": "integer"}, + {"type": "integer"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "dihedrals": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["dihedral-type", "atom1", "atom2", "atom3", "atom4"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": ["integer", "string"]}, + {"type": "integer"}, + {"type": "integer"}, + {"type": "integer"}, + {"type": "integer"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + }, + "impropers": { + "type": "object", + "properties": { + "format": { + "type": "array", + "const": ["improper-type", "atom1", "atom2", "atom3", "atom4"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": ["integer", "string"]}, + {"type": "integer"}, + {"type": "integer"}, + {"type": "integer"}, + {"type": "integer"} + ], + "items": false + } + } + }, + "required": ["format", "data"] + } + }, + "required": ["application", "format", "revision", "types"] +} diff --git a/tools/json/reformat-json.cpp b/tools/json/reformat-json.cpp new file mode 100644 index 0000000000..54897233b4 --- /dev/null +++ b/tools/json/reformat-json.cpp @@ -0,0 +1,47 @@ + +#include "json.h" + +#include +#include +#include + +using json = LAMMPS_NS::json; + +int main(int argc, char **argv) +{ + if (argc < 3) { + printf("Usage: %s [ ...]\n", argv[0]); + return 1; + } + + int indent = std::stoi(argv[1]); + + // loop over files + + for (int i=2; i < argc; ++i) { + std::string file = argv[i]; + std::string backup = file + ".bak"; + + FILE *fp = fopen(file.c_str(), "r"); + if (!fp) { + printf("Cannot open file %s for reading: %s\n", file.c_str(), strerror(errno)); + return 2; + } + + auto jsondata = json::parse(fp, nullptr, true, true); + fclose(fp); + + rename(file.c_str(), backup.c_str()); + + fp = fopen(file.c_str(), "w"); + if (!fp) { + printf("Cannot open file %s for writing: %s\n", file.c_str(), strerror(errno)); + return 3; + } + std::string data = jsondata.dump(indent); + data += '\n'; + fputs(data.c_str(), fp); + fclose(fp); + } + return 0; +} diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index b396a01494..5d3f3c0bb3 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.6.13 LANGUAGES CXX) +project(lammps-gui VERSION 1.6.14 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) diff --git a/tools/lammps-gui/help_index.table b/tools/lammps-gui/help_index.table index d859fac2a6..1dfd361805 100644 --- a/tools/lammps-gui/help_index.table +++ b/tools/lammps-gui/help_index.table @@ -482,6 +482,7 @@ fix_aveforce.html fix aveforce fix_ave_grid.html fix ave/grid fix_ave_histo.html fix ave/histo fix_ave_histo.html fix ave/histo/weight +fix_ave_moments.html fix ave/moments fix_ave_time.html fix ave/time fix_balance.html fix balance fix_bocs.html fix bocs @@ -542,6 +543,7 @@ fix_flow_gauss.html fix flow/gauss fix_freeze.html fix freeze fix_freeze.html fix freeze/kk fix_gcmc.html fix gcmc +fix_gjf.html fix gjf fix_gld.html fix gld fix_gle.html fix gle fix_gravity.html fix gravity @@ -732,6 +734,7 @@ fix_saed_vtk.html fix saed/vtk fix_setforce.html fix setforce fix_setforce.html fix setforce/kk fix_setforce.html fix setforce/spin +fix_set.html fix set fix_sgcmc.html fix sgcmc fix_shake.html fix rattle fix_shake.html fix shake diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index 3061e973ea..03e85237e4 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -55,6 +55,11 @@ + + + Must set en_US.UTF-8 locale on macOS since it lacks support for C.UTF-8 + + Change working directory to user's home dir if initial directory is "/" or the Application folder @@ -68,7 +73,7 @@ Double-click on highlighted URL opens it in web browser. Also available via context menu. - + Update Tutorial menu entries to cover all 8 tutorials Highlight warnings and error messages in Output window diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index 5f2599c3df..1cd5d41532 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -36,8 +36,13 @@ int main(int argc, char *argv[]) qRegisterMetaTypeStreamOperators>("QList"); #endif +#ifndef Q_OS_MACOS // enforce using the plain ASCII C locale with UTF-8 encoding within the GUI. qputenv("LC_ALL", "C.UTF-8"); +#else + // macOS does not support "C" locale with UTF-8 encoding, but Qt requires UTF-8 + qputenv("LC_ALL", "en_US.UTF-8"); +#endif QApplication app(argc, argv); QCoreApplication::setOrganizationName("The LAMMPS Developers"); diff --git a/tools/magic b/tools/magic index 43e8affbd3..5a0c2ccfbb 100644 --- a/tools/magic +++ b/tools/magic @@ -1,30 +1,23 @@ -# Add the contents of this file to either $HOME/.magic -# or /etc/magic so that the file(1) command can recognize + +# These definitions are included in file since version 5.41. +# https://www.darwinsys.com/file/ https://github.com/file/file +# If you have an older version of the software, add the +# contents of this file to either $HOME/.magic or +# /etc/magic so that the file(1) command can recognize # your LAMMPS files and print some useful information. -# Last change: 2021-03-15 by akohlmey@gmail.com +# +# Last change: 2025-05-28 by akohlmey@gmail.com -# Binary restart file for the LAMMPS MD code, https://www.lammps.org - -# written on a little endian machine +# Binary restart file for the LAMMPS MD code 0 string LammpS\ RestartT LAMMPS binary restart file ->16 lelong 0x0001 ->>20 lelong x (rev %d), ->>>32 string x Version %s, ->>>>&41 string x Units %s, ->>>>>&5 lequad x Timestep %lld, ->>>>>>0x10 lelong 0x0001 Little Endian - -# written on a big endian machine -0 string LammpS\ RestartT LAMMPS binary restart file ->16 lelong 0x1000 Big Endian ->>20 belong x (rev %d), ->>>32 string x Version %s, ->>>>&41 string x Units %s, ->>>>>&5 bequad x Timestep %lld, ->>>>>>0x10 lelong 0x1000 Big Endian +>0x14 long x (rev %d), +>>0x20 string x Version %s, +>>>&41 string x Units %s, +>>>>&5 quad x Timestep %lld, +>>>>>0x10 lelong 0x0001 Little Endian +>>>>>0x10 lelong 0x1000 Big Endian # Atom style binary dump file for the LAMMPS MD code, https://www.lammps.org - # written on a little endian machine 0 lequad -8 >0x08 string DUMPATOM LAMMPS atom style binary dump @@ -39,7 +32,7 @@ >>>0x10 lelong 0x1000 Big Endian, >>>>0x18 bequad x First time step: %lld -# Atom style binary dump file for the LAMMPS MD code, https://www.lammps.org +# Atom style binary dump file for the LAMMPS MD code # written on a little endian machine 0 lequad -10 >0x08 string DUMPCUSTOM LAMMPS custom style binary dump @@ -64,7 +57,7 @@ >0x12 string msi2lmp written by msi2lmp >0x11 string via\ write_data written by LAMMPS -# Data file written by OVITO +# LAMMPS data file written by OVITO 0 string #\ LAMMPS\ data\ file LAMMPS data file >0x13 string written\ by\ OVITO written by OVITO diff --git a/tools/singularity/fedora41_musl_mingw.def b/tools/singularity/fedora41_musl_mingw.def index 7e15f6590b..a2cf77dab8 100644 --- a/tools/singularity/fedora41_musl_mingw.def +++ b/tools/singularity/fedora41_musl_mingw.def @@ -2,8 +2,8 @@ BootStrap: docker From: fedora:41 %setup - curl -L -o musl-gcc-f37.tar.gz https://download.lammps.org/static/musl-gcc-f37.tar.gz - cp musl-gcc-f37.tar.gz ${APPTAINER_ROOTFS} + curl -L -o musl-gcc-f41.tar.gz https://download.lammps.org/static/musl-gcc-f41.tar.gz + cp musl-gcc-f41.tar.gz ${APPTAINER_ROOTFS} %post dnf -y update @@ -52,8 +52,8 @@ From: fedora:41 dnf clean all # install musl-libc Linux-2-Linux cross-compiler - tar -C /usr/ -xvf /musl-gcc-f37.tar.gz - rm -f /musl-gcc-f37.tar.gz + tar -C /usr/ -xvf /musl-gcc-f41.tar.gz + rm -f /musl-gcc-f41.tar.gz # install NSIS EnVar plugin curl -L -o EnVar_plugin.zip https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip diff --git a/tools/swig/lammps.i b/tools/swig/lammps.i index 283b5d8b4e..6761ce7acf 100644 --- a/tools/swig/lammps.i +++ b/tools/swig/lammps.i @@ -116,6 +116,7 @@ extern void lammps_mpi_init(); extern void lammps_mpi_finalize(); extern void lammps_kokkos_finalize(); extern void lammps_python_finalize(); +extern void lammps_plugin_finalize(); extern void lammps_error(void *handle, int error_type, const char *error_text); extern char *lammps_expand(void *handle, const char *line); @@ -315,6 +316,7 @@ extern void lammps_mpi_init(); extern void lammps_mpi_finalize(); extern void lammps_kokkos_finalize(); extern void lammps_python_finalize(); +extern void lammps_plugin_finalize(); extern void lammps_error(void *handle, int error_type, const char *error_text); extern char *lammps_expand(void *handle, const char *line); diff --git a/unittest/commands/test_set_property.cpp b/unittest/commands/test_set_property.cpp index c306ea4739..26635d225f 100644 --- a/unittest/commands/test_set_property.cpp +++ b/unittest/commands/test_set_property.cpp @@ -90,8 +90,9 @@ TEST_F(SetTest, NoBoxNoAtoms) ASSERT_EQ(compute->vector_atom[0], 0); TEST_FAILURE(".*ERROR: Illegal set command: need at least four.*", command("set type 1 x");); - TEST_FAILURE(".*ERROR: Unknown set command style: xxx.*", command("set xxx 1 x 0.0");); - TEST_FAILURE(".*ERROR: Set keyword or custom property yyy does not exist.*", + TEST_FAILURE(".*ERROR: Unknown set or fix set command style: xxx.*", + command("set xxx 1 x 0.0");); + TEST_FAILURE(".*ERROR: Unrecognized set or fix set command keyword yyy.*", command("set type 1 yyy 0.0");); TEST_FAILURE(".*ERROR: Cannot set attribute spin/atom for atom style atomic.*", @@ -447,9 +448,9 @@ TEST_F(SetTest, EffPackage) EXPECT_EQ(compute->array_atom[6][1], 0.5); EXPECT_EQ(compute->array_atom[7][1], 1.0); - TEST_FAILURE(".*ERROR on proc 0: Incorrect value for electron spin: 0.5.*", + TEST_FAILURE(".*Expected integer parameter instead of '0.5' in input script.*", command("set atom * spin/electron 0.5");); - TEST_FAILURE(".*ERROR on proc 0: Incorrect value for electron radius: -0.5.*", + TEST_FAILURE(".*ERROR on proc 0: Invalid electron radius -0.5 in set.*", command("set atom * radius/electron -0.5");); } diff --git a/unittest/cplusplus/test_advanced_utils.cpp b/unittest/cplusplus/test_advanced_utils.cpp index 468f353080..e863296e3e 100644 --- a/unittest/cplusplus/test_advanced_utils.cpp +++ b/unittest/cplusplus/test_advanced_utils.cpp @@ -153,7 +153,7 @@ TEST_F(Advanced_utils, expand_args) // disable use of input->command and input->arg which point to the last run command right now lmp->input->command = nullptr; - lmp->input->arg = nullptr; + lmp->input->arg = nullptr; auto narg = utils::expand_args(FLERR, oarg, args, 0, earg, lmp); EXPECT_EQ(narg, 16); @@ -214,8 +214,13 @@ TEST_F(Advanced_utils, expand_args) args[5][7] = '3'; delete[] args[4]; args[4] = utils::strdup("v_temp[2*]"); + TEST_FAILURE("ERROR: Upper bound required to expand vector style variable temp.*", + utils::expand_args(FLERR, oarg, args, 0, earg, lmp);); + + delete[] args[4]; + args[4] = utils::strdup("v_temp[*2]"); narg = utils::expand_args(FLERR, oarg, args, 0, earg, lmp); - EXPECT_EQ(narg, 13); + EXPECT_EQ(narg, 14); EXPECT_STREQ(earg[0], "v_step"); EXPECT_STREQ(earg[1], "c_temp"); EXPECT_STREQ(earg[2], "f_1[1]"); @@ -224,11 +229,12 @@ TEST_F(Advanced_utils, expand_args) EXPECT_STREQ(earg[5], "c_temp[2]"); EXPECT_STREQ(earg[6], "c_temp[3]"); EXPECT_STREQ(earg[7], "c_temp[4]"); - EXPECT_STREQ(earg[8], "v_temp[2*]"); - EXPECT_STREQ(earg[9], "c_gofr[3*]"); - EXPECT_STREQ(earg[10], "c_gofr[1][*]"); - EXPECT_STREQ(earg[11], "c_gofr[*2][2]"); - EXPECT_STREQ(earg[12], "c_gofr[*][*]"); + EXPECT_STREQ(earg[8], "v_temp[1]"); + EXPECT_STREQ(earg[9], "v_temp[2]"); + EXPECT_STREQ(earg[10], "c_gofr[3*]"); + EXPECT_STREQ(earg[11], "c_gofr[1][*]"); + EXPECT_STREQ(earg[12], "c_gofr[*2][2]"); + EXPECT_STREQ(earg[13], "c_gofr[*][*]"); for (int i = 0; i < narg; ++i) delete[] earg[i]; diff --git a/unittest/force-styles/tests/angle-cosine_delta.yaml b/unittest/force-styles/tests/angle-cosine_delta.yaml index 0ac97119e7..db0a05438c 100644 --- a/unittest/force-styles/tests/angle-cosine_delta.yaml +++ b/unittest/force-styles/tests/angle-cosine_delta.yaml @@ -1,6 +1,5 @@ --- lammps_version: 4 Feb 2025 -tags: generated date_generated: Fri Feb 21 16:17:55 2025 epsilon: 5e-13 skip_tests: numdiff diff --git a/unittest/force-styles/tests/angle-mwlc.yaml b/unittest/force-styles/tests/angle-mwlc.yaml index dc4d817456..707f6ff881 100644 --- a/unittest/force-styles/tests/angle-mwlc.yaml +++ b/unittest/force-styles/tests/angle-mwlc.yaml @@ -1,6 +1,5 @@ --- lammps_version: 19 Nov 2024 -tags: generated date_generated: Fri Jan 10 13:54:41 2025 epsilon: 2.5e-13 skip_tests: diff --git a/unittest/force-styles/tests/bond-fene.yaml b/unittest/force-styles/tests/bond-fene.yaml index e5077eda0e..b9430a75fc 100644 --- a/unittest/force-styles/tests/bond-fene.yaml +++ b/unittest/force-styles/tests/bond-fene.yaml @@ -7,7 +7,8 @@ prerequisites: ! | atom full bond fene pre_commands: ! "" -post_commands: ! "" +post_commands: ! | + special_bonds lj/coul 0.0 1.0 1.0 input_file: in.fourmol bond_style: fene bond_coeff: ! | diff --git a/unittest/force-styles/tests/bond-fene_expand.yaml b/unittest/force-styles/tests/bond-fene_expand.yaml index 250f89af15..2be87fc3d3 100644 --- a/unittest/force-styles/tests/bond-fene_expand.yaml +++ b/unittest/force-styles/tests/bond-fene_expand.yaml @@ -7,7 +7,8 @@ prerequisites: ! | atom full bond fene/expand pre_commands: ! "" -post_commands: ! "" +post_commands: ! | + special_bonds lj/coul 0.0 1.0 1.0 input_file: in.fourmol bond_style: fene/expand bond_coeff: ! | diff --git a/unittest/force-styles/tests/bond-fene_nm.yaml b/unittest/force-styles/tests/bond-fene_nm.yaml index 892d26c7aa..77f79fe780 100644 --- a/unittest/force-styles/tests/bond-fene_nm.yaml +++ b/unittest/force-styles/tests/bond-fene_nm.yaml @@ -7,7 +7,8 @@ prerequisites: ! | atom full bond fene/nm pre_commands: ! "" -post_commands: ! "" +post_commands: ! | + special_bonds lj/coul 0.0 1.0 1.0 input_file: in.fourmol bond_style: fene/nm bond_coeff: ! | diff --git a/unittest/force-styles/tests/fix-timestep-efield_lepton.yaml b/unittest/force-styles/tests/fix-timestep-efield_lepton.yaml index 7928c81c45..a3539e1af1 100644 --- a/unittest/force-styles/tests/fix-timestep-efield_lepton.yaml +++ b/unittest/force-styles/tests/fix-timestep-efield_lepton.yaml @@ -1,6 +1,5 @@ --- lammps_version: 29 Aug 2024 -tags: generated date_generated: Tue Nov 12 08:53:18 2024 epsilon: 2e-13 skip_tests: diff --git a/unittest/force-styles/tests/fix-timestep-efield_lepton_region.yaml b/unittest/force-styles/tests/fix-timestep-efield_lepton_region.yaml index f0a6b121a4..b7be61782e 100644 --- a/unittest/force-styles/tests/fix-timestep-efield_lepton_region.yaml +++ b/unittest/force-styles/tests/fix-timestep-efield_lepton_region.yaml @@ -1,6 +1,5 @@ --- lammps_version: 29 Aug 2024 -tags: generated date_generated: Tue Nov 12 08:53:32 2024 epsilon: 2e-13 skip_tests: diff --git a/unittest/force-styles/tests/mol-pair-lj_pirani.yaml b/unittest/force-styles/tests/mol-pair-lj_pirani.yaml index d745fd788b..06e7940c67 100644 --- a/unittest/force-styles/tests/mol-pair-lj_pirani.yaml +++ b/unittest/force-styles/tests/mol-pair-lj_pirani.yaml @@ -1,6 +1,5 @@ --- lammps_version: 2 Apr 2025 -tags: generated date_generated: Fri Apr 18 00:29:06 2025 epsilon: 5e-14 skip_tests: