From 4ec05d73328972fa40c2114364e8dcba85707bea Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:50 -0400 Subject: [PATCH 01/14] SPIN: free rsec before re-allocating it in fix nve/spin init() FixNVESpin::init() runs once per run command, so every run after the first orphaned the previous rsec allocation. --- src/SPIN/fix_nve_spin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SPIN/fix_nve_spin.cpp b/src/SPIN/fix_nve_spin.cpp index 8a12f715d5e..9eae606ba0c 100644 --- a/src/SPIN/fix_nve_spin.cpp +++ b/src/SPIN/fix_nve_spin.cpp @@ -297,6 +297,7 @@ void FixNVESpin::init() // setting the sector variables/lists nsectors = 0; + memory->destroy(rsec); memory->create(rsec,3,"nve/spin:rsec"); // perform the sectoring operation From f88168e20567aee63224a680a7d97dba542fb6ad Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 02/14] OPENMP: initialize the fp array in pair style local/density/omp The second pass assigns fp only for those local densities that apply to the type of the central atom, but the force loop reads all of them, so the array must be zeroed first. The non-threaded pair style does this already. --- src/OPENMP/pair_local_density_omp.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/OPENMP/pair_local_density_omp.cpp b/src/OPENMP/pair_local_density_omp.cpp index 909569c7fdc..5b2fab5e330 100644 --- a/src/OPENMP/pair_local_density_omp.cpp +++ b/src/OPENMP/pair_local_density_omp.cpp @@ -73,6 +73,15 @@ void PairLocalDensityOMP::compute(int eflag, int vflag) for (int k = 0; k < nLD; k++) memset(&localrho[k][tid*nall], 0, nall * sizeof(double)); + // zero fp. pass 2 assigns it only for those local densities that apply + // to the type of the central atom, while pass 3 reads all of them. + // fp is shared, so each thread only zeroes its own chunk of atoms. + + int kfrom, kto; + loop_setup_thr(kfrom, kto, tid, nall, nthreads); + for (int k = 0; k < nLD; k++) + memset(&fp[k][kfrom], 0, (kto-kfrom) * sizeof(double)); + // ------------------------------------------------------------------- // Pass 1: compute per-thread local densities From 3f70c441a6dd1f44d31e868fa2adc9cb0c38736d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 03/14] MANYBODY: free arrays before re-reading a local/density potential file A second pair_coeff command orphaned the entire set of tabulated arrays. --- src/MANYBODY/pair_local_density.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/MANYBODY/pair_local_density.cpp b/src/MANYBODY/pair_local_density.cpp index 411f357bd1b..d785c2ef697 100644 --- a/src/MANYBODY/pair_local_density.cpp +++ b/src/MANYBODY/pair_local_density.cpp @@ -690,6 +690,22 @@ void PairLocalDensity::parse_file(char *filename) { double *ftmp; // tmp var to extract the complete 2D frho array from file // setting up all arrays to be read from files and broadcasted + // free any storage from a previous pair_coeff command first + + memory->destroy(uppercut); + memory->destroy(lowercut); + memory->destroy(uppercutsq); + memory->destroy(lowercutsq); + memory->destroy(c0); + memory->destroy(c2); + memory->destroy(c4); + memory->destroy(c6); + memory->destroy(rho_min); + memory->destroy(rho_max); + memory->destroy(delta_rho); + memory->destroy(a); + memory->destroy(b); + memory->create(uppercut, nLD, "pairLD:uppercut"); memory->create(lowercut, nLD, "pairLD:lowercut"); memory->create(uppercutsq, nLD, "pairLD:uppercutsq"); @@ -804,6 +820,8 @@ void PairLocalDensity::parse_file(char *filename) { MPI_Bcast(&ftmp[0], nLD*nrho, MPI_DOUBLE, 0, world); // set up rho and frho arrays + memory->destroy(rho); + memory->destroy(frho); memory->create(rho, nLD, nrho, "pairLD:rho"); memory->create(frho, nLD, nrho, "pairLD:frho"); From 83ece4d3db7ac8cd45a74cffcf6374c3c2c01f1d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 04/14] MC: free cell arrays before re-allocating them in pair dsmc init_style() --- src/MC/pair_dsmc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/MC/pair_dsmc.cpp b/src/MC/pair_dsmc.cpp index 73f5eb7fc7c..ab50a29836a 100644 --- a/src/MC/pair_dsmc.cpp +++ b/src/MC/pair_dsmc.cpp @@ -293,6 +293,12 @@ void PairDSMC::init_style() total_ncells = ncellsx*ncellsy*ncellsz; vol = cellx*celly*cellz; + // free storage from a previous init_style() call + + memory->destroy(particle_list); + memory->destroy(first); + memory->destroy(number); + memory->create(particle_list,atom->ntypes+1,0,"pair:particle_list"); memory->create(first,atom->ntypes+1,total_ncells,"pair:first"); memory->create(number,atom->ntypes+1,total_ncells,"pair:number"); From e53c1973231915ee09f52635f3f022bf201c6b1c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 05/14] DPD-MESO, DPD-SMOOTH: delete random number generators before replacing them A second pair_style command leaked the previous generator: randomT in pair style edpd, random in pair style sdpd/taitwater/isothermal. --- src/DPD-MESO/pair_edpd.cpp | 1 + src/DPD-SMOOTH/pair_sdpd_taitwater_isothermal.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/DPD-MESO/pair_edpd.cpp b/src/DPD-MESO/pair_edpd.cpp index 2df3bea181f..e8e7c4e534f 100644 --- a/src/DPD-MESO/pair_edpd.cpp +++ b/src/DPD-MESO/pair_edpd.cpp @@ -283,6 +283,7 @@ void PairEDPD::settings(int narg, char **arg) delete random; random = new RanMars(lmp,(seed + comm->me) % 900000000); + delete randomT; randomT = new RanMars(lmp,(2*seed + comm->me) % 900000000); // reset cutoffs that have been explicitly set diff --git a/src/DPD-SMOOTH/pair_sdpd_taitwater_isothermal.cpp b/src/DPD-SMOOTH/pair_sdpd_taitwater_isothermal.cpp index 1716fb99a45..e686c075433 100644 --- a/src/DPD-SMOOTH/pair_sdpd_taitwater_isothermal.cpp +++ b/src/DPD-SMOOTH/pair_sdpd_taitwater_isothermal.cpp @@ -259,6 +259,7 @@ void PairSDPDTaitwaterIsothermal::settings (int narg, char **arg) { #ifdef USE_ZEST generator.seed (seed); #else + delete random; random = new RanMars (lmp, seed); #endif } From fad6dfb988e75fffb6e5436683a758c8ce2fe4e4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 06/14] DIFFRACTION: assign the ntypes class member in compute xrd and saed The constructors declared a local variable of the same name, so the member stayed uninitialized until the first compute, while memory_usage() reads it as early as the first thermo output. --- src/DIFFRACTION/compute_saed.cpp | 2 +- src/DIFFRACTION/compute_xrd.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DIFFRACTION/compute_saed.cpp b/src/DIFFRACTION/compute_saed.cpp index c84f4c146f0..9a1108de064 100644 --- a/src/DIFFRACTION/compute_saed.cpp +++ b/src/DIFFRACTION/compute_saed.cpp @@ -55,7 +55,7 @@ ComputeSAED::ComputeSAED(LAMMPS *lmp, int narg, char **arg) : nlocalgroup = 0; if (lmp->citeme) lmp->citeme->add(cite_compute_saed_c); - int ntypes = atom->ntypes; + ntypes = atom->ntypes; int natoms = group->count(igroup); int dimension = domain->dimension; int *periodicity = domain->periodicity; diff --git a/src/DIFFRACTION/compute_xrd.cpp b/src/DIFFRACTION/compute_xrd.cpp index 18fb8a25866..6e4594f5064 100644 --- a/src/DIFFRACTION/compute_xrd.cpp +++ b/src/DIFFRACTION/compute_xrd.cpp @@ -56,7 +56,7 @@ ComputeXRD::ComputeXRD(LAMMPS *lmp, int narg, char **arg) : nlocalgroup = 0; if (lmp->citeme) lmp->citeme->add(cite_compute_xrd_c); - int ntypes = atom->ntypes; + ntypes = atom->ntypes; int natoms = group->count(igroup); int dimension = domain->dimension; int *periodicity = domain->periodicity; From 0644b1ad10148ca2f11370cb887d564def1bc029 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 07/14] KSPACE: print pppm/dipole/spin grid statistics only after allocating the grid ngrid is set by allocate(), which ran after the statistics were printed, so the reported number of 3d grid values was undefined on the first run. Both PPPM and PPPMDipole have these two blocks in the opposite order. --- src/KSPACE/pppm.cpp | 2 +- src/KSPACE/pppm_dipole_spin.cpp | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 5af807113fb..04eb0195ef9 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -98,7 +98,7 @@ PPPM::PPPM(LAMMPS *lmp) : KSpace(lmp), MPI_Comm_rank(world,&me); MPI_Comm_size(world,&nprocs); - nfft_both = 0; + ngrid = nfft_both = 0; nxhi_in = nxlo_in = nxhi_out = nxlo_out = 0; nyhi_in = nylo_in = nyhi_out = nylo_out = 0; nzhi_in = nzlo_in = nzhi_out = nzlo_out = 0; diff --git a/src/KSPACE/pppm_dipole_spin.cpp b/src/KSPACE/pppm_dipole_spin.cpp index 40a6dda9d41..c89fb2b8b0a 100644 --- a/src/KSPACE/pppm_dipole_spin.cpp +++ b/src/KSPACE/pppm_dipole_spin.cpp @@ -202,6 +202,19 @@ void PPPMDipoleSpin::init() double estimated_accuracy = final_accuracy_dipole(); + // allocate K-space dependent memory + // don't invoke allocate peratom(), will be allocated when needed + // must happen before printing the stats below, since allocate() + // is what sets ngrid and nfft_both + + allocate(); + + // pre-compute Green's function denominator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + compute_rho_coeff(); + // print stats int ngrid_max,nfft_both_max; @@ -221,17 +234,6 @@ void PPPMDipoleSpin::init() ngrid_max,nfft_both_max); utils::logmesg(lmp,mesg); } - - // allocate K-space dependent memory - // don't invoke allocate peratom(), will be allocated when needed - - allocate(); - - // pre-compute Green's function denominator expansion - // pre-compute 1d charge distribution coefficients - - compute_gf_denom(); - compute_rho_coeff(); } /* ---------------------------------------------------------------------- From 17899b4b2c4b4a2be16d5fe4a83d1d9d1a16fb16 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 08/14] initialize radius of superellipsoid atoms read from a data file Atoms without an entry in the Ellipsoids section kept an undefined radius, which was then written to restart and dump files. --- src/atom_vec_ellipsoid.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/atom_vec_ellipsoid.cpp b/src/atom_vec_ellipsoid.cpp index d520bf88b52..3a015e00130 100644 --- a/src/atom_vec_ellipsoid.cpp +++ b/src/atom_vec_ellipsoid.cpp @@ -682,6 +682,10 @@ void AtomVecEllipsoid::data_atom_post(int ilocal) if (rmass[ilocal] <= 0.0) error->one(FLERR, "Invalid density in Atoms section of data file"); + // data_atom_bonus() overrides this for atoms listed in the Ellipsoids section + + if (atom->superellipsoid_flag) radius[ilocal] = 0.0; + angmom[ilocal][0] = 0.0; angmom[ilocal][1] = 0.0; angmom[ilocal][2] = 0.0; From d2d917b6a91bd36d915ce5d597d810abfbb2229f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:51 -0400 Subject: [PATCH 09/14] EFF: initialize the ECP parameter arrays in pair style eff/cut write_restart_settings() stores all 100 entries, not only those set with the ecp keyword, so it wrote undefined data to the restart file. --- src/EFF/pair_eff_cut.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/EFF/pair_eff_cut.h b/src/EFF/pair_eff_cut.h index 89c9376c0af..f76de25dc03 100644 --- a/src/EFF/pair_eff_cut.h +++ b/src/EFF/pair_eff_cut.h @@ -49,9 +49,12 @@ class PairEffCut : public Pair { int ecp_found; double cut_global; double **cut; - int ecp_type[100]; - double PAULI_CORE_A[100], PAULI_CORE_B[100], PAULI_CORE_C[100]; - double PAULI_CORE_D[100], PAULI_CORE_E[100]; + // only the entries of ECP types set with the "ecp" keyword are filled in, + // but write_restart_settings() stores the arrays as a whole, so they must + // not contain uninitialized data + int ecp_type[100] = {}; + double PAULI_CORE_A[100] = {}, PAULI_CORE_B[100] = {}, PAULI_CORE_C[100] = {}; + double PAULI_CORE_D[100] = {}, PAULI_CORE_E[100] = {}; double hhmss2e, h2e; int nmax; From b00b335f0470f715e75aaae82807262d7a180f27 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:52 -0400 Subject: [PATCH 10/14] ML-QUIP: free storage before replacing it in PairQUIP::coeff() --- src/ML-QUIP/pair_quip.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ML-QUIP/pair_quip.cpp b/src/ML-QUIP/pair_quip.cpp index 7f8e6fd6af8..dc70794846b 100644 --- a/src/ML-QUIP/pair_quip.cpp +++ b/src/ML-QUIP/pair_quip.cpp @@ -251,7 +251,9 @@ void PairQUIP::coeff(int narg, char **arg) } // use expanded file name, including LAMMPS_POTENTIALS search path + delete[] quip_file; quip_file = utils::strdup(utils::get_potential_file_path(arg[2])); + delete[] quip_string; quip_string = utils::strdup(arg[3]); n_quip_file = strlen(quip_file); n_quip_string = strlen(quip_string); @@ -286,6 +288,7 @@ void PairQUIP::coeff(int narg, char **arg) // and returns the necessary size of quip_potential. This behavior // is invoked by setting n_potential_quip to 0. n_quip_potential = 0; + delete[] quip_potential; quip_potential = new int[1]; quip_lammps_potential_initialise(quip_potential, &n_quip_potential, &cutoff, quip_file, &n_quip_file, quip_string, &n_quip_string); From 09208c9e8db64d24028a7165fc2fbe9b6b08b83e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:52 -0400 Subject: [PATCH 11/14] delete_atoms: free the tagproc buffer after condensing atom IDs --- src/delete_atoms.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/delete_atoms.cpp b/src/delete_atoms.cpp index 2c366a73bcd..7ab1dfc7c4b 100644 --- a/src/delete_atoms.cpp +++ b/src/delete_atoms.cpp @@ -237,6 +237,13 @@ void DeleteAtoms::command(int narg, char **arg) // overwrite tags with condensed values for (int i = 0; i < nlocal; ++i) tag[i] = newtags[i]; + // tagproc is only needed by the settags() callback above + // newtags points to mynewtags and thus must not be deleted + + delete[] tagproc; + tagproc = nullptr; + newtags = nullptr; + // update atom map since we changed the tags atom->map_tag_max = -1; atom->map_style_set(); From 2812bc50829251cbb2a95a31e88ec29196d9bb16 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:52 -0400 Subject: [PATCH 12/14] restore the Input class pointers in the expand_args test Clearing input->arg orphaned the argument list that Input frees on delete. --- unittest/cplusplus/test_advanced_utils.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/unittest/cplusplus/test_advanced_utils.cpp b/unittest/cplusplus/test_advanced_utils.cpp index beab937ff16..1a595201fa9 100644 --- a/unittest/cplusplus/test_advanced_utils.cpp +++ b/unittest/cplusplus/test_advanced_utils.cpp @@ -163,6 +163,9 @@ TEST_F(AdvancedUtils, expand_args) args[8] = utils::strdup("c_gofr[*][*]"); // disable use of input->command and input->arg which point to the last run command right now + // must be restored afterwards, or Input cannot free its argument list + char *saved_command = lmp->input->command; + char **saved_arg = lmp->input->arg; lmp->input->command = nullptr; lmp->input->arg = nullptr; @@ -253,6 +256,9 @@ TEST_F(AdvancedUtils, expand_args) for (int i = 0; i < oarg; ++i) delete[] args[i]; delete[] args; + + lmp->input->command = saved_command; + lmp->input->arg = saved_arg; } TEST_F(AdvancedUtils, check_packages_for_style) From a97e3c6bbaba6c94be7cdb3c76b03dd5ba121a43 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 07:22:52 -0400 Subject: [PATCH 13/14] do not report still reachable blocks when running ctest -T memcheck Memory that is still reachable when the process exits is not a leak. Those reports bury the actual defects under thousands of one-time initializations in OpenSSL, libcurl, CPython and GoogleTest and under everything that is still in use when a command like quit terminates the process on purpose. MEMORYCHECK_COMMAND_OPTIONS is a cache variable, so existing build folders keep their current setting and have to be updated explicitly with cmake -D MEMORYCHECK_COMMAND_OPTIONS="..." --- cmake/Modules/Testing.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/Modules/Testing.cmake b/cmake/Modules/Testing.cmake index 732613605ea..e83fc940343 100644 --- a/cmake/Modules/Testing.cmake +++ b/cmake/Modules/Testing.cmake @@ -11,7 +11,11 @@ if(ENABLE_TESTING) file(READ ${SUPP} SUPPRESSIONS) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/lammps.supp "${SUPPRESSIONS}") endforeach() - set(VALGRIND_DEFAULT_OPTIONS "--leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=${CMAKE_BINARY_DIR}/lammps.supp") + # blocks that are still reachable when the process exits are not leaks. reporting + # them buries the actual defects under thousands of one-time initializations in + # OpenSSL, libcurl, CPython, and GoogleTest, plus everything that is still in use + # when a command like "quit" or an error exit terminates the process on purpose. + set(VALGRIND_DEFAULT_OPTIONS "--leak-check=full --show-leak-kinds=definite,indirect,possible --track-origins=yes --suppressions=${CMAKE_BINARY_DIR}/lammps.supp") set(MEMORYCHECK_COMMAND "${VALGRIND_BINARY}" CACHE FILEPATH "Memory Check Command") set(MEMORYCHECK_COMMAND_OPTIONS "${VALGRIND_DEFAULT_OPTIONS}" CACHE STRING "Memory Check Command Options") From 02639e1af97e68dd4bc23d2f03b37481d2619ebb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 26 Jul 2026 09:11:28 -0400 Subject: [PATCH 14/14] also reflect updated command line in tools/valgrind/README --- tools/valgrind/README | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/valgrind/README b/tools/valgrind/README index 1b53347c480..b1402624420 100644 --- a/tools/valgrind/README +++ b/tools/valgrind/README @@ -5,7 +5,8 @@ and fixing real issues. When using CMake, these are automatically included when running "ctest -T memcheck". To manually add them to do a memory check on running LAMMPS, use a command line like following: -valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \ +valgrind --leak-check=full --track-origins=yes \ + --show-leak-kinds=definite,indirect,possible \ --suppressions=/path/to/lammps/tools/valgrind/FlexiBLAS.supp \ --suppressions=/path/to/lammps/tools/valgrind/GTest.supp \ --suppressions=/path/to/lammps/tools/valgrind/Kokkos.supp \ @@ -21,7 +22,7 @@ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \ Or you can create a file $HOME/.valgrindrc with one option per line: --leak-check=full ---show-leak-kinds=all +--show-leak-kinds=definite,indirect,possible --track-origins=yes --suppressions=/path/to/lammps/tools/valgrind/FlexiBLAS.supp --suppressions=/path/to/lammps/tools/valgrind/GTest.supp @@ -37,4 +38,4 @@ Or you can create a file $HOME/.valgrindrc with one option per line: These options will be automatically added to the valgrind command line, so it becomes: valgrind lmp -in in.melt -Last update: 2026-06-27 +Last update: 2026-07-26