From 43048811ddc69dd3b90789bdefc329d56be4e693 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Tue, 24 May 2022 15:33:40 -0600 Subject: [PATCH 01/75] Build dbidrj array. --- examples/snap/compute.snap.dat | 59 +++++++ examples/snap/in.snap.compute | 6 +- python/lammps/mliap/__init__.py | 3 +- src/ML-SNAP/compute_snap.cpp | 271 ++++++++++++++++++++++++++++++-- src/ML-SNAP/compute_snap.h | 11 +- src/ML-SNAP/sna.cpp | 5 +- src/fix_move.cpp | 247 ++++++++++++++++++++++++++++- src/fix_move.h | 55 +++++++ 8 files changed, 628 insertions(+), 29 deletions(-) create mode 100644 examples/snap/compute.snap.dat diff --git a/examples/snap/compute.snap.dat b/examples/snap/compute.snap.dat new file mode 100644 index 0000000000..60aa40fdd6 --- /dev/null +++ b/examples/snap/compute.snap.dat @@ -0,0 +1,59 @@ +# Time-averaged data for fix snap +# TimeStep Number-of-rows +# Row c_snap[1] c_snap[2] c_snap[3] c_snap[4] c_snap[5] c_snap[6] c_snap[7] c_snap[8] c_snap[9] c_snap[10] c_snap[11] +0 55 +1 0 0 0 0 0 3.12659e+06 1.91282e+06 1.01756e+06 1.18149e+06 419003 2775.75 +2 0 0 0 0 0 0 -2617.97 -11804.8 -32003.5 -14156.5 -126.705 +3 0 0 0 0 0 0 -2414.16 -4239.67 -6275.15 -3852.23 -118.927 +4 0 0 0 0 0 0 2529.98 3883.7 6245.75 2522.89 103.66 +5 0 0 0 0 0 0 411.847 604.579 57.0959 1095.67 -188.806 +6 0 0 0 0 0 0 1541.86 4697.43 11841.7 5519.43 275.079 +7 0 0 0 0 0 0 -2870.68 -1447.5 4412.24 1032.92 -63.9586 +8 0 0 0 0 0 0 1193.62 7012.92 20475.9 9007.1 230.377 +9 0 0 0 0 0 0 4848.36 11241.9 22593.7 11630.3 42.8991 +10 0 0 0 0 0 0 -1770.07 -2679.25 -3788.5 -2555.62 -135.264 +11 0 0 0 0 0 0 -4969.62 -8016.32 -11201.8 -7220.33 -85.5022 +12 0 0 0 0 0 0 1641.76 3596.16 7806.47 3219.57 40.8509 +13 0 0 0 0 0 0 325.571 4349.75 13049 5826.43 27.2534 +14 0 0 0 0 0 0 5920.17 5611.27 846.546 2245.23 83.7477 +15 0 0 0 0 0 0 -888.529 -848.965 -1874.49 -290.268 -68.0047 +16 0 0 0 0 0 0 -1916.74 67.9945 4784.3 2143.56 -39.6058 +17 0 0 0 0 0 0 -4098.57 -10375.2 -22007.6 -10355 -200.101 +18 0 0 0 0 0 0 -2284.58 -6551.33 -15184.8 -7117.19 -67.4731 +19 0 0 0 0 0 0 -2737.86 -632.669 6669.64 2094.01 52.5289 +20 0 0 0 0 0 0 -2329.4 -41.9068 7566.17 1913.97 100.188 +21 0 0 0 0 0 0 -444.112 -2754.7 -8428.65 -3849.65 -122.932 +22 0 0 0 0 0 0 -70.5051 111.212 854.264 255.733 65.2259 +23 0 0 0 0 0 0 3554.61 12874.2 31397 14566.8 47.5973 +24 0 0 0 0 0 0 1865.24 2108.07 1180.27 1465.26 91.3443 +25 0 0 0 0 0 0 -889.973 2561.32 11256.4 4537.35 77.4022 +26 0 0 0 0 0 0 3550.36 106.913 -9710.14 -2944.98 144.241 +27 0 0 0 0 0 0 -4712.47 -8838.63 -14464.9 -8091.56 -224.069 +28 0 0 0 0 0 0 -2024.94 -4432.38 -9505.05 -4018.8 -207.602 +29 0 0 0 0 0 0 2379.69 4724.47 7670.76 5006.86 -23.6309 +30 0 0 0 0 0 0 376.992 1771.26 5976.85 2024.35 134.961 +31 0 0 0 0 0 0 1237.27 -1519.65 -9085.33 -3530.88 -43.4288 +32 0 0 0 0 0 0 583.161 6064.47 18404.5 7643.32 243.05 +33 0 0 0 0 0 0 -2538.86 -2021.15 691.987 -389.262 -141.239 +34 0 0 0 0 0 0 2885.38 5612.51 9715.93 5772.93 193.908 +35 0 0 0 0 0 0 -6048.23 -11209.3 -18774.1 -10567.4 -252.412 +36 0 0 0 0 0 0 -1418.32 -3619.88 -5764.64 -4231.84 203.031 +37 0 0 0 0 0 0 3007.44 1474.23 -3713.21 -994.284 140.462 +38 0 0 0 0 0 0 4888.42 4654.63 805.35 2190.37 43.3575 +39 0 0 0 0 0 0 969.58 3277.56 6218.65 3924.82 -58.9942 +40 0 0 0 0 0 0 2987.73 4234.51 5529.54 3085.54 43.2781 +41 0 0 0 0 0 0 810.067 -1872.94 -8730.18 -3125.43 -210.33 +42 0 0 0 0 0 0 2844.79 2986.48 1115.95 1588.01 123.161 +43 0 0 0 0 0 0 134.538 -4097.82 -14380.1 -6204.27 -19.7911 +44 0 0 0 0 0 0 -2999.2 -2447.09 1548.16 -1098.43 162.086 +45 0 0 0 0 0 0 -2288.5 -5930.54 -12773.2 -6503.71 -200.232 +46 0 0 0 0 0 0 -2625.62 -6290.98 -12970.9 -6562.73 -182.126 +47 0 0 0 0 0 0 -228.949 4114.07 13655.9 5798.77 32.8425 +48 0 0 0 0 0 0 2900.97 5126.05 7340.27 4953.94 90.5452 +49 0 0 0 0 0 0 1798.49 -1194.98 -9074.02 -3404.76 -11.9431 +50 0 0 0 0 0 0 -3.09692e+06 -3.518e+06 -4.33318e+06 -2.30338e+06 1.32116e+08 +51 0 0 0 0 0 0 -3.10721e+06 -3.53165e+06 -4.34977e+06 -2.31581e+06 1.28785e+08 +52 0 0 0 0 0 0 -3.10871e+06 -3.53788e+06 -4.36295e+06 -2.32103e+06 1.4248e+08 +53 0 0 0 0 0 0 3585.35 6805.98 11450.9 6458.62 914589 +54 0 0 0 0 0 0 -6674.27 -11551.6 -17884.1 -10474.7 -2.08251e+06 +55 0 0 0 0 0 0 -11913.9 -22733.1 -38858.2 -21261 -7.73337e+06 diff --git a/examples/snap/in.snap.compute b/examples/snap/in.snap.compute index b0c7314882..005ba8b1a3 100644 --- a/examples/snap/in.snap.compute +++ b/examples/snap/in.snap.compute @@ -3,7 +3,7 @@ # initialize simulation variable nsteps index 0 -variable nrep equal 1 +variable nrep equal 2 variable a equal 2.0 units metal @@ -81,7 +81,7 @@ thermo 100 # test output: 1: total potential energy # 2: xy component of stress tensor -# 3: Sum(B_{000}^i, all i of type 2) +# 3: Sum(B_{000}^i, all i of type 2) # 4: xz component of Sum(Sum(r_j*dB_{222}^i/dR[j]), all i of type 2), all j) # 5: y component of -Sum(d(B_{222}^i)/dR[2]), all i of type 2) # @@ -89,7 +89,7 @@ thermo 100 thermo_style custom & pe pxy c_bsum2[1] c_vbsum[55] v_db_2_25 & - c_snap[1][11] c_snap[13][11] c_snap[1][6] c_snap[12][10] c_snap[6][10] + c_snap[1][11] c_snap[13][11] c_snap[1][6] c_snap[12][10] c_snap[6][10] thermo_modify norm no # dump mydump_db all custom 1000 dump_db id c_db[*] diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py index 57fe97d803..bbb5bb51a5 100644 --- a/python/lammps/mliap/__init__.py +++ b/python/lammps/mliap/__init__.py @@ -4,7 +4,8 @@ # try to improperly start up a new interpreter. import sysconfig import ctypes -library = sysconfig.get_config_vars('INSTSONAME')[0] +#library = sysconfig.get_config_vars('INSTSONAME')[0] +library="/usr/local/Cellar/python@3.10/3.10.2/Frameworks/Python.framework/Versions/3.10/Python" try: pylib = ctypes.CDLL(library) except OSError as e: diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 83f56e338c..70464420c2 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -56,6 +56,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : bzeroflag = 1; quadraticflag = 0; bikflag = 0; + dbirjflag = 0; chemflag = 0; bnormflag = 0; wselfallflag = 0; @@ -81,6 +82,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : memory->create(cutsq,ntypes+1,ntypes+1,"snap:cutsq"); for (int i = 1; i <= ntypes; i++) { cut = 2.0*radelem[i]*rcutfac; + printf("cut: %f\n", cut); if (cut > cutmax) cutmax = cut; cutsq[i][i] = cut*cut; for (int j = i+1; j <= ntypes; j++) { @@ -147,6 +149,11 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : error->all(FLERR,"Illegal compute snap command"); bikflag = atoi(arg[iarg+1]); iarg += 2; + } else if (strcmp(arg[iarg],"dbirjflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + dbirjflag = atoi(arg[iarg+1]); + iarg += 2; } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute snap command"); @@ -194,7 +201,9 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : natoms = atom->natoms; bik_rows = 1; if (bikflag) bik_rows = natoms; - size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; + //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; + dbirj_rows = ndims_force*natoms; + size_array_rows = bik_rows+dbirj_rows+ndims_virial; size_array_cols = nperdim*atom->ntypes+1; lastcol = size_array_cols-1; @@ -222,6 +231,14 @@ ComputeSnap::~ComputeSnap() memory->destroy(sinnerelem); memory->destroy(dinnerelem); } + + if (dbirjflag){ + printf("dbirj_rows: %d\n", dbirj_rows); + memory->destroy(dbirj); + memory->destroy(nneighs); + memory->destroy(neighsum); + memory->destroy(icounter); + } } /* ---------------------------------------------------------------------- */ @@ -231,8 +248,10 @@ void ComputeSnap::init() if (force->pair == nullptr) error->all(FLERR,"Compute snap requires a pair style be defined"); - if (cutmax > force->pair->cutforce) + if (cutmax > force->pair->cutforce){ + //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); + } // need an occasional full neighbor list @@ -243,7 +262,7 @@ void ComputeSnap::init() snaptr->init(); // allocate memory for global array - + printf("----- dbirjflag: %d\n", dbirjflag); memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, @@ -283,6 +302,13 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { + if (dbirjflag){ + printf("----- dbirjflag true.\n"); + get_dbirj_length(); + printf("----- got dbirj_length\n"); + } + printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); + //else{ int ntotal = atom->nlocal + atom->nghost; invoked_array = update->ntimestep; @@ -295,22 +321,22 @@ void ComputeSnap::compute_array() memory->create(snap_peratom,nmax,size_peratom, "snap:snap_peratom"); } - // clear global array - - for (int irow = 0; irow < size_array_rows; irow++) - for (int icoeff = 0; icoeff < size_array_cols; icoeff++) + printf("size_array_rows: %d\n", size_array_rows); + for (int irow = 0; irow < size_array_rows; irow++){ + for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ + //printf("%d %d\n", irow, icoeff); snap[irow][icoeff] = 0.0; + } + } // clear local peratom array - for (int i = 0; i < ntotal; i++) for (int icoeff = 0; icoeff < size_peratom; icoeff++) { snap_peratom[i][icoeff] = 0.0; } // invoke full neighbor list (will copy or build if necessary) - neighbor->build_one(list); const int inum = list->inum; @@ -324,11 +350,17 @@ void ComputeSnap::compute_array() double** const x = atom->x; const int* const mask = atom->mask; - + //printf("----- inum: %d\n", inum); + //printf("----- NEIGHMASK: %d\n", NEIGHMASK); + int ninside; + int numneigh_sum = 0; for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; + printf("----- ii, ilist, itag, irow: %d %d %d %d\n", ii, ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]], irow); const int i = ilist[ii]; + //printf("----- ii, i: %d %d\n", ii, i); + //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); if (mask[i] & groupbit) { const double xtmp = x[i][0]; @@ -353,7 +385,13 @@ void ComputeSnap::compute_array() // typej = types of neighbors of I within cutoff // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi - int ninside = 0; + /* + This loop assigns quantities in snaptr. + snaptr is a SNA class instance, see sna.h + + */ + //int ninside = 0; + ninside=0; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; @@ -367,6 +405,7 @@ void ComputeSnap::compute_array() if (chemflag) jelem = map[jtype]; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { + //printf("cutsq: %f\n", cutsq[itype][jtype]); snaptr->rij[ninside][0] = delx; snaptr->rij[ninside][1] = dely; snaptr->rij[ninside][2] = delz; @@ -382,27 +421,94 @@ void ComputeSnap::compute_array() } } + /* + Now that we have assigned neighbor quantities with previous loop, we are ready to compute things. + Here we compute the wigner functions (U), Z is some other quantity, and bi is bispectrum. + */ snaptr->compute_ui(ninside, ielem); snaptr->compute_zi(); snaptr->compute_bi(ielem); + /* + Looks like this loop computes derivatives. + How does snaptr know what atom I we're dealing with? + I think it only needs neighbor info, and then it goes from there. + */ + //printf("----- Derivative loop - looping over neighbors j.\n"); + printf("----- ninside: %d\n", ninside); // numneighs of I within cutoff for (int jj = 0; jj < ninside; jj++) { + //printf("----- jj: %d\n", jj); const int j = snaptr->inside[jj]; + //printf("----- jj, j, jtag: %d %d %d\n", jj, j, atom->tag[j]); + //int dbirj_row_indx = 3*neighsum[i] + 3*jj ; // THIS IS WRONG, SEE NEXT LINE. + //int dbirj_row_indx = 3*neighsum[j] + 3*i_indx; // need to get i_indx. + // How to get i_indx? + /* + i_indx must start at zero and end at (nneighs[j]-1). + We can start a counter for each atom j. + Maybe this icounter can serve as an index for i as a neighbor of j. + icounter starts at zero and ends at (nneighs[j]-1). + */ + //icounter[atom->tag[j]-1] += 1; + int dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + icounter[atom->tag[j]-1] += 1; + /* + j is an atom index starting from 0. + Use atom->tag[j] to get the atom in the box (index starts at 1). + Need to make sure that the order of these ij pairs is the same when multiplying by dE/dD later. + */ + //printf("----- jj, j, jtag: %d %d %d\n", jj, j, atom->tag[j]); snaptr->compute_duidrj(jj); snaptr->compute_dbidrj(); // Accumulate dBi/dRi, -dBi/dRj + /* + snap_peratom[i] has type double * because each atom index has indices for descriptors. + */ double *snadi = snap_peratom[i]+typeoffset_local; double *snadj = snap_peratom[j]+typeoffset_local; - + //printf("----- ncoeff: %d\n", ncoeff); + //printf("snadi: %f %f %f %f %f\n", snadi[0], snadi[1], snadi[2], snadi[3], snadi[4]); + //printf("----- typeoffset_local: %d\n", typeoffset_local); + //printf("snadi: "); + //for (int s=0; s<(ncoeff*3); s++){ + // printf("%f ", snadi[s]); + //} + /* + printf("snadj: "); + for (int s=0; s<(ncoeff*3); s++){ + printf("%f ", snadj[s]); + } + */ + //printf("\n"); for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + //printf("----- dblist[icoeff]: %f %f %f\n", snaptr->dblist[icoeff][0], snaptr->dblist[icoeff][1], snaptr->dblist[icoeff][2]); + /* + I think these are the descriptor derivatives. + Desriptor derivatives wrt atom i. + What exactly is being summed here? + This is a loop over descriptors or coeff k. + + */ snadi[icoeff] += snaptr->dblist[icoeff][0]; snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + /* + Descriptor derivatives wrt atom j + */ snadj[icoeff] -= snaptr->dblist[icoeff][0]; snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + + + if (dbirjflag){ + dbirj[dbirj_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; + dbirj[dbirj_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; + dbirj[dbirj_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; + } + } if (quadraticflag) { @@ -453,9 +559,11 @@ void ComputeSnap::compute_array() } } - } + } // for (int jj = 0; jj < ninside; jj++) + //printf("---- irow after jj loop: %d\n", irow); // Accumulate Bi + //printf("----- Accumulate Bi.\n"); // linear contributions @@ -476,18 +584,38 @@ void ComputeSnap::compute_array() } } } + + numneigh_sum += ninside; + } // for (int ii = 0; ii < inum; ii++) + + // Check icounter. + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + printf("icounter[i]: %d\n", icounter[i]); + } } + //printf("----- Accumulate bispecturm force contributions to global array.\n"); // accumulate bispectrum force contributions to global array - + //printf("----- ntotal, nmax, natoms: %d %d %d\n", ntotal, nmax, atom->natoms); for (int itype = 0; itype < atom->ntypes; itype++) { const int typeoffset_local = ndims_peratom*nperdim*itype; const int typeoffset_global = nperdim*itype; + //printf("----- nperdim: %d\n", nperdim); + /*nperdim = ncoeff set previsouly*/ for (int icoeff = 0; icoeff < nperdim; icoeff++) { + //printf("----- icoeff: %d\n", icoeff); for (int i = 0; i < ntotal; i++) { double *snadi = snap_peratom[i]+typeoffset_local; int iglobal = atom->tag[i]; + if (icoeff==4){ + if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ + //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); + } + } int irow = 3*(iglobal-1)+bik_rows; + //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; @@ -495,13 +623,20 @@ void ComputeSnap::compute_array() } } + //printf("----- Accumulate forces to global array.\n"); + /* + These are the last columns. + */ // accumulate forces to global array for (int i = 0; i < atom->nlocal; i++) { int iglobal = atom->tag[i]; int irow = 3*(iglobal-1)+bik_rows; + //printf("---- irow: %d\n", irow); snap[irow++][lastcol] = atom->f[i][0]; + //printf("---- irow: %d\n", irow); snap[irow++][lastcol] = atom->f[i][1]; + //printf("---- irow: %d\n", irow); snap[irow][lastcol] = atom->f[i][2]; } @@ -531,6 +666,9 @@ void ComputeSnap::compute_array() snapall[irow++][lastcol] = c_virial->vector[4]; snapall[irow][lastcol] = c_virial->vector[3]; + //}// else + + printf("----- End of compute_array.\n"); } /* ---------------------------------------------------------------------- @@ -567,6 +705,111 @@ void ComputeSnap::dbdotr_compute() } } +/* ---------------------------------------------------------------------- + compute dbirj length +------------------------------------------------------------------------- */ + +void ComputeSnap::get_dbirj_length() +{ + // invoke full neighbor list (will copy or build if necessary) + neighbor->build_one(list); + //memory->destroy(snap); + //memory->destroy(snapall); + dbirj_rows = 0; + const int inum = list->inum; + const int* const ilist = list->ilist; + const int* const numneigh = list->numneigh; + int** const firstneigh = list->firstneigh; + int * const type = atom->type; + const int* const mask = atom->mask; + double** const x = atom->x; + //printf("----- inum: %d\n", inum); + memory->create(neighsum, inum, "snap:neighsum"); + memory->create(nneighs, inum, "snap:nneighs"); + memory->create(icounter, inum, "snap:icounter"); + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + icounter[i]=0; + neighsum[i] = 0; + nneighs[i] = 0; + const double xtmp = x[i][0]; + const double ytmp = x[i][1]; + const double ztmp = x[i][2]; + const int itype = type[i]; + const int* const jlist = firstneigh[i]; + const int jnum = numneigh[i]; + //printf("----- jnum: %d\n", jnum); + int jnum_cutoff = 0; + for (int jj = 0; jj < jnum; jj++) { + int j = jlist[jj]; + j &= NEIGHMASK; + + const double delx = x[j][0] - xtmp; + const double dely = x[j][1] - ytmp; + const double delz = x[j][2] - ztmp; + const double rsq = delx*delx + dely*dely + delz*delz; + int jtype = type[j]; + + if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { + dbirj_rows += 1; //jnum + 1; + jnum_cutoff += 1; + nneighs[i]+=1; + } + } + //printf("----- jnum_cutoff: %d\n", jnum_cutoff); + } + } + + dbirj_rows *= ndims_force; + + // Loop over all atoms again to calculate neighsum. + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + //printf("nneighs[i]: %d\n", nneighs[i]); + //neighsum[i] = 0; + //printf("i nneighs[i]: %d %d\n", i, nneighs[i]); + if (i==0){ + neighsum[i]=0; + } + else{ + for (int jj=0; jj < ii; jj++){ + const int j = ilist[jj]; + if (mask[j] & groupbit) { + //printf(" j nneighs[j-1]: %d %d\n", j, nneighs[j]); + neighsum[i] += nneighs[j]; + } + } + } + } + //printf("%d\n", neighsum[i]); + } + + memory->create(dbirj, dbirj_rows, ncoeff, "snap:dbirj"); + // Set size array rows which now depends on dbirj_rows. + //size_array_rows = bik_rows+dbirj_rows+ndims_virial; + //printf("----- dbirj_rows: %d\n", dbirj_rows); + //printf("----- end of dbirj length.\n"); + /* + memory->create(snap,size_array_rows,size_array_cols, + "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, + "snap:snapall"); + array = snapall; + */ +} + +/* ---------------------------------------------------------------------- + compute array length +------------------------------------------------------------------------- */ + +double ComputeSnap::compute_scalar() +{ + if (dbirjflag) get_dbirj_length(); + return size_array_rows; +} + /* ---------------------------------------------------------------------- memory usage ------------------------------------------------------------------------- */ diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index bc0670e2c7..d077b8f463 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -31,6 +31,7 @@ class ComputeSnap : public Compute { void init() override; void init_list(int, class NeighList *) override; void compute_array() override; + double compute_scalar() override; double memory_usage() override; private: @@ -52,13 +53,19 @@ class ComputeSnap : public Compute { class SNA *snaptr; double cutmax; int quadraticflag; - int bikflag; - int bik_rows; + //int bikflag; + //int bik_rows; + int bikflag, bik_rows, dbirjflag, dbirj_rows; + double **dbirj; + int *nneighs; // number of neighs inside the snap cutoff. + int *neighsum; + int *icounter; // counting atoms i for each j. Compute *c_pe; Compute *c_virial; void dbdotr_compute(); + void get_dbirj_length(); }; } // namespace LAMMPS_NS diff --git a/src/ML-SNAP/sna.cpp b/src/ML-SNAP/sna.cpp index 33937b9c45..d71eeaa7cb 100644 --- a/src/ML-SNAP/sna.cpp +++ b/src/ML-SNAP/sna.cpp @@ -776,6 +776,7 @@ void SNA::compute_dbidrj() int elem3 = elem_duarray; + //printf("----- idxb_max: %d\n", idxb_max); for (int jjb = 0; jjb < idxb_max; jjb++) { const int j1 = idxb[jjb].j1; const int j2 = idxb[jjb].j2; @@ -1334,6 +1335,9 @@ double SNA::memory_usage() void SNA::create_twojmax_arrays() { + + //printf("----- idxb_max: %d\n", idxb_max); + //printf("----- ntriples: %d\n", ntriples); int jdimpq = twojmax + 2; memory->create(rootpqarray, jdimpq, jdimpq, "sna:rootpqarray"); @@ -1595,4 +1599,3 @@ double SNA::compute_dsfac(double r, double rcut, double sinner, double dinner) return dsfac; } - diff --git a/src/fix_move.cpp b/src/fix_move.cpp index 37e8647671..7cf5567028 100644 --- a/src/fix_move.cpp +++ b/src/fix_move.cpp @@ -35,11 +35,16 @@ #include #include +#include +#include +#include +#include + using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; -enum { LINEAR, WIGGLE, ROTATE, VARIABLE, TRANSROT }; +enum { LINEAR, WIGGLE, ROTATE, VARIABLE, TRANSROT, WIGGLE_EIGEN }; enum { EQUAL, ATOM }; #define INERTIA 0.2 // moment of inertia prefactor for ellipsoid @@ -187,7 +192,80 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } else error->all(FLERR, "Illegal fix move command"); - } else + } + // Fix move wiggle_eigen + else if (strcmp(arg[3], "wiggle_eigen") == 0) { + int natoms = atom->natoms; + int nlocal = atom->nlocal; + printf("----- fix move wiggle_eigen initialize -----\n"); + printf("natoms: %d\n", natoms); + memory->create(emat,natoms*3,natoms*3,"move:emat"); + memory->create(freq,natoms*3,"move:freq"); + + // Read EMAT + std::ifstream readfile2; + for (int i = 0; i < natoms*3; i++) { + for (int j = 0; j < natoms*3; j++) { + emat[i][j] = 0.0; + } + } + readfile2.open("../EMAT"); + //readfile2.open("ev_real.txt"); + if (!readfile2.is_open()) { + //printf("ASDFASDF"); + printf("Unable to open ../EMAT\n"); + exit(1); + } + //printf("natoms: %d\n", natoms); + for (int i=0;i<3*natoms;i++){ + for (int j=0;j<3*natoms;j++){ + readfile2>>emat[i][j]; + } + } + + // Read FREQUENCIES + std::ifstream readfile3; + for (int i = 0; i < natoms*3; i++) { + freq[i]=0.0; + } + readfile3.open("FREQUENCIES"); + //readfile2.open("ev_real.txt"); + + if (!readfile3.is_open()) { + printf("Unable to open FREQUENCIES.\n"); + exit(1); + } + //printf("natoms: %d\n", natoms); + for (int i=0;i<3*natoms;i++){ + readfile3>>freq[i]; + } + + + if (narg < 8) error->all(FLERR, "Illegal fix move command"); + iarg = 8; + mstyle = WIGGLE_EIGEN; + if (strcmp(arg[4], "NULL") == 0) + axflag = 0; + else { + axflag = 1; + ax = utils::numeric(FLERR, arg[4], false, lmp); + } + if (strcmp(arg[5], "NULL") == 0) + ayflag = 0; + else { + ayflag = 1; + ay = utils::numeric(FLERR, arg[5], false, lmp); + } + if (strcmp(arg[6], "NULL") == 0) + azflag = 0; + else { + azflag = 1; + az = utils::numeric(FLERR, arg[6], false, lmp); + } + period = utils::numeric(FLERR, arg[7], false, lmp); + if (period <= 0.0) error->all(FLERR, "Illegal fix move command"); + } + else error->all(FLERR, "Illegal fix move command"); // optional args @@ -236,6 +314,10 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : if (axflag) ax *= xscale; if (ayflag) ay *= yscale; if (azflag) az *= zscale; + } else if (mstyle == WIGGLE_EIGEN) { + if (axflag) ax *= xscale; + if (ayflag) ay *= yscale; + if (azflag) az *= zscale; } else if (mstyle == ROTATE) { point[0] *= xscale; point[1] *= yscale; @@ -252,7 +334,7 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : // set omega_rotate from period - if ((mstyle == WIGGLE) || (mstyle == ROTATE) || (mstyle == TRANSROT)) + if ((mstyle == WIGGLE) || (mstyle == ROTATE) || (mstyle == TRANSROT) || (mstyle == WIGGLE_EIGEN)) omega_rotate = MY_2PI / period; // runit = unit vector along rotation axis @@ -295,10 +377,10 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : // AtomVec pointers to retrieve per-atom storage of extra quantities - avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); - avec_line = dynamic_cast(atom->style_match("line")); - avec_tri = dynamic_cast(atom->style_match("tri")); - avec_body = dynamic_cast(atom->style_match("body")); + avec_ellipsoid = dynamic_cast( atom->style_match("ellipsoid")); + avec_line = dynamic_cast( atom->style_match("line")); + avec_tri = dynamic_cast( atom->style_match("tri")); + avec_body = dynamic_cast( atom->style_match("body")); // xoriginal = initial unwrapped positions of atoms // toriginal = initial theta of lines @@ -380,6 +462,11 @@ FixMove::~FixMove() memory->destroy(displace); memory->destroy(velocity); + if (mstyle==WIGGLE_EIGEN){ + memory->destroy(emat); + memory->destroy(freq); + } + delete[] xvarstr; delete[] yvarstr; delete[] zvarstr; @@ -495,7 +582,7 @@ void FixMove::init() velocity = nullptr; if (utils::strmatch(update->integrate_style, "^respa")) - nlevels_respa = (dynamic_cast(update->integrate))->nlevels; + nlevels_respa = (dynamic_cast( update->integrate))->nlevels; } /* ---------------------------------------------------------------------- @@ -527,6 +614,7 @@ void FixMove::initial_integrate(int /*vflag*/) int *tri = atom->tri; int *body = atom->body; int *mask = atom->mask; + int *tag = atom->tag; int nlocal = atom->nlocal; @@ -652,6 +740,142 @@ void FixMove::initial_integrate(int /*vflag*/) // X = P + C + A cos(w*dt) + B sin(w*dt) // V = w R0 cross (A cos(w*dt) + B sin(w*dt)) +} else if (mstyle == WIGGLE_EIGEN) { + //printf("----- Wiggling!-----\n"); + int modeindx = 10; + //printf("%f %f\n", omega_rotate, MY_2PI*freq[modeindx]); + omega_rotate = MY_2PI*freq[modeindx]; + double arg = omega_rotate * delta; + double sine = sin(arg); + double cosine = cos(arg); + + //printf("arg: %f\n", arg); + for (int alpha=0; alpha<3; alpha++){ + for (int i=0; iremap_near(x[i], xold); + } + + } + + + } + + /* + for (int i=0; iremap_near(x[i], xold); + } + + } + */ + + /* + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + xold[0] = x[i][0]; + xold[1] = x[i][1]; + xold[2] = x[i][2]; + + if (axflag) { + v[i][0] = ax * omega_rotate * cosine; + x[i][0] = xoriginal[i][0] + ax * sine; + } else if (rmass) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + x[i][0] += dtv * v[i][0]; + } else { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + x[i][0] += dtv * v[i][0]; + } + + if (ayflag) { + v[i][1] = ay * omega_rotate * cosine; + x[i][1] = xoriginal[i][1] + ay * sine; + } else if (rmass) { + dtfm = dtf / rmass[i]; + v[i][1] += dtfm * f[i][1]; + x[i][1] += dtv * v[i][1]; + } else { + dtfm = dtf / mass[type[i]]; + v[i][1] += dtfm * f[i][1]; + x[i][1] += dtv * v[i][1]; + } + + if (azflag) { + v[i][2] = az * omega_rotate * cosine; + x[i][2] = xoriginal[i][2] + az * sine; + } else if (rmass) { + dtfm = dtf / rmass[i]; + v[i][2] += dtfm * f[i][2]; + x[i][2] += dtv * v[i][2]; + } else { + dtfm = dtf / mass[type[i]]; + v[i][2] += dtfm * f[i][2]; + x[i][2] += dtv * v[i][2]; + } + + domain->remap_near(x[i], xold); + } + } + */ + // for rotate by right-hand rule around omega: + // P = point = vector = point of rotation + // R = vector = axis of rotation + // w = omega of rotation (from period) + // X0 = xoriginal = initial coord of atom + // R0 = runit = unit vector for R + // D = X0 - P = vector from P to X0 + // C = (D dot R0) R0 = projection of atom coord onto R line + // A = D - C = vector from R line to X0 + // B = R0 cross A = vector perp to A in plane of rotation + // A,B define plane of circular rotation around R line + // X = P + C + A cos(w*dt) + B sin(w*dt) + // V = w R0 cross (A cos(w*dt) + B sin(w*dt)) + } else if (mstyle == ROTATE) { double arg = omega_rotate * delta; double cosine = cos(arg); @@ -1303,6 +1527,13 @@ void FixMove::set_arrays(int i) if (ayflag) xoriginal[i][1] -= ay * sine; if (azflag) xoriginal[i][2] -= az * sine; +} else if (mstyle == WIGGLE_EIGEN) { + double arg = omega_rotate * delta; + double sine = sin(arg); + if (axflag) xoriginal[i][0] -= ax * sine; + if (ayflag) xoriginal[i][1] -= ay * sine; + if (azflag) xoriginal[i][2] -= az * sine; + } else if (mstyle == ROTATE) { double a[3], b[3], c[3], d[3], disp[3], ddotr; double arg = -omega_rotate * delta; diff --git a/src/fix_move.h b/src/fix_move.h index e6b253a2a2..8023a66c1b 100644 --- a/src/fix_move.h +++ b/src/fix_move.h @@ -73,6 +73,11 @@ class FixMove : public Fix { int maxatom; double **displace, **velocity; + // fix move wiggle_eigen variables + + double **emat; + double *freq; + class AtomVecEllipsoid *avec_ellipsoid; class AtomVecLine *avec_line; class AtomVecTri *avec_tri; @@ -83,3 +88,53 @@ class FixMove : public Fix { #endif #endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Fix move cannot set linear z motion for 2d problem + +Self-explanatory. + +E: Fix move cannot set wiggle z motion for 2d problem + +Self-explanatory. + +E: Fix move cannot rotate around non z-axis for 2d problem + +UNDOCUMENTED + +E: Fix move cannot define z or vz variable for 2d problem + +Self-explanatory. + +E: Zero length rotation vector with fix move + +Self-explanatory. + +E: Variable name for fix move does not exist + +Self-explanatory. + +E: Variable for fix move is invalid style + +Only equal-style variables can be used. + +E: Cannot add atoms to fix move variable + +Atoms can not be added afterwards to this fix option. + +E: Resetting timestep size is not allowed with fix move + +This is because fix move is moving atoms based on elapsed time. + +U: Fix move cannot rotate aroung non z-axis for 2d problem + +Self-explanatory. + +*/ From d4f1b702a26020a43d6a15f0de3abfe4f4d2dd39 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Sat, 28 May 2022 10:31:45 -0600 Subject: [PATCH 02/75] Working derivative extraction. --- src/ML-SNAP/compute_snap.cpp | 241 ++++++++++++++++++++++++++++------- src/ML-SNAP/compute_snap.h | 2 + 2 files changed, 200 insertions(+), 43 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 70464420c2..8572f3b8a4 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -238,6 +238,7 @@ ComputeSnap::~ComputeSnap() memory->destroy(nneighs); memory->destroy(neighsum); memory->destroy(icounter); + memory->destroy(dbiri); } } @@ -354,10 +355,11 @@ void ComputeSnap::compute_array() //printf("----- NEIGHMASK: %d\n", NEIGHMASK); int ninside; int numneigh_sum = 0; + int dbirj_row_indx; for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; - printf("----- ii, ilist, itag, irow: %d %d %d %d\n", ii, ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]], irow); + printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); const int i = ilist[ii]; //printf("----- ii, i: %d %d\n", ii, i); //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); @@ -450,9 +452,14 @@ void ComputeSnap::compute_array() icounter starts at zero and ends at (nneighs[j]-1). */ //icounter[atom->tag[j]-1] += 1; - int dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. - printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); - icounter[atom->tag[j]-1] += 1; + if (dbirjflag){ + dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + icounter[atom->tag[j]-1] += 1; + } + //int dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + //icounter[atom->tag[j]-1] += 1; /* j is an atom index starting from 0. Use atom->tag[j] to get the atom in the box (index starts at 1). @@ -507,8 +514,12 @@ void ComputeSnap::compute_array() dbirj[dbirj_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; dbirj[dbirj_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; dbirj[dbirj_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; + // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i. + dbiri[3*(atom->tag[i]-1)+0][icoeff] -= snaptr->dblist[icoeff][0]; + dbiri[3*(atom->tag[i]-1)+1][icoeff] -= snaptr->dblist[icoeff][1]; + dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; } - + } if (quadraticflag) { @@ -588,56 +599,185 @@ void ComputeSnap::compute_array() numneigh_sum += ninside; } // for (int ii = 0; ii < inum; ii++) + printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); + // Check icounter. + /* for (int ii = 0; ii < inum; ii++) { const int i = ilist[ii]; if (mask[i] & groupbit) { printf("icounter[i]: %d\n", icounter[i]); } } + */ - //printf("----- Accumulate bispecturm force contributions to global array.\n"); - // accumulate bispectrum force contributions to global array - //printf("----- ntotal, nmax, natoms: %d %d %d\n", ntotal, nmax, atom->natoms); - for (int itype = 0; itype < atom->ntypes; itype++) { - const int typeoffset_local = ndims_peratom*nperdim*itype; - const int typeoffset_global = nperdim*itype; - //printf("----- nperdim: %d\n", nperdim); - /*nperdim = ncoeff set previsouly*/ - for (int icoeff = 0; icoeff < nperdim; icoeff++) { - //printf("----- icoeff: %d\n", icoeff); - for (int i = 0; i < ntotal; i++) { - double *snadi = snap_peratom[i]+typeoffset_local; - int iglobal = atom->tag[i]; - if (icoeff==4){ - if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ - //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); + // Sum all the derivatives we calculated to check usual compute snap output. + if (dbirjflag){ + fh_d = fopen("DEBUG", "w"); + int row_indx=0; + for (int ii=0; iintypes); + //for (int itype = 0; itype < atom->ntypes; itype++) { + for (int itype=0; itype<1; itype++){ + const int typeoffset_local = ndims_peratom*nperdim*itype; + const int typeoffset_global = nperdim*itype; + //printf("----- nperdim: %d\n", nperdim); + /*nperdim = ncoeff set previsouly*/ + for (int icoeff = 0; icoeff < nperdim; icoeff++) { + //printf("----- icoeff: %d\n", icoeff); + for (int i = 0; i < atom->nlocal; i++) { + //printf("i: %d\n", i); + + + for (int jj=0; jjtag[i]-1] + 3*jj; + int irow = dbirj_row_indx+bik_rows; + //printf(" row_indx, irow: %d %d\n", dbirj_row_indx, irow); + snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; + //printf(" irow: %d\n", irow); + snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; + //printf(" irow: %d\n", irow); + snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+2][icoeff]; + } + + + //int dbirj_row_indx = 3*neighsum[atom->tag[i]-1] + 3*icounter[atom->tag[i]-1]; + //printf(" row_indx: %d\n", dbirj_row_indx); + + /* + double *snadi = snap_peratom[i]+typeoffset_local; + int iglobal = atom->tag[i]; + if (icoeff==4){ + if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ + //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); + } + } + int irow = 3*(iglobal-1)+bik_rows; + //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; + snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; + */ + } + } + } + + } + + else{ + //printf("----- Accumulate bispecturm force contributions to global array.\n"); + // accumulate bispectrum force contributions to global array + //printf("----- ntotal, nmax, natoms: %d %d %d\n", ntotal, nmax, atom->natoms); + for (int itype = 0; itype < atom->ntypes; itype++) { + const int typeoffset_local = ndims_peratom*nperdim*itype; + const int typeoffset_global = nperdim*itype; + //printf("----- nperdim: %d\n", nperdim); + /*nperdim = ncoeff set previsouly*/ + for (int icoeff = 0; icoeff < nperdim; icoeff++) { + //printf("----- icoeff: %d\n", icoeff); + for (int i = 0; i < ntotal; i++) { + double *snadi = snap_peratom[i]+typeoffset_local; + int iglobal = atom->tag[i]; + if (icoeff==4){ + if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ + //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); + } + } + int irow = 3*(iglobal-1)+bik_rows; + //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; + snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; } - int irow = 3*(iglobal-1)+bik_rows; - //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; - snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; } } } +printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); //printf("----- Accumulate forces to global array.\n"); /* These are the last columns. */ // accumulate forces to global array + if (dbirjflag){ - for (int i = 0; i < atom->nlocal; i++) { - int iglobal = atom->tag[i]; - int irow = 3*(iglobal-1)+bik_rows; - //printf("---- irow: %d\n", irow); - snap[irow++][lastcol] = atom->f[i][0]; - //printf("---- irow: %d\n", irow); - snap[irow++][lastcol] = atom->f[i][1]; - //printf("---- irow: %d\n", irow); - snap[irow][lastcol] = atom->f[i][2]; + } + else{ + for (int i = 0; i < atom->nlocal; i++) { + int iglobal = atom->tag[i]; + int irow = 3*(iglobal-1)+bik_rows; + //printf("---- irow: %d\n", irow); + snap[irow++][lastcol] = atom->f[i][0]; + //printf("---- irow: %d\n", irow); + snap[irow++][lastcol] = atom->f[i][1]; + //printf("---- irow: %d\n", irow); + snap[irow][lastcol] = atom->f[i][2]; + } } // accumulate bispectrum virial contributions to global array @@ -645,9 +785,9 @@ void ComputeSnap::compute_array() dbdotr_compute(); // sum up over all processes - +printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); +printf("`-`-`-` snapall[50][6]: %f\n", snapall[50][6]); MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); - // assign energy to last column for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; int irow = 0; @@ -679,7 +819,15 @@ void ComputeSnap::compute_array() void ComputeSnap::dbdotr_compute() { double **x = atom->x; - int irow0 = bik_rows+ndims_force*natoms; + + int irow0; + if (dbirjflag){ + irow0 = bik_rows+dbirj_rows; + } + else{ + irow0 = bik_rows+ndims_force*natoms; + } + //int irow0 = bik_rows+ndims_force*natoms; // sum over bispectrum contributions to forces // on all particles including ghosts @@ -713,8 +861,8 @@ void ComputeSnap::get_dbirj_length() { // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - //memory->destroy(snap); - //memory->destroy(snapall); + memory->destroy(snap); + memory->destroy(snapall); dbirj_rows = 0; const int inum = list->inum; const int* const ilist = list->ilist; @@ -727,6 +875,12 @@ void ComputeSnap::get_dbirj_length() memory->create(neighsum, inum, "snap:neighsum"); memory->create(nneighs, inum, "snap:nneighs"); memory->create(icounter, inum, "snap:icounter"); + memory->create(dbiri, 3*inum,ncoeff, "snap:dbiri"); + for (int ii=0; iicreate(dbirj, dbirj_rows, ncoeff, "snap:dbirj"); // Set size array rows which now depends on dbirj_rows. - //size_array_rows = bik_rows+dbirj_rows+ndims_virial; + size_array_rows = bik_rows+dbirj_rows+ndims_virial; //printf("----- dbirj_rows: %d\n", dbirj_rows); //printf("----- end of dbirj length.\n"); - /* + memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); array = snapall; - */ + } /* ---------------------------------------------------------------------- diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index d077b8f463..3b03512efc 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -35,6 +35,7 @@ class ComputeSnap : public Compute { double memory_usage() override; private: + FILE * fh_d; int natoms, nmax, size_peratom, lastcol; int ncoeff, nperdim, yoffset, zoffset; int ndims_peratom, ndims_force, ndims_virial; @@ -57,6 +58,7 @@ class ComputeSnap : public Compute { //int bik_rows; int bikflag, bik_rows, dbirjflag, dbirj_rows; double **dbirj; + double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j int *nneighs; // number of neighs inside the snap cutoff. int *neighsum; int *icounter; // counting atoms i for each j. From 5318ce9b7440eeea99423b3e8bec3425330f8db1 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Sat, 28 May 2022 20:08:58 -0600 Subject: [PATCH 03/75] compute snapneigh gets atom and neighbor indices --- src/ML-SNAP/compute_snap.cpp | 18 +- src/ML-SNAP/compute_snapneigh.cpp | 537 ++++++++++++++++++++++++++++++ src/ML-SNAP/compute_snapneigh.h | 73 ++++ 3 files changed, 619 insertions(+), 9 deletions(-) create mode 100644 src/ML-SNAP/compute_snapneigh.cpp create mode 100644 src/ML-SNAP/compute_snapneigh.h diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 8572f3b8a4..d0e4d67578 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -303,11 +303,13 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { + if (dbirjflag){ printf("----- dbirjflag true.\n"); get_dbirj_length(); printf("----- got dbirj_length\n"); } + printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); //else{ int ntotal = atom->nlocal + atom->nghost; @@ -599,7 +601,8 @@ void ComputeSnap::compute_array() numneigh_sum += ninside; } // for (int ii = 0; ii < inum; ii++) - printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); + printf("----- bik_rows: %d\n", bik_rows); + printf("----- bikflag: %d\n", bikflag); // Check icounter. /* @@ -680,7 +683,7 @@ void ComputeSnap::compute_array() if (dbirjflag){ - printf("ntypes: %d\n", atom->ntypes); + //printf("ntypes: %d\n", atom->ntypes); //for (int itype = 0; itype < atom->ntypes; itype++) { for (int itype=0; itype<1; itype++){ const int typeoffset_local = ndims_peratom*nperdim*itype; @@ -758,7 +761,6 @@ void ComputeSnap::compute_array() } } -printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); //printf("----- Accumulate forces to global array.\n"); /* These are the last columns. @@ -861,8 +863,8 @@ void ComputeSnap::get_dbirj_length() { // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - memory->destroy(snap); - memory->destroy(snapall); + //memory->destroy(snap); + //memory->destroy(snapall); dbirj_rows = 0; const int inum = list->inum; const int* const ilist = list->ilist; @@ -947,10 +949,8 @@ void ComputeSnap::get_dbirj_length() //printf("----- dbirj_rows: %d\n", dbirj_rows); //printf("----- end of dbirj length.\n"); - memory->create(snap,size_array_rows,size_array_cols, - "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, - "snap:snapall"); + memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); array = snapall; } diff --git a/src/ML-SNAP/compute_snapneigh.cpp b/src/ML-SNAP/compute_snapneigh.cpp new file mode 100644 index 0000000000..dd6146e154 --- /dev/null +++ b/src/ML-SNAP/compute_snapneigh.cpp @@ -0,0 +1,537 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 "compute_snapneigh.h" + +#include "sna.h" +#include "atom.h" +#include "update.h" +#include "modify.h" +#include "neighbor.h" +#include "neigh_list.h" +#include "force.h" +#include "pair.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#include + +using namespace LAMMPS_NS; + +enum{SCALAR,VECTOR,ARRAY}; + +ComputeSnapneigh::ComputeSnapneigh(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), radelem(nullptr), wjelem(nullptr), + sinnerelem(nullptr), dinnerelem(nullptr) +{ + + array_flag = 1; + //vector_flag = 1; + extarray = 0; + + double rfac0, rmin0; + int twojmax, switchflag, bzeroflag, bnormflag, wselfallflag; + + int ntypes = atom->ntypes; + int nargmin = 6+2*ntypes; + + if (narg < nargmin) error->all(FLERR,"Illegal compute snap command"); + + // default values + + rmin0 = 0.0; + switchflag = 1; + bzeroflag = 1; + quadraticflag = 0; + bikflag = 0; + dbirjflag = 0; + chemflag = 0; + bnormflag = 0; + wselfallflag = 0; + switchinnerflag = 0; + nelements = 1; + + // process required arguments + + memory->create(radelem,ntypes+1,"snapneigh:radelem"); // offset by 1 to match up with types + memory->create(wjelem,ntypes+1,"snapneigh:wjelem"); + rcutfac = atof(arg[3]); + rfac0 = atof(arg[4]); + twojmax = atoi(arg[5]); + for (int i = 0; i < ntypes; i++) + radelem[i+1] = atof(arg[6+i]); + for (int i = 0; i < ntypes; i++) + wjelem[i+1] = atof(arg[6+ntypes+i]); + + // construct cutsq + + double cut; + cutmax = 0.0; + memory->create(cutsq,ntypes+1,ntypes+1,"snapneigh:cutsq"); + for (int i = 1; i <= ntypes; i++) { + cut = 2.0*radelem[i]*rcutfac; + printf("cut: %f\n", cut); + if (cut > cutmax) cutmax = cut; + cutsq[i][i] = cut*cut; + for (int j = i+1; j <= ntypes; j++) { + cut = (radelem[i]+radelem[j])*rcutfac; + cutsq[i][j] = cutsq[j][i] = cut*cut; + } + } + + // set local input checks + + int sinnerflag = 0; + int dinnerflag = 0; + + // process optional args + + int iarg = nargmin; + + while (iarg < narg) { + if (strcmp(arg[iarg],"rmin0") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + rmin0 = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"bzeroflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + bzeroflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"switchflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + switchflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"quadraticflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + quadraticflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"chem") == 0) { + if (iarg+2+ntypes > narg) + error->all(FLERR,"Illegal compute snap command"); + chemflag = 1; + memory->create(map,ntypes+1,"compute_snapneigh:map"); + nelements = utils::inumeric(FLERR,arg[iarg+1],false,lmp); + for (int i = 0; i < ntypes; i++) { + int jelem = utils::inumeric(FLERR,arg[iarg+2+i],false,lmp); + if (jelem < 0 || jelem >= nelements) + error->all(FLERR,"Illegal compute snap command"); + map[i+1] = jelem; + } + iarg += 2+ntypes; + } else if (strcmp(arg[iarg],"bnormflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + bnormflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"wselfallflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + wselfallflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"bikflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + bikflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"dbirjflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + dbirjflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + switchinnerflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"sinner") == 0) { + iarg++; + if (iarg+ntypes > narg) + error->all(FLERR,"Illegal compute snap command"); + memory->create(sinnerelem,ntypes+1,"snapneigh:sinnerelem"); + for (int i = 0; i < ntypes; i++) + sinnerelem[i+1] = utils::numeric(FLERR,arg[iarg+i],false,lmp); + sinnerflag = 1; + iarg += ntypes; + } else if (strcmp(arg[iarg],"dinner") == 0) { + iarg++; + if (iarg+ntypes > narg) + error->all(FLERR,"Illegal compute snap command"); + memory->create(dinnerelem,ntypes+1,"snapneigh:dinnerelem"); + for (int i = 0; i < ntypes; i++) + dinnerelem[i+1] = utils::numeric(FLERR,arg[iarg+i],false,lmp); + dinnerflag = 1; + iarg += ntypes; + } else error->all(FLERR,"Illegal compute snap command"); + } + + if (switchinnerflag && !(sinnerflag && dinnerflag)) + error->all(FLERR,"Illegal compute snap command: switchinnerflag = 1, missing sinner/dinner keyword"); + + if (!switchinnerflag && (sinnerflag || dinnerflag)) + error->all(FLERR,"Illegal compute snap command: switchinnerflag = 0, unexpected sinner/dinner keyword"); + + /* + snaptr = new SNA(lmp, rfac0, twojmax, + rmin0, switchflag, bzeroflag, + chemflag, bnormflag, wselfallflag, + nelements, switchinnerflag); + */ + + //ncoeff = snaptr->ncoeff; + nperdim = ncoeff; + if (quadraticflag) nperdim += (ncoeff*(ncoeff+1))/2; + ndims_force = 3; + ndims_virial = 6; + yoffset = nperdim; + zoffset = 2*nperdim; + natoms = atom->natoms; + bik_rows = 1; + if (bikflag) bik_rows = natoms; + //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; + dbirj_rows = ndims_force*natoms; + size_array_rows = bik_rows+dbirj_rows+ndims_virial; + size_array_cols = nperdim*atom->ntypes+1; + lastcol = size_array_cols-1; + + ndims_peratom = ndims_force; + size_peratom = ndims_peratom*nperdim*atom->ntypes; + + nmax = 0; + +} + +/* ---------------------------------------------------------------------- */ + +ComputeSnapneigh::~ComputeSnapneigh() +{ + + memory->destroy(neighs); + memory->destroy(radelem); + memory->destroy(wjelem); + memory->destroy(cutsq); + //delete snaptr; + + if (chemflag) memory->destroy(map); + + if (switchinnerflag) { + memory->destroy(sinnerelem); + memory->destroy(dinnerelem); + } + + if (dbirjflag){ + printf("dbirj_rows: %d\n", dbirj_rows); + memory->destroy(dbirj); + memory->destroy(nneighs); + memory->destroy(neighsum); + memory->destroy(icounter); + memory->destroy(dbiri); + } +} + +/* ---------------------------------------------------------------------- */ + +void ComputeSnapneigh::init() +{ + if (force->pair == nullptr) + error->all(FLERR,"Compute snap requires a pair style be defined"); + + if (cutmax > force->pair->cutforce){ + //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); + error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); + } + + // need an occasional full neighbor list + + neighbor->add_request(this, NeighConst::REQ_FULL | NeighConst::REQ_OCCASIONAL); + +} + + +/* ---------------------------------------------------------------------- */ + +void ComputeSnapneigh::init_list(int /*id*/, NeighList *ptr) +{ + list = ptr; +} + +/* ---------------------------------------------------------------------- */ + +void ComputeSnapneigh::compute_array() +{ + + if (dbirjflag){ + printf("----- dbirjflag true.\n"); + get_dbirj_length(); + printf("----- got dbirj_length\n"); + } + + printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); + //else{ + int ntotal = atom->nlocal + atom->nghost; + + invoked_array = update->ntimestep; + + // invoke full neighbor list (will copy or build if necessary) + neighbor->build_one(list); + + const int inum = list->inum; + const int* const ilist = list->ilist; + const int* const numneigh = list->numneigh; + int** const firstneigh = list->firstneigh; + int * const type = atom->type; + + // compute sna derivatives for each atom in group + // use full neighbor list to count atoms less than cutoff + + double** const x = atom->x; + const int* const mask = atom->mask; + //printf("----- inum: %d\n", inum); + //printf("----- NEIGHMASK: %d\n", NEIGHMASK); + int ninside; + int numneigh_sum = 0; + int dbirj_row_indx; + for (int ii = 0; ii < inum; ii++) { + int irow = 0; + if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; + //printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); + const int i = ilist[ii]; + //printf("----- ii, i: %d %d\n", ii, i); + //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); + if (mask[i] & groupbit) { + + const double xtmp = x[i][0]; + const double ytmp = x[i][1]; + const double ztmp = x[i][2]; + const int itype = type[i]; + int ielem = 0; + if (chemflag) + ielem = map[itype]; + const double radi = radelem[itype]; + const int* const jlist = firstneigh[i]; + const int jnum = numneigh[i]; + const int typeoffset_local = ndims_peratom*nperdim*(itype-1); + const int typeoffset_global = nperdim*(itype-1); + + // insure rij, inside, and typej are of size jnum + + //snaptr->grow_rij(jnum); + + // rij[][3] = displacements between atom I and those neighbors + // inside = indices of neighbors of I within cutoff + // typej = types of neighbors of I within cutoff + // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi + + /* + This loop assigns quantities in snaptr. + snaptr is a SNA class instance, see sna.h + + */ + //int ninside = 0; + ninside=0; + for (int jj = 0; jj < jnum; jj++) { + int j = jlist[jj]; + j &= NEIGHMASK; + + const double delx = x[j][0] - xtmp; + const double dely = x[j][1] - ytmp; + const double delz = x[j][2] - ztmp; + const double rsq = delx*delx + dely*dely + delz*delz; + int jtype = type[j]; + int jelem = 0; + if (chemflag) + jelem = map[jtype]; + if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { + if (dbirjflag){ + dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + icounter[atom->tag[j]-1] += 1; + + neighs[dbirj_row_indx+0][0] = atom->tag[i]; + neighs[dbirj_row_indx+1][0] = atom->tag[i]; + neighs[dbirj_row_indx+2][0] = atom->tag[i]; + + neighs[dbirj_row_indx+0][1] = atom->tag[j]; + neighs[dbirj_row_indx+1][1] = atom->tag[j]; + neighs[dbirj_row_indx+2][1] = atom->tag[j]; + } + } + } + + // for (int jj = 0; jj < ninside; jj++) + //printf("---- irow after jj loop: %d\n", irow); + + } + + numneigh_sum += ninside; + } // for (int ii = 0; ii < inum; ii++) + + + // Check icounter. + /* + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + printf("icounter[i]: %d\n", icounter[i]); + } + } + */ + + + // sum up over all processes + // I'll need to do something like this... + /* + MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); + // assign energy to last column + for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; + int irow = 0; + double reference_energy = c_pe->compute_scalar(); + snapall[irow][lastcol] = reference_energy; + */ + + + for (int i=0; ibuild_one(list); + dbirj_rows = 0; + const int inum = list->inum; + const int* const ilist = list->ilist; + const int* const numneigh = list->numneigh; + int** const firstneigh = list->firstneigh; + int * const type = atom->type; + const int* const mask = atom->mask; + double** const x = atom->x; + //printf("----- inum: %d\n", inum); + memory->create(neighsum, inum, "snapneigh:neighsum"); + memory->create(nneighs, inum, "snapneigh:nneighs"); + memory->create(icounter, inum, "snapneigh:icounter"); + memory->create(dbiri, 3*inum,ncoeff, "snapneigh:dbiri"); + for (int ii=0; ii1e-20) { + dbirj_rows += 1; //jnum + 1; + jnum_cutoff += 1; + nneighs[i]+=1; + } + } + //printf("----- jnum_cutoff: %d\n", jnum_cutoff); + } + } + + dbirj_rows *= ndims_force; + + // Loop over all atoms again to calculate neighsum. + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + //printf("nneighs[i]: %d\n", nneighs[i]); + //neighsum[i] = 0; + //printf("i nneighs[i]: %d %d\n", i, nneighs[i]); + if (i==0){ + neighsum[i]=0; + } + else{ + for (int jj=0; jj < ii; jj++){ + const int j = ilist[jj]; + if (mask[j] & groupbit) { + //printf(" j nneighs[j-1]: %d %d\n", j, nneighs[j]); + neighsum[i] += nneighs[j]; + } + } + //neighsum[i] += 1; // Add 1 for the self term dBi/dRi + } + } + //printf("%d\n", neighsum[i]); + } + + memory->create(dbirj, dbirj_rows, ncoeff, "snapneigh:dbirj"); + memory->create(neighs, dbirj_rows, 2, "snapneigh:neighs"); + size_array_rows = dbirj_rows; + size_array_cols = 2; + //vector = neighs; + array = neighs; + // Set size array rows which now depends on dbirj_rows. + //size_array_rows = bik_rows+dbirj_rows+ndims_virial; + //printf("----- dbirj_rows: %d\n", dbirj_rows); + //printf("----- end of dbirj length.\n"); + +} + +/* ---------------------------------------------------------------------- + compute array length +------------------------------------------------------------------------- */ + +double ComputeSnapneigh::compute_scalar() +{ + if (dbirjflag) get_dbirj_length(); + return size_array_rows; +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +double ComputeSnapneigh::memory_usage() +{ + + double bytes = (double)size_array_rows*size_array_cols * + sizeof(double); // array + + return bytes; +} diff --git a/src/ML-SNAP/compute_snapneigh.h b/src/ML-SNAP/compute_snapneigh.h new file mode 100644 index 0000000000..1ac712a101 --- /dev/null +++ b/src/ML-SNAP/compute_snapneigh.h @@ -0,0 +1,73 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 COMPUTE_CLASS +// clang-format off +ComputeStyle(snapneigh,ComputeSnapneigh); +// clang-format on +#else + +#ifndef LMP_COMPUTE_SNAPNEIGH_H +#define LMP_COMPUTE_SNAPNEIGH_H + +#include "compute.h" + +namespace LAMMPS_NS { + +class ComputeSnapneigh : public Compute { + public: + ComputeSnapneigh(class LAMMPS *, int, char **); + ~ComputeSnapneigh() override; + void init() override; + void init_list(int, class NeighList *) override; + void compute_array() override; + double compute_scalar() override; + double memory_usage() override; + + private: + FILE * fh_d; + int natoms, nmax, size_peratom, lastcol; + int ncoeff, nperdim, yoffset, zoffset; + int ndims_peratom, ndims_force, ndims_virial; + double **cutsq; + class NeighList *list; + double **snap, **snapall; + double **snap_peratom; + double rcutfac; + double *radelem; + double *wjelem; + int *map; // map types to [0,nelements) + int nelements, chemflag; + int switchinnerflag; + double *sinnerelem; + double *dinnerelem; + //class SNA *snaptr; + double cutmax; + int quadraticflag; + //int bikflag; + //int bik_rows; + int bikflag, bik_rows, dbirjflag, dbirj_rows; + double **dbirj; + double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j + int *nneighs; // number of neighs inside the snap cutoff. + int *neighsum; + int *icounter; // counting atoms i for each j. + double **neighs; // neighborlist for neural networks + + void get_dbirj_length(); +}; + +} // namespace LAMMPS_NS + +#endif +#endif From ce646a38596be468f4007ca3863c09ae79910d4e Mon Sep 17 00:00:00 2001 From: rohskopf Date: Mon, 6 Jun 2022 15:26:52 -0600 Subject: [PATCH 04/75] Working derivative extraction. --- examples/mliap/Ta06A.mliap.pytorch.model.pt | Bin 0 -> 2087 bytes python/lammps/mliap/__init__.py | 2 +- src/ML-SNAP/compute_snap.cpp | 178 ++++++++++---------- src/ML-SNAP/compute_snapneigh.cpp | 66 +++++--- 4 files changed, 137 insertions(+), 109 deletions(-) create mode 100644 examples/mliap/Ta06A.mliap.pytorch.model.pt diff --git a/examples/mliap/Ta06A.mliap.pytorch.model.pt b/examples/mliap/Ta06A.mliap.pytorch.model.pt new file mode 100644 index 0000000000000000000000000000000000000000..fc2e10398ec6fae4978e37c11ac899615096ef7d GIT binary patch literal 2087 zcmah~YfKzf6rSbHP~Nmf(8a2ihhK`>yjTIC9p(ZBYJ2SAmAoeDAZtgwj zeD~b%JjTZ)CQ(#c8nt5NPFXofsRB7nV~YujtjHX`O%pX8$@-!Wry(8E z103Fqk}k>|N8=M3=qDj#1lIT=lg%d9y?kKcAg=~FK~V-Y$TE?$*>yxFYFuALlHzqB zr<2Vh(mq9vG0Zg$^VqytQCLMbZoVllU~`FB(|H|nLHs&fy4IAgV~dDXQutu3N)%+h zsa9xl5mJH?32AVbd050IV_STNBq;vx(6slN2~i?g zwB9SE3z(%1BXFM|?iVaZ9uRUm1?!C)1?w>n3YOcYg5_74V7XGxreIgJNUw||P!TZm z!X?Wt+hLa^%7|C#UDg5*VNW&v zhoKk;<2(lSjFWL3gNKisoHgjs=!dOi=H`)6*ye{O78e%PEAkq6glYlbE7S-UtQ)oq zb&NxY9VXlecp1kCG+R(DI<)$s&4SuF3Xl4sJz|ICF)Jj@2<-AhhaHm6Xh?S3A@SKE z*<**qZ-->BS#P)q`Vg;2RGc;Y7$>_1`>90q=wTP*i1gyXX(s(aG$;lTJl<)hR!~12 zM$m1u)`yc+-%?8hYbGNuKvK#YJi(N(nHW~)f=COfVi@zP!G3%hZJr{WeYi+9=pk3R zd`xOuf@8-!ZJ07LeXDk*EUGE4D^*puc^jAYq__rkhfV8n)61ppqXye>`R3pUM~s^b zp}#McOdGZDAN}$*@2oMWGG{ixjHkQjJ>!>&vz{N%Z~Wot#5vDJmweiB?YhxCUpILC z=Q-ob*usso>#rKOI$A59ublKqP3JS$et*q)#yy^YyJFJg_8z(nH!d1{z0@=Q)dhn; z;WTD`nlVb>dgE)R=&Ipr_;oz%i&cEMM0 zD}UM;Zl3aAN`2P|O%={7-Zu>?>AT23&%I!r?4B=t>G}zyHS=x$&#^;Bam7b}o$_5U zl25;4`F(0=f|_Z;V_W-e@qv|Gwp8k=B!|vIqCoOHcI`F;J$d| z=_{nales!>HnnvLcaXGeMQ-!fuK>P7+!n=~X>#*t6a8HY_*OD~jDsvo|Fqb=${~Wd zvHTCW+&Y#k((R+;Rh*}>)>YNZZDaX}x214rjI(tww~c)f`9DPmsmVoKn?E0ulCsIx ta3)qR>5>LS#^Ucreate(cutsq,ntypes+1,ntypes+1,"snap:cutsq"); for (int i = 1; i <= ntypes; i++) { cut = 2.0*radelem[i]*rcutfac; - printf("cut: %f\n", cut); + //printf("cut: %f\n", cut); if (cut > cutmax) cutmax = cut; cutsq[i][i] = cut*cut; for (int j = i+1; j <= ntypes; j++) { @@ -204,7 +204,8 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; dbirj_rows = ndims_force*natoms; size_array_rows = bik_rows+dbirj_rows+ndims_virial; - size_array_cols = nperdim*atom->ntypes+1; + if (dbirjflag) size_array_cols = nperdim; + else size_array_cols = nperdim*atom->ntypes+1; lastcol = size_array_cols-1; ndims_peratom = ndims_force; @@ -217,6 +218,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : ComputeSnap::~ComputeSnap() { + memory->destroy(snap); memory->destroy(snapall); memory->destroy(snap_peratom); @@ -233,12 +235,18 @@ ComputeSnap::~ComputeSnap() } if (dbirjflag){ - printf("dbirj_rows: %d\n", dbirj_rows); + //printf("dbirj_rows: %d\n", dbirj_rows); + //printf("----- destroy dbirj\n"); memory->destroy(dbirj); + //printf("----- 1-1-1-1-1-1\n"); memory->destroy(nneighs); + //printf("----- 2-1-1-1-1-1\n"); memory->destroy(neighsum); + //printf("----- 3-1-1-1-1-1\n"); memory->destroy(icounter); + //printf("----- 4-1-1-1-1-1\n"); memory->destroy(dbiri); + //printf("----- 5-1-1-1-1-1\n"); } } @@ -263,7 +271,8 @@ void ComputeSnap::init() snaptr->init(); // allocate memory for global array - printf("----- dbirjflag: %d\n", dbirjflag); + + //printf("----- dbirjflag: %d\n", dbirjflag); memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, @@ -279,7 +288,6 @@ void ComputeSnap::init() c_pe = modify->compute[ipe]; // add compute for reference virial tensor - std::string id_virial = std::string("snap_press"); std::string pcmd = id_virial + " all pressure NULL virial"; modify->add_compute(pcmd); @@ -304,13 +312,17 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { + //printf(" -----2 dbirjflag: %d\n", dbirjflag); if (dbirjflag){ - printf("----- dbirjflag true.\n"); + //printf("----- dbirjflag true.\n"); get_dbirj_length(); - printf("----- got dbirj_length\n"); + //printf("----- got dbirj_length\n"); } + //else{ + // printf("----- dbirjflag false.\n"); + //} - printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); + //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); //else{ int ntotal = atom->nlocal + atom->nghost; @@ -325,7 +337,7 @@ void ComputeSnap::compute_array() "snap:snap_peratom"); } // clear global array - printf("size_array_rows: %d\n", size_array_rows); + //printf("size_array_rows: %d\n", size_array_rows); for (int irow = 0; irow < size_array_rows; irow++){ for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ //printf("%d %d\n", irow, icoeff); @@ -361,7 +373,7 @@ void ComputeSnap::compute_array() for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; - printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); + //printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); const int i = ilist[ii]; //printf("----- ii, i: %d %d\n", ii, i); //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); @@ -439,7 +451,7 @@ void ComputeSnap::compute_array() I think it only needs neighbor info, and then it goes from there. */ //printf("----- Derivative loop - looping over neighbors j.\n"); - printf("----- ninside: %d\n", ninside); // numneighs of I within cutoff + //printf("----- ninside: %d\n", ninside); // numneighs of I within cutoff for (int jj = 0; jj < ninside; jj++) { //printf("----- jj: %d\n", jj); const int j = snaptr->inside[jj]; @@ -456,7 +468,7 @@ void ComputeSnap::compute_array() //icounter[atom->tag[j]-1] += 1; if (dbirjflag){ dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. - printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); icounter[atom->tag[j]-1] += 1; } //int dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. @@ -522,6 +534,7 @@ void ComputeSnap::compute_array() dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; } + } if (quadraticflag) { @@ -579,10 +592,13 @@ void ComputeSnap::compute_array() //printf("----- Accumulate Bi.\n"); // linear contributions - - int k = typeoffset_global; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) + int k; + if (dbirjflag) k = 0; + else k = typeoffset_global; + for (int icoeff = 0; icoeff < ncoeff; icoeff++){ + //printf("----- %d %f\n", icoeff, snaptr->blist[icoeff]); snap[irow][k++] += snaptr->blist[icoeff]; + } // quadratic contributions @@ -601,8 +617,8 @@ void ComputeSnap::compute_array() numneigh_sum += ninside; } // for (int ii = 0; ii < inum; ii++) - printf("----- bik_rows: %d\n", bik_rows); - printf("----- bikflag: %d\n", bikflag); + //printf("----- bik_rows: %d\n", bik_rows); + //printf("----- bikflag: %d\n", bikflag); // Check icounter. /* @@ -615,16 +631,19 @@ void ComputeSnap::compute_array() */ // Sum all the derivatives we calculated to check usual compute snap output. + /* if (dbirjflag){ fh_d = fopen("DEBUG", "w"); int row_indx=0; + double sum; for (int ii=0; iintypes); //for (int itype = 0; itype < atom->ntypes; itype++) { + int dbiri_indx; + int irow; for (int itype=0; itype<1; itype++){ const int typeoffset_local = ndims_peratom*nperdim*itype; const int typeoffset_global = nperdim*itype; //printf("----- nperdim: %d\n", nperdim); - /*nperdim = ncoeff set previsouly*/ for (int icoeff = 0; icoeff < nperdim; icoeff++) { //printf("----- icoeff: %d\n", icoeff); + dbiri_indx=0; for (int i = 0; i < atom->nlocal; i++) { //printf("i: %d\n", i); - + //int dbiri_indx = 0; + //int irow; for (int jj=0; jjtag[i]-1] + 3*jj; - int irow = dbirj_row_indx+bik_rows; + int snap_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*jj; + //printf("snap_row_indx: %d\n", snap_row_indx); + //int irow = dbirj_row_indx+bik_rows; + irow = snap_row_indx + bik_rows; //printf(" row_indx, irow: %d %d\n", dbirj_row_indx, irow); snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; //printf(" irow: %d\n", irow); snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; //printf(" irow: %d\n", irow); snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+2][icoeff]; + dbiri_indx = dbiri_indx+3; } + // Put dBi/dRi at end of each dBj/dRi chunk. + //int dbiri_row_indx; + irow = dbiri_indx + bik_rows; + //printf("dbiri_indx: %d\n", dbiri_indx); + //printf("dbiri: %f %f %f\n", dbiri[3*i+0][icoeff], dbiri[3*i+1][icoeff], dbiri[3*i+2][icoeff]); + snap[irow++][icoeff+typeoffset_global] += dbiri[3*i+0][icoeff]; + //printf(" irow: %d\n", irow); + snap[irow++][icoeff+typeoffset_global] += dbiri[3*i+1][icoeff]; + //printf(" irow: %d\n", irow); + snap[irow][icoeff+typeoffset_global] += dbiri[3*i+2][icoeff]; - - //int dbirj_row_indx = 3*neighsum[atom->tag[i]-1] + 3*icounter[atom->tag[i]-1]; - //printf(" row_indx: %d\n", dbirj_row_indx); - - /* - double *snadi = snap_peratom[i]+typeoffset_local; - int iglobal = atom->tag[i]; - if (icoeff==4){ - if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ - //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); - } - } - int irow = 3*(iglobal-1)+bik_rows; - //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; - snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; - */ + dbiri_indx = dbiri_indx+3; } } } + //printf(" Accumulated to global array.\n"); } @@ -784,22 +774,26 @@ void ComputeSnap::compute_array() // accumulate bispectrum virial contributions to global array - dbdotr_compute(); + //dbdotr_compute(); // sum up over all processes -printf("`-`-`-` snap[50][6]: %f\n", snap[50][6]); -printf("`-`-`-` snapall[50][6]: %f\n", snapall[50][6]); MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); // assign energy to last column - for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; - int irow = 0; - double reference_energy = c_pe->compute_scalar(); - snapall[irow][lastcol] = reference_energy; + if (dbirjflag){ + + } + else{ + for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; + int irow = 0; + double reference_energy = c_pe->compute_scalar(); + snapall[irow][lastcol] = reference_energy; + } // assign virial stress to last column // switch to Voigt notation c_virial->compute_vector(); + /* irow += 3*natoms+bik_rows; snapall[irow++][lastcol] = c_virial->vector[0]; snapall[irow++][lastcol] = c_virial->vector[1]; @@ -807,10 +801,9 @@ printf("`-`-`-` snapall[50][6]: %f\n", snapall[50][6]); snapall[irow++][lastcol] = c_virial->vector[5]; snapall[irow++][lastcol] = c_virial->vector[4]; snapall[irow][lastcol] = c_virial->vector[3]; + */ //}// else - - printf("----- End of compute_array.\n"); } /* ---------------------------------------------------------------------- @@ -824,7 +817,7 @@ void ComputeSnap::dbdotr_compute() int irow0; if (dbirjflag){ - irow0 = bik_rows+dbirj_rows; + irow0 = bik_rows+dbirj_rows+(3*natoms); } else{ irow0 = bik_rows+ndims_force*natoms; @@ -861,10 +854,11 @@ void ComputeSnap::dbdotr_compute() void ComputeSnap::get_dbirj_length() { + + memory->destroy(snap); + memory->destroy(snapall); // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - //memory->destroy(snap); - //memory->destroy(snapall); dbirj_rows = 0; const int inum = list->inum; const int* const ilist = list->ilist; @@ -877,8 +871,8 @@ void ComputeSnap::get_dbirj_length() memory->create(neighsum, inum, "snap:neighsum"); memory->create(nneighs, inum, "snap:nneighs"); memory->create(icounter, inum, "snap:icounter"); - memory->create(dbiri, 3*inum,ncoeff, "snap:dbiri"); - for (int ii=0; iicreate(dbiri, 3*atom->nlocal,ncoeff, "snap:dbiri"); + for (int ii=0; ii<3*atom->nlocal; ii++){ for (int icoeff=0; icoeffcreate(dbirj, dbirj_rows, ncoeff, "snap:dbirj"); + for (int i=0; inlocal; // Add 3*N for dBi/dRi //printf("----- dbirj_rows: %d\n", dbirj_rows); //printf("----- end of dbirj length.\n"); diff --git a/src/ML-SNAP/compute_snapneigh.cpp b/src/ML-SNAP/compute_snapneigh.cpp index dd6146e154..eef18a93f4 100644 --- a/src/ML-SNAP/compute_snapneigh.cpp +++ b/src/ML-SNAP/compute_snapneigh.cpp @@ -82,7 +82,7 @@ ComputeSnapneigh::ComputeSnapneigh(LAMMPS *lmp, int narg, char **arg) : memory->create(cutsq,ntypes+1,ntypes+1,"snapneigh:cutsq"); for (int i = 1; i <= ntypes; i++) { cut = 2.0*radelem[i]*rcutfac; - printf("cut: %f\n", cut); + //printf("cut: %f\n", cut); if (cut > cutmax) cutmax = cut; cutsq[i][i] = cut*cut; for (int j = i+1; j <= ntypes; j++) { @@ -235,12 +235,12 @@ ComputeSnapneigh::~ComputeSnapneigh() } if (dbirjflag){ - printf("dbirj_rows: %d\n", dbirj_rows); - memory->destroy(dbirj); + //printf("dbirj_rows: %d\n", dbirj_rows); + //memory->destroy(dbirj); memory->destroy(nneighs); memory->destroy(neighsum); memory->destroy(icounter); - memory->destroy(dbiri); + //memory->destroy(dbiri); } } @@ -276,12 +276,12 @@ void ComputeSnapneigh::compute_array() { if (dbirjflag){ - printf("----- dbirjflag true.\n"); + //printf("----- dbirjflag true.\n"); get_dbirj_length(); - printf("----- got dbirj_length\n"); + //printf("----- got dbirj_length\n"); } - printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); + //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); //else{ int ntotal = atom->nlocal + atom->nghost; @@ -306,6 +306,7 @@ void ComputeSnapneigh::compute_array() int ninside; int numneigh_sum = 0; int dbirj_row_indx; + int dbiri_indx=0; for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; @@ -358,7 +359,10 @@ void ComputeSnapneigh::compute_array() jelem = map[jtype]; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { if (dbirjflag){ - dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + //dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. + //dbirj_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*icounter[atom->tag[j]-1]; // 3*tagi is to leave space for dBi/dRi + dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] + 3*(atom->tag[j]-1); // THIS IS WRONG, SEE NEXT VAR. + //printf("--- %d %d %d %d\n", dbirj_row_indx, 3*neighsum[atom->tag[i]-1], 3*(atom->tag[i]-1), jj); //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); icounter[atom->tag[j]-1] += 1; @@ -369,13 +373,32 @@ void ComputeSnapneigh::compute_array() neighs[dbirj_row_indx+0][1] = atom->tag[j]; neighs[dbirj_row_indx+1][1] = atom->tag[j]; neighs[dbirj_row_indx+2][1] = atom->tag[j]; + + neighs[dbirj_row_indx+0][2] = 0; + neighs[dbirj_row_indx+1][2] = 1; + neighs[dbirj_row_indx+2][2] = 2; + + dbiri_indx = dbiri_indx+3; } } } - // for (int jj = 0; jj < ninside; jj++) - //printf("---- irow after jj loop: %d\n", irow); + //printf("--- dbiri_indx: %d\n", dbiri_indx); + // Put dBi/dRi in + neighs[dbiri_indx+0][0] = atom->tag[i]; + neighs[dbiri_indx+1][0] = atom->tag[i]; + neighs[dbiri_indx+2][0] = atom->tag[i]; + + neighs[dbiri_indx+0][1] = atom->tag[i]; + neighs[dbiri_indx+1][1] = atom->tag[i]; + neighs[dbiri_indx+2][1] = atom->tag[i]; + + neighs[dbiri_indx+0][2] = 0; + neighs[dbiri_indx+1][2] = 1; + neighs[dbiri_indx+2][2] = 2; + + dbiri_indx = dbiri_indx+3; } numneigh_sum += ninside; @@ -404,14 +427,14 @@ void ComputeSnapneigh::compute_array() snapall[irow][lastcol] = reference_energy; */ - + /* for (int i=0; imask; double** const x = atom->x; //printf("----- inum: %d\n", inum); - memory->create(neighsum, inum, "snapneigh:neighsum"); - memory->create(nneighs, inum, "snapneigh:nneighs"); - memory->create(icounter, inum, "snapneigh:icounter"); - memory->create(dbiri, 3*inum,ncoeff, "snapneigh:dbiri"); + memory->create(neighsum, atom->nlocal, "snapneigh:neighsum"); + memory->create(nneighs, atom->nlocal, "snapneigh:nneighs"); + memory->create(icounter, atom->nlocal, "snapneigh:icounter"); + //memory->create(dbiri, 3*inum,ncoeff, "snapneigh:dbiri"); + /* for (int ii=0; iicreate(dbirj, dbirj_rows, ncoeff, "snapneigh:dbirj"); - memory->create(neighs, dbirj_rows, 2, "snapneigh:neighs"); - size_array_rows = dbirj_rows; - size_array_cols = 2; + size_array_rows = dbirj_rows+(3*atom->nlocal); + size_array_cols = 3; + //memory->create(dbirj, dbirj_rows, ncoeff, "snapneigh:dbirj"); + memory->create(neighs, size_array_rows, size_array_cols, "snapneigh:neighs"); + //vector = neighs; array = neighs; // Set size array rows which now depends on dbirj_rows. From 880382e26ad3988b41c601c7d03e776599a55534 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Mon, 6 Jun 2022 17:43:47 -0600 Subject: [PATCH 05/75] Compute snap array contains force indices in last columns. --- src/ML-SNAP/compute_snap.cpp | 80 ++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index be06df4b68..b40d86546b 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -204,7 +204,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; dbirj_rows = ndims_force*natoms; size_array_rows = bik_rows+dbirj_rows+ndims_virial; - if (dbirjflag) size_array_cols = nperdim; + if (dbirjflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[2], and cartesian index else size_array_cols = nperdim*atom->ntypes+1; lastcol = size_array_cols-1; @@ -528,10 +528,35 @@ void ComputeSnap::compute_array() dbirj[dbirj_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; dbirj[dbirj_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; dbirj[dbirj_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; + if (icoeff==(ncoeff-1)){ + dbirj[dbirj_row_indx+0][ncoeff] = atom->tag[i]; + dbirj[dbirj_row_indx+0][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+0][ncoeff+2] = 0; + dbirj[dbirj_row_indx+1][ncoeff] = atom->tag[i]; + dbirj[dbirj_row_indx+1][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+1][ncoeff+2] = 1; + dbirj[dbirj_row_indx+2][ncoeff] = atom->tag[i]; + dbirj[dbirj_row_indx+2][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+2][ncoeff+2] = 2; + } // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i. dbiri[3*(atom->tag[i]-1)+0][icoeff] -= snaptr->dblist[icoeff][0]; dbiri[3*(atom->tag[i]-1)+1][icoeff] -= snaptr->dblist[icoeff][1]; dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; + // Get last columns + if (icoeff==(ncoeff-1)){ + dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; + + dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; + + dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; + } } @@ -696,11 +721,31 @@ void ComputeSnap::compute_array() //int irow = dbirj_row_indx+bik_rows; irow = snap_row_indx + bik_rows; //printf(" row_indx, irow: %d %d\n", dbirj_row_indx, irow); - snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; + // x-coordinate + snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbirj[dbirj_row_indx+0][ncoeff]; + snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+0][ncoeff+1]; + snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+0][ncoeff+2]; + } + irow++; //printf(" irow: %d\n", irow); - snap[irow++][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; + // y-coordinate + snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbirj[dbirj_row_indx+1][ncoeff]; + snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+1][ncoeff+1]; + snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+1][ncoeff+2]; + } + irow++; //printf(" irow: %d\n", irow); + // z-coordinate snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+2][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbirj[dbirj_row_indx+2][ncoeff]; + snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+2][ncoeff+1]; + snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+2][ncoeff+2]; + } dbiri_indx = dbiri_indx+3; } // Put dBi/dRi at end of each dBj/dRi chunk. @@ -708,11 +753,30 @@ void ComputeSnap::compute_array() irow = dbiri_indx + bik_rows; //printf("dbiri_indx: %d\n", dbiri_indx); //printf("dbiri: %f %f %f\n", dbiri[3*i+0][icoeff], dbiri[3*i+1][icoeff], dbiri[3*i+2][icoeff]); - snap[irow++][icoeff+typeoffset_global] += dbiri[3*i+0][icoeff]; + // x-coordinate + snap[irow][icoeff+typeoffset_global] += dbiri[3*i+0][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbiri[3*i+0][ncoeff]; + snap[irow][ncoeff+1] += dbiri[3*i+0][ncoeff+1]; + snap[irow][ncoeff+2] += dbiri[3*i+0][ncoeff+2]; + } + irow++; //printf(" irow: %d\n", irow); - snap[irow++][icoeff+typeoffset_global] += dbiri[3*i+1][icoeff]; + // y-coordinate + snap[irow][icoeff+typeoffset_global] += dbiri[3*i+1][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbiri[3*i+1][ncoeff]; + snap[irow][ncoeff+1] += dbiri[3*i+1][ncoeff+1]; + snap[irow][ncoeff+2] += dbiri[3*i+1][ncoeff+2]; + } + irow++; //printf(" irow: %d\n", irow); snap[irow][icoeff+typeoffset_global] += dbiri[3*i+2][icoeff]; + if (icoeff==(ncoeff-1)){ + snap[irow][ncoeff] += dbiri[3*i+2][ncoeff]; + snap[irow][ncoeff+1] += dbiri[3*i+2][ncoeff+1]; + snap[irow][ncoeff+2] += dbiri[3*i+2][ncoeff+2]; + } dbiri_indx = dbiri_indx+3; } @@ -871,7 +935,7 @@ void ComputeSnap::get_dbirj_length() memory->create(neighsum, inum, "snap:neighsum"); memory->create(nneighs, inum, "snap:nneighs"); memory->create(icounter, inum, "snap:icounter"); - memory->create(dbiri, 3*atom->nlocal,ncoeff, "snap:dbiri"); + memory->create(dbiri, 3*atom->nlocal,ncoeff+3, "snap:dbiri"); for (int ii=0; ii<3*atom->nlocal; ii++){ for (int icoeff=0; icoeffcreate(dbirj, dbirj_rows, ncoeff, "snap:dbirj"); + memory->create(dbirj, dbirj_rows, ncoeff+3, "snap:dbirj"); for (int i=0; i Date: Thu, 9 Jun 2022 23:22:16 -0400 Subject: [PATCH 06/75] reaxff/species delete keyword --- src/REAXFF/fix_reaxff_species.cpp | 225 +++++++++++++++++++++++++++++- src/REAXFF/fix_reaxff_species.h | 16 ++- 2 files changed, 232 insertions(+), 9 deletions(-) diff --git a/src/REAXFF/fix_reaxff_species.cpp b/src/REAXFF/fix_reaxff_species.cpp index a89cd6f0dc..9f39afd793 100644 --- a/src/REAXFF/fix_reaxff_species.cpp +++ b/src/REAXFF/fix_reaxff_species.cpp @@ -19,6 +19,7 @@ #include "fix_reaxff_species.h" #include "atom.h" +#include "atom_vec.h" #include "comm.h" #include "domain.h" #include "error.h" @@ -45,7 +46,8 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, { if (narg < 7) error->all(FLERR, "Illegal fix reaxff/species command"); - force_reneighbor = 0; + force_reneighbor = 1; + next_reneighbor = -1; vector_flag = 1; size_vector = 2; @@ -125,8 +127,10 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, MolName = nullptr; MolType = nullptr; NMol = nullptr; + Mol2Spec = nullptr; nd = nullptr; molmap = nullptr; + mark = nullptr; nmax = 0; setupflag = 0; @@ -142,10 +146,11 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, // optional args eletype = nullptr; - ele = filepos = nullptr; + ele = filepos = filedel = nullptr; eleflag = posflag = padflag = 0; + delflag = masslimitflag = 0; - singlepos_opened = multipos_opened = 0; + singlepos_opened = multipos_opened = del_opened = 0; multipos = 0; posfreq = 0; @@ -180,6 +185,43 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, eleflag = 1; iarg += ntypes + 1; + // delete species + } else if (strcmp(arg[iarg],"delete") == 0) { + delflag = 1; + + filedel = new char[255]; + strcpy(filedel,arg[iarg+1]); + if (me == 0) { + fdel = fopen(filedel, "w"); + if (fdel == nullptr) error->one(FLERR,"Cannot open fix reaxff/species delete file"); + } + + del_opened = 1; + + if (strcmp(arg[iarg+2],"masslimit") == 0) { + masslimitflag = 1; + massmin = atof(arg[iarg+3]); + massmax = atof(arg[iarg+4]); + iarg += 5; + } else { + ndelspec = atoi(arg[iarg+2]); + if (iarg+ndelspec+3 > narg) error->all(FLERR,"Illegal fix reaxff/species command"); + + del_species.resize(ndelspec); + for (int i = 0; i < ndelspec; i ++) + del_species[i] = arg[iarg+3+i]; + + if (me == 0) { + fprintf(fdel,"Timestep"); + for (i = 0; i < ndelspec; i++) + fprintf(fdel,"\t%s",del_species[i].c_str()); + fprintf(fdel,"\n"); + fflush(fdel); + } + + iarg += ndelspec + 3; + } + // position of molecules } else if (strcmp(arg[iarg], "position") == 0) { if (iarg + 3 > narg) error->all(FLERR, "Illegal fix reaxff/species command"); @@ -229,6 +271,7 @@ FixReaxFFSpecies::~FixReaxFFSpecies() memory->destroy(nd); memory->destroy(Name); memory->destroy(NMol); + memory->destroy(Mol2Spec); memory->destroy(MolType); memory->destroy(MolName); @@ -358,6 +401,8 @@ void FixReaxFFSpecies::Output_ReaxFF_Bonds(bigint ntimestep, FILE * /*fp*/) if (me == 0) fflush(pos); } + if (delflag) DeleteSpecies(Nmole, Nspec); + nvalid += nfreq; } @@ -540,6 +585,11 @@ void FixReaxFFSpecies::FindSpecies(int Nmole, int &Nspec) memory->create(Nameall, ntypes, "reaxff/species:Nameall"); memory->create(NMolall, Nmole, "reaxff/species:NMolall"); + memory->destroy(Mol2Spec); + Mol2Spec = nullptr; + memory->create(Mol2Spec, Nmole, "reaxff/species:Mol2Spec"); + for (m = 0; m < Nmole; m++) Mol2Spec[m] = -1; + for (m = 1, Nspec = 0; m <= Nmole; m++) { for (n = 0; n < ntypes; n++) Name[n] = 0; for (n = 0, flag_mol = 0; n < nlocal; n++) { @@ -563,11 +613,15 @@ void FixReaxFFSpecies::FindSpecies(int Nmole, int &Nspec) flag_spec = 0; for (l = 0; l < ntypes; l++) if (MolName[ntypes * k + l] != Name[l]) flag_spec = 1; - if (flag_spec == 0) NMol[k]++; + if (flag_spec == 0) { + NMol[k]++; + Mol2Spec[m-1] = k; + } flag_identity *= flag_spec; } if (Nspec == 0 || flag_identity == 1) { for (l = 0; l < ntypes; l++) MolName[ntypes * Nspec + l] = Name[l]; + Mol2Spec[m-1] = Nspec; Nspec++; } } @@ -758,6 +812,169 @@ void FixReaxFFSpecies::WritePos(int Nmole, int Nspec) /* ---------------------------------------------------------------------- */ +void FixReaxFFSpecies::DeleteSpecies(int Nmole, int Nspec) +{ + int i, j, m, n, k, itype, cid; + int ndel, ndelone, count, count_tmp; + int *Nameall; + int *mask = atom->mask; + double localmass, totalmass; + double **spec_atom = f_SPECBOND->array_atom; + std::string species_str; + + AtomVec *avec = atom->avec; + + mark = nullptr; + memory->create(mark, nlocal, "reaxff/species:mark"); + for (i = 0; i < nlocal; i++) mark[i] = 0; + + Nameall = nullptr; + memory->create(Nameall, ntypes, "reaxff/species:Nameall"); + + int ndelcomm; + if (masslimitflag) ndelcomm = Nspec; + else ndelcomm = ndelspec; + + double *deletecount; + memory->create(deletecount, ndelcomm, "reaxff/species:deletecount"); + for (i = 0; i < ndelcomm; i++) deletecount[i] = 0; + + int nmarklist; + int *marklist; + memory->create(marklist, nlocal, "reaxff/species:marklist"); + + for (m = 1; m <= Nmole; m++) { + localmass = totalmass = count = nmarklist = 0; + for (n = 0; n < ntypes; n++) Name[n] = 0; + + for (i = 0; i < nlocal; i++) { + if (!(mask[i] & groupbit)) continue; + cid = nint(clusterID[i]); + if (cid == m) { + itype = atom->type[i]-1; + Name[itype]++; + count++; + marklist[nmarklist++] = i; + localmass += atom->mass[atom->type[i]]; + } + } + + MPI_Allreduce(&count, &count_tmp, 1, MPI_INT, MPI_SUM, world); + count = count_tmp; + + MPI_Allreduce(Name, Nameall, ntypes, MPI_INT, MPI_SUM, world); + for (n = 0; n < ntypes; n++) Name[n] = Nameall[n]; + + MPI_Allreduce(&localmass, &totalmass, 1 , MPI_DOUBLE, MPI_SUM, world); + + species_str = ""; + for (j = 0; j < ntypes; j++) { + if (Name[j] != 0) { + if (eletype) species_str += eletype[j]; + else species_str += ele[j]; + if (Name[j] != 1) species_str += fmt::format("{}",Name[j]); + } + } + + if (masslimitflag) { + + // find corresponding moltype + + if (totalmass > massmin && totalmass < massmax) { + for (j = 0; j < nmarklist; j++) { + mark[marklist[j]] = 1; + deletecount[Mol2Spec[m-1]] += 1.0 / (double) count; + } + } + } else { + if (count > 0) { + for (i = 0; i < ndelspec; i++) { + if (del_species[i] == species_str) { + for (j = 0; j < nmarklist; j++) { + mark[marklist[j]] = 1; + deletecount[i] += 1.0 / (double) count; + } + break; + } + } + } + } + } + + // delete atoms. loop in reverse order to avoid copying marked atoms + + ndel = ndelone = 0; + for (i = atom->nlocal-1; i >= 0; i--) { + if (mark[i] == 1) { + avec->copy(atom->nlocal-1,i,1); + atom->nlocal--; + ndelone++; + } + } + + MPI_Allreduce(&ndelone, &ndel, 1, MPI_INT, MPI_SUM, world); + + atom->natoms -= ndel; + + if (me == 0) MPI_Reduce(MPI_IN_PLACE, deletecount, ndelcomm, MPI_DOUBLE, MPI_SUM, 0, world); + else MPI_Reduce(deletecount, deletecount, ndelcomm, MPI_DOUBLE, MPI_SUM, 0, world); + + if (me == 0) { + if (masslimitflag) { + int printflag = 0; + for (int m = 0; m < Nspec; m++) { + if (deletecount[m] > 0) { + if (printflag == 0) { + fprintf(fdel, "Timestep %lld", update->ntimestep); + printflag = 1; + } + fprintf(fdel, " %g ", deletecount[m]); + for (j = 0; j < ntypes; j ++) { + int itemp = MolName[ntypes * m + j]; + if (itemp != 0) { + if (eletype) fprintf(fdel, "%s", eletype[j]); + else fprintf(fdel, "%c", ele[j]); + if (itemp != 1) fprintf(fdel, "%d", itemp); + } + } + } + } + if (printflag) { + fprintf(fdel, "\n"); + fflush(fdel); + } + } else { + int writeflag = 0; + for (i = 0; i < ndelspec; i++) + if (deletecount[i]) writeflag = 1; + + if (writeflag) { + fprintf(fdel, "%lld", update->ntimestep); + for (i = 0; i < ndelspec; i++) { + fprintf(fdel, "\t%g", deletecount[i]); + } + fprintf(fdel, "\n"); + fflush(fdel); + } + } + } + + if (ndel && (atom->map_style != Atom::MAP_NONE)) { + atom->nghost = 0; + atom->map_init(); + atom->map_set(); + } + + next_reneighbor = update->ntimestep; + + memory->destroy(Nameall); + memory->destroy(marklist); + memory->destroy(mark); + memory->destroy(deletecount); +} + +/* ---------------------------------------------------------------------- */ + double FixReaxFFSpecies::compute_vector(int n) { if (n == 0) return vector_nmole; diff --git a/src/REAXFF/fix_reaxff_species.h b/src/REAXFF/fix_reaxff_species.h index f441c3bc92..64d408ed24 100644 --- a/src/REAXFF/fix_reaxff_species.h +++ b/src/REAXFF/fix_reaxff_species.h @@ -45,19 +45,24 @@ class FixReaxFFSpecies : public Fix { protected: int me, nprocs, nmax, nlocal, ntypes, ntotal; - int nrepeat, nfreq, posfreq, compressed; + int nrepeat, nfreq, posfreq, compressed, ndelspec; int Nmoltype, vector_nmole, vector_nspec; - int *Name, *MolName, *NMol, *nd, *MolType, *molmap; + int *Name, *MolName, *NMol, *nd, *MolType, *molmap, *mark; + int *Mol2Spec; double *clusterID; AtomCoord *x0; double bg_cut; double **BOCut; - FILE *fp, *pos; + std::vector del_species; + + FILE *fp, *pos, *fdel; int eleflag, posflag, multipos, padflag, setupflag; - int singlepos_opened, multipos_opened; - char *ele, **eletype, *filepos; + int delflag, masslimitflag; + double massmin, massmax; + int singlepos_opened, multipos_opened, del_opened; + char *ele, **eletype, *filepos, *filedel; void Output_ReaxFF_Bonds(bigint, FILE *); AtomCoord chAnchor(AtomCoord, AtomCoord); @@ -65,6 +70,7 @@ class FixReaxFFSpecies : public Fix { void SortMolecule(int &); void FindSpecies(int, int &); void WriteFormulas(int, int); + void DeleteSpecies(int, int); int CheckExistence(int, int); int nint(const double &); From 716a012dbe9d449501815be9cd1b97f50795a264 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Tue, 14 Jun 2022 00:51:53 -0400 Subject: [PATCH 07/75] add keyword for 'delete species list' option also add check that only one sub-keyword is used --- src/REAXFF/fix_reaxff_species.cpp | 20 ++++++++++++-------- src/REAXFF/fix_reaxff_species.h | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/REAXFF/fix_reaxff_species.cpp b/src/REAXFF/fix_reaxff_species.cpp index 9f39afd793..4b84a85236 100644 --- a/src/REAXFF/fix_reaxff_species.cpp +++ b/src/REAXFF/fix_reaxff_species.cpp @@ -148,7 +148,7 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, eletype = nullptr; ele = filepos = filedel = nullptr; eleflag = posflag = padflag = 0; - delflag = masslimitflag = 0; + delflag = specieslistflag = masslimitflag = 0; singlepos_opened = multipos_opened = del_opened = 0; multipos = 0; @@ -188,7 +188,6 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, // delete species } else if (strcmp(arg[iarg],"delete") == 0) { delflag = 1; - filedel = new char[255]; strcpy(filedel,arg[iarg+1]); if (me == 0) { @@ -199,17 +198,19 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, del_opened = 1; if (strcmp(arg[iarg+2],"masslimit") == 0) { + if (iarg+5 > narg) error->all(FLERR,"Illegal fix reaxff/species command"); masslimitflag = 1; massmin = atof(arg[iarg+3]); massmax = atof(arg[iarg+4]); iarg += 5; - } else { - ndelspec = atoi(arg[iarg+2]); - if (iarg+ndelspec+3 > narg) error->all(FLERR,"Illegal fix reaxff/species command"); + } else if (strcmp(arg[iarg+2],"specieslist") == 0) { + specieslistflag = 1; + ndelspec = atoi(arg[iarg+3]); + if (iarg+ndelspec+4 > narg) error->all(FLERR,"Illegal fix reaxff/species command"); del_species.resize(ndelspec); for (int i = 0; i < ndelspec; i ++) - del_species[i] = arg[iarg+3+i]; + del_species[i] = arg[iarg+4+i]; if (me == 0) { fprintf(fdel,"Timestep"); @@ -217,9 +218,9 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, fprintf(fdel,"\t%s",del_species[i].c_str()); fprintf(fdel,"\n"); fflush(fdel); - } + } else error->all(FLERR, "Illegal fix reaxff/species command"); - iarg += ndelspec + 3; + iarg += ndelspec + 4; } // position of molecules @@ -255,6 +256,9 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, if (ntypes > 3) ele[3] = 'N'; } + if (delflag && specieslistflag && masslimitflag) + error->all(FLERR, "Illegal fix reaxff/species command"); + vector_nmole = 0; vector_nspec = 0; } diff --git a/src/REAXFF/fix_reaxff_species.h b/src/REAXFF/fix_reaxff_species.h index 64d408ed24..e272ff7d6c 100644 --- a/src/REAXFF/fix_reaxff_species.h +++ b/src/REAXFF/fix_reaxff_species.h @@ -59,7 +59,7 @@ class FixReaxFFSpecies : public Fix { FILE *fp, *pos, *fdel; int eleflag, posflag, multipos, padflag, setupflag; - int delflag, masslimitflag; + int delflag, specieslistflag, masslimitflag; double massmin, massmax; int singlepos_opened, multipos_opened, del_opened; char *ele, **eletype, *filepos, *filedel; From fbf9f62eef63eb5cf0ef359a3fb280a46d3c8660 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Tue, 14 Jun 2022 01:18:46 -0400 Subject: [PATCH 08/75] add credits --- src/REAXFF/fix_reaxff_species.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/REAXFF/fix_reaxff_species.cpp b/src/REAXFF/fix_reaxff_species.cpp index 4b84a85236..5325086ef9 100644 --- a/src/REAXFF/fix_reaxff_species.cpp +++ b/src/REAXFF/fix_reaxff_species.cpp @@ -14,6 +14,7 @@ /* ---------------------------------------------------------------------- Contributing authors: Ray Shan (Sandia, tnshan@sandia.gov) Oleg Sergeev (VNIIA, sergeev@vniia.ru) + Jacob Gissinger (NASA, jacob.r.gissinger@gmail.com), 'delete' keyword ------------------------------------------------------------------------- */ #include "fix_reaxff_species.h" From f423c32f42def47cec706fa689373fc7d7bcd204 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Tue, 14 Jun 2022 01:23:36 -0400 Subject: [PATCH 09/75] reaxff delete species docs --- doc/src/fix_reaxff_species.rst | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/src/fix_reaxff_species.rst b/doc/src/fix_reaxff_species.rst index a652777ba7..208ee31009 100644 --- a/doc/src/fix_reaxff_species.rst +++ b/doc/src/fix_reaxff_species.rst @@ -20,7 +20,7 @@ Syntax * Nfreq = calculate average bond-order every this many timesteps * filename = name of output file * zero or more keyword/value pairs may be appended -* keyword = *cutoff* or *element* or *position* +* keyword = *cutoff* or *element* or *position* or *delete* .. parsed-literal:: @@ -31,6 +31,14 @@ Syntax *position* value = posfreq filepos posfreq = write position files every this many timestep filepos = name of position output file + *delete* value = filedel keyword value + filedel = name of delete species output file + keyword = *specieslist* or *masslimit* + *specieslist* value = Nspecies Species1 Species2 ... + Nspecies = number of species in list + *masslimit* value = massmin massmax + massmin = minimum molecular weight of species to delete + massmax = maximum molecular weight of species to delete Examples """""""" @@ -40,6 +48,7 @@ Examples fix 1 all reaxff/species 10 10 100 species.out fix 1 all reaxff/species 1 2 20 species.out cutoff 1 1 0.40 cutoff 1 2 0.55 fix 1 all reaxff/species 1 100 100 species.out element Au O H position 1000 AuOH.pos + fix 1 all reaxff/species 1 100 100 species.out delete species.del masslimit 0 50 Description """"""""""" @@ -104,6 +113,30 @@ character appears in *filepos*, then one file per snapshot is written at *posfreq* and the "\*" character is replaced with the timestep value. For example, AuO.pos.\* becomes AuO.pos.0, AuO.pos.1000, etc. +The optional keyword *delete* enables the periodic removal of +molecules from the system. Criteria for deletion can be either a list +of specific chemical formulae or a range of molecular weights. +Molecules are deleted every *Nfreq* timesteps, and bond connectivity +is determined using the *Nevery* and *Nrepeat* keywords. The +*filedel* argument is the name of the output file that records the +species that are removed from the system. The *specieslist* keyword +permits specific chemical species to be deleted. The *Nspecies* +argument specifies how many species are eligible for deletion and is +followed by a list of chemical formulae, whose strings are compared to +species identified by this fix. For example, "specieslist 2 CO CO2" +deletes molecules that are identified as "CO" and "CO2" in the species +output file. When using the *specieslist* keyword, the *filedel* file +has the following format: the first line lists the chemical formulae +eligible for deletion, and each additional line contains the timestep +on which a molecule deletion occurs and the number of each species +deleted on that timestep. The *masslimit* keyword permits deletion of +molecules with molecular weights between *massmin* and *massmax*. +When using the *masslimit* keyword, each line of the *filedel* file +contains the timestep on which deletions occurs, followed by how many +of each species are deleted (with quantities preceding chemical +formulae). The *specieslist* and *masslimit* keywords cannot both be +used in the same *reaxff/species* fix. + ---------- The *Nevery*, *Nrepeat*, and *Nfreq* arguments specify on what From 215552eb562c2080dae5f6d8aab3049fcfe0fe66 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Tue, 14 Jun 2022 12:39:03 -0400 Subject: [PATCH 10/75] typo --- src/REAXFF/fix_reaxff_species.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/REAXFF/fix_reaxff_species.cpp b/src/REAXFF/fix_reaxff_species.cpp index 5325086ef9..e5298bdc4d 100644 --- a/src/REAXFF/fix_reaxff_species.cpp +++ b/src/REAXFF/fix_reaxff_species.cpp @@ -219,10 +219,10 @@ FixReaxFFSpecies::FixReaxFFSpecies(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, fprintf(fdel,"\t%s",del_species[i].c_str()); fprintf(fdel,"\n"); fflush(fdel); - } else error->all(FLERR, "Illegal fix reaxff/species command"); + } iarg += ndelspec + 4; - } + } else error->all(FLERR, "Illegal fix reaxff/species command"); // position of molecules } else if (strcmp(arg[iarg], "position") == 0) { From 5060a8b8a5853c2f500012a4c768bcf335c64315 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Tue, 14 Jun 2022 16:31:31 -0600 Subject: [PATCH 11/75] Make dB/dR indices start at zero in compute snap array. --- src/ML-SNAP/compute_snap.cpp | 77 ++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index b40d86546b..3927703f01 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -220,20 +220,26 @@ ComputeSnap::~ComputeSnap() { memory->destroy(snap); + //printf("---------------- 1\n"); memory->destroy(snapall); + //printf("---------------- 2\n"); memory->destroy(snap_peratom); + //printf("---------------- 3\n"); memory->destroy(radelem); + //printf("---------------- 4\n"); memory->destroy(wjelem); + //printf("---------------- 5\n"); memory->destroy(cutsq); + //printf("---------------- 6\n"); delete snaptr; - + //printf("---------------- 7\n"); if (chemflag) memory->destroy(map); if (switchinnerflag) { memory->destroy(sinnerelem); memory->destroy(dinnerelem); } - + //printf("---------------- 8\n"); if (dbirjflag){ //printf("dbirj_rows: %d\n", dbirj_rows); //printf("----- destroy dbirj\n"); @@ -248,6 +254,7 @@ ComputeSnap::~ComputeSnap() memory->destroy(dbiri); //printf("----- 5-1-1-1-1-1\n"); } + //printf("---------------- 9\n"); } /* ---------------------------------------------------------------------- */ @@ -529,14 +536,14 @@ void ComputeSnap::compute_array() dbirj[dbirj_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; dbirj[dbirj_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; if (icoeff==(ncoeff-1)){ - dbirj[dbirj_row_indx+0][ncoeff] = atom->tag[i]; - dbirj[dbirj_row_indx+0][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+0][ncoeff] = atom->tag[i]-1; + dbirj[dbirj_row_indx+0][ncoeff+1] = atom->tag[j]-1; dbirj[dbirj_row_indx+0][ncoeff+2] = 0; - dbirj[dbirj_row_indx+1][ncoeff] = atom->tag[i]; - dbirj[dbirj_row_indx+1][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+1][ncoeff] = atom->tag[i]-1; + dbirj[dbirj_row_indx+1][ncoeff+1] = atom->tag[j]-1; dbirj[dbirj_row_indx+1][ncoeff+2] = 1; - dbirj[dbirj_row_indx+2][ncoeff] = atom->tag[i]; - dbirj[dbirj_row_indx+2][ncoeff+1] = atom->tag[j]; + dbirj[dbirj_row_indx+2][ncoeff] = atom->tag[i]-1; + dbirj[dbirj_row_indx+2][ncoeff+1] = atom->tag[j]-1; dbirj[dbirj_row_indx+2][ncoeff+2] = 2; } // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i. @@ -545,16 +552,16 @@ void ComputeSnap::compute_array() dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; // Get last columns if (icoeff==(ncoeff-1)){ - dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]; - dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; - dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]; - dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; - dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]; - dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]; + dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; } } @@ -821,7 +828,15 @@ void ComputeSnap::compute_array() */ // accumulate forces to global array if (dbirjflag){ + for (int i=0; inlocal; i++){ + + int iglobal = atom->tag[i]; + snap[iglobal-1][ncoeff+0] = atom->f[i][0]; + snap[iglobal-1][ncoeff+1] = atom->f[i][1]; + snap[iglobal-1][ncoeff+2] = atom->f[i][2]; + + } } else{ for (int i = 0; i < atom->nlocal; i++) { @@ -838,13 +853,22 @@ void ComputeSnap::compute_array() // accumulate bispectrum virial contributions to global array - //dbdotr_compute(); + if (dbirjflag){ + + } + else{ + dbdotr_compute(); + } // sum up over all processes MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); // assign energy to last column if (dbirjflag){ - + // Assign reference energy right after the dbirj rows, first column. + int irow = bik_rows + dbirj_rows + 3*natoms; // add 3N for the dBi/dRi rows + //printf("irow bik_rows dbirj_rows: %d %d %d\n", irow, bik_rows, dbirj_rows); + double reference_energy = c_pe->compute_scalar(); + snapall[irow][0] = reference_energy; } else{ for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; @@ -857,15 +881,18 @@ void ComputeSnap::compute_array() // switch to Voigt notation c_virial->compute_vector(); - /* - irow += 3*natoms+bik_rows; - snapall[irow++][lastcol] = c_virial->vector[0]; - snapall[irow++][lastcol] = c_virial->vector[1]; - snapall[irow++][lastcol] = c_virial->vector[2]; - snapall[irow++][lastcol] = c_virial->vector[5]; - snapall[irow++][lastcol] = c_virial->vector[4]; - snapall[irow][lastcol] = c_virial->vector[3]; - */ + if (dbirjflag){ + + } + else{ + int irow = 3*natoms+bik_rows; + snapall[irow++][lastcol] = c_virial->vector[0]; + snapall[irow++][lastcol] = c_virial->vector[1]; + snapall[irow++][lastcol] = c_virial->vector[2]; + snapall[irow++][lastcol] = c_virial->vector[5]; + snapall[irow++][lastcol] = c_virial->vector[4]; + snapall[irow][lastcol] = c_virial->vector[3]; + } //}// else } From effae2c01a6473314193ad5180c8d9f6134e0218 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 17 Jun 2022 12:05:05 -0600 Subject: [PATCH 12/75] Added compute snap descriptor gradient example. --- examples/snap/README.md | 9 + examples/snap/compute_snap_dgrad.py | 130 +++++++++++++ python/examples/in.lj | 25 +++ python/examples/simple.py | 20 +- src/ML-SNAP/compute_snap.cpp | 283 +++++----------------------- 5 files changed, 224 insertions(+), 243 deletions(-) create mode 100644 examples/snap/README.md create mode 100644 examples/snap/compute_snap_dgrad.py create mode 100644 python/examples/in.lj diff --git a/examples/snap/README.md b/examples/snap/README.md new file mode 100644 index 0000000000..7c34fe7021 --- /dev/null +++ b/examples/snap/README.md @@ -0,0 +1,9 @@ +See `compute_snap_dgrad.py` for a test that compares the dBi/dRj from compute snap (`dbirjflag=1`) to the sum of dBi/dRj from usual compute snap (`dbirjflag=0`). + +The format of the global array from `dbirjflag=1` is as follows. + +The first N rows belong to bispectrum components for each atom, if we use `bikflag=1`. The first `K` columns correspond to each bispectrum coefficient. The final 3 columns contain reference force components for each atom. + +The rows after the first N rows contain dBi/dRj values for all pairs. These values are arranged in row chunks for each atom `j`, where all the rows in a chunk are associated with the neighbors `i` of `j`, as well as the self-terms where `i=j`. So for atom `j`, the number of rows is equal to the number of atoms within the SNAP cutoff, plus 1 for the `i=j` terms, times 3 for each Cartesian component. The total number of dBi/dRj rows is therefore equal to `N*(Nneigh+1)*3`, and `Nneigh` may be different for each atom. To facilitate with determining which row belong to which atom pair `ij`, the last 3 columns contain indices; the 3rd to last column contains global indices of atoms `i` (the neighbors), the 2nd to last column contains global indices of atoms `j`, and the last column contains an index 0,1,2 for the Cartesian component. Like the `bik` rows, the first `K` columns correspond to each bispectrum coefficient. + +Finally, the first column of the last row contains the reference energy. diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py new file mode 100644 index 0000000000..dc06ae1576 --- /dev/null +++ b/examples/snap/compute_snap_dgrad.py @@ -0,0 +1,130 @@ +""" +compute_snap_dgrad.py +Purpose: Demonstrate extraction of descriptor gradient (dB/dR) array from compute snap. + Show that dBi/dRj components summed over neighbors i yields same output as regular compute snap with dbirjflag=0. + This shows that the dBi/dRj components extracted with dbirjflag=1 are correct. +Serial syntax: + python compute_snap_dgrad.py +Parallel syntax: + mpirun -np 2 python compute_snap_dgrad.py +""" + +from __future__ import print_function +import sys +import ctypes +import numpy as np + +# uncomment this if running in parallel via mpi4py +#me = 0 +#from mpi4py import MPI +#me = MPI.COMM_WORLD.Get_rank() +#nprocs = MPI.COMM_WORLD.Get_size() + +from lammps import lammps, LMP_TYPE_ARRAY, LMP_STYLE_GLOBAL +cmds = ["-screen", "none", "-log", "none"] +lmp = lammps(cmdargs=cmds) + +def run_lammps(dbirjflag): + lmp.command("clear") + lmp.command("units metal") + lmp.command("boundary p p p") + lmp.command("atom_modify map hash") + lmp.command(f"lattice bcc {latparam}") + lmp.command(f"region box block 0 {nx} 0 {ny} 0 {nz}") + lmp.command(f"create_box {ntypes} box") + lmp.command(f"create_atoms {ntypes} box") + lmp.command("mass * 180.88") + lmp.command("displace_atoms all random 0.01 0.01 0.01 123456") + # Pair style + snap_options=f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dbirjflag {dbirjflag}' + lmp.command(f"pair_style zero {rcutfac}") + lmp.command(f"pair_coeff * *") + lmp.command(f"pair_style zbl {zblcutinner} {zblcutouter}") + lmp.command(f"pair_coeff * * {zblz} {zblz}") + # set up compute snap generating global array + lmp.command(f"compute snap all snap {snap_options}") + # Run + lmp.command(f"thermo 100") + lmp.command(f"run {nsteps}") + +# Declare simulation/structure variables +nsteps=0 +nrep=2 +latparam=2.0 +ntypes=2 +nx=nrep +ny=nrep +nz=nrep + +# Declare compute snap variables +twojmax=8 +m = (twojmax/2)+1 +rcutfac=1.0 +rfac0=0.99363 +rmin0=0 +radelem1=2.3 +radelem2=2.0 +wj1=1.0 +wj2=0.96 +quadratic=0 +bzero=0 +switch=0 +bikflag=1 +dbirjflag=1 + +# Declare reference potential variables +zblcutinner=4.0 +zblcutouter=4.8 +zblz=73 + +# Number of descriptors +if (twojmax % 2 == 0): + nd = int(m*(m+1)*(2*m+1)/6) +else: + nd = int(m*(m+1)*(m+2)/3) +print(f"Number of descriptors based on twojmax : {nd}") + +# Run lammps with dbirjflag on +run_lammps(1) + +# Get global snap array +lmp_snap = lmp.numpy.extract_compute("snap",0, 2) + +# Extract dBj/dRi (includes dBi/dRi) +natoms = lmp.get_natoms() +fref1 = lmp_snap[0:natoms,-3:].flatten() +eref1 = lmp_snap[-6,0] +dbdr_length = np.shape(lmp_snap)[0]-(natoms)-6 # Length of neighborlist pruned dbdr array +dBdR = lmp_snap[natoms:(natoms+dbdr_length),:] +force_indices = lmp_snap[natoms:(natoms+dbdr_length),-3:].astype(np.int32) + +# Sum over neighbors j for each atom i, like dbirjflag=0 does. +array1 = np.zeros((3*natoms,nd)) +a = 0 +for k in range(0,nd): + for l in range(0,dbdr_length): + j = force_indices[l,0] + i = force_indices[l,1] + #array1[3*(i-1)+a,k] += dBdR[l,k] + array1[3*(i)+a,k] += dBdR[l,k] + a = a+1 + if (a>2): + a=0 + +# Run lammps with dbirjflag off +run_lammps(0) + +# Get global snap array +lmp_snap = lmp.numpy.extract_compute("snap",0, 2) +natoms = lmp.get_natoms() +fref2 = lmp_snap[natoms:(natoms+3*natoms),-1] +eref2 = lmp_snap[0,-1] +array2 = lmp_snap[natoms:natoms+(3*natoms), nd:-1] + +# Sum the arrays obtained from dbirjflag on and off. +summ = array1 + array2 +#np.savetxt("sum.dat", summ) + +print(f"Maximum difference in descriptor sums: {np.max(summ)}") +print(f"Maximum difference in reference forces: {np.max(fref1-fref2)}") +print(f"Difference in reference energy: {np.max(eref1-eref2)}") diff --git a/python/examples/in.lj b/python/examples/in.lj new file mode 100644 index 0000000000..3dc80bdfea --- /dev/null +++ b/python/examples/in.lj @@ -0,0 +1,25 @@ +# 3d Lennard-Jones melt + +units lj +atom_style atomic +atom_modify map array + +lattice fcc 0.8442 +region box block 0 4 0 4 0 4 +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 + +variable fx atom fx + +run 10 diff --git a/python/examples/simple.py b/python/examples/simple.py index 8925ce48c0..2f9c191165 100755 --- a/python/examples/simple.py +++ b/python/examples/simple.py @@ -5,10 +5,10 @@ # Purpose: mimic operation of examples/COUPLE/simple/simple.cpp via Python # Serial syntax: simple.py in.lammps -# in.lammps = LAMMPS input script +# in.simple = LAMMPS input script -# Parallel syntax: mpirun -np 4 simple.py in.lammps -# in.lammps = LAMMPS input script +# Parallel syntax: mpirun -np 4 python simple.py in.simple +# in.simple = LAMMPS input script # also need to uncomment mpi4py sections below from __future__ import print_function @@ -27,9 +27,9 @@ infile = sys.argv[1] me = 0 # uncomment this if running in parallel via mpi4py -#from mpi4py import MPI -#me = MPI.COMM_WORLD.Get_rank() -#nprocs = MPI.COMM_WORLD.Get_size() +from mpi4py import MPI +me = MPI.COMM_WORLD.Get_rank() +nprocs = MPI.COMM_WORLD.Get_size() from lammps import lammps lmp = lammps() @@ -122,10 +122,10 @@ if me == 0: print("Gather post scatter subset:", boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) -lmp.reset_box([0,0,0],[10,10,8],0,0,0) +#lmp.reset_box([0,0,0],[10,10,8],0,0,0) -boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() -if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) +#boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() +#if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) # uncomment if running in parallel via mpi4py -#print("Proc %d out of %d procs has" % (me,nprocs), lmp) +print("Proc %d out of %d procs has" % (me,nprocs), lmp) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 3927703f01..d1b0980dfb 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -13,7 +13,6 @@ ------------------------------------------------------------------------- */ #include "compute_snap.h" - #include "sna.h" #include "atom.h" #include "update.h" @@ -25,6 +24,7 @@ #include "comm.h" #include "memory.h" #include "error.h" +#include "universe.h" // For MPI #include @@ -82,7 +82,6 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : memory->create(cutsq,ntypes+1,ntypes+1,"snap:cutsq"); for (int i = 1; i <= ntypes; i++) { cut = 2.0*radelem[i]*rcutfac; - //printf("cut: %f\n", cut); if (cut > cutmax) cutmax = cut; cutsq[i][i] = cut*cut; for (int j = i+1; j <= ntypes; j++) { @@ -201,10 +200,9 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : natoms = atom->natoms; bik_rows = 1; if (bikflag) bik_rows = natoms; - //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; dbirj_rows = ndims_force*natoms; size_array_rows = bik_rows+dbirj_rows+ndims_virial; - if (dbirjflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[2], and cartesian index + if (dbirjflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[j], and cartesian index else size_array_cols = nperdim*atom->ntypes+1; lastcol = size_array_cols-1; @@ -220,41 +218,25 @@ ComputeSnap::~ComputeSnap() { memory->destroy(snap); - //printf("---------------- 1\n"); memory->destroy(snapall); - //printf("---------------- 2\n"); memory->destroy(snap_peratom); - //printf("---------------- 3\n"); memory->destroy(radelem); - //printf("---------------- 4\n"); memory->destroy(wjelem); - //printf("---------------- 5\n"); memory->destroy(cutsq); - //printf("---------------- 6\n"); delete snaptr; - //printf("---------------- 7\n"); if (chemflag) memory->destroy(map); if (switchinnerflag) { memory->destroy(sinnerelem); memory->destroy(dinnerelem); } - //printf("---------------- 8\n"); if (dbirjflag){ - //printf("dbirj_rows: %d\n", dbirj_rows); - //printf("----- destroy dbirj\n"); memory->destroy(dbirj); - //printf("----- 1-1-1-1-1-1\n"); memory->destroy(nneighs); - //printf("----- 2-1-1-1-1-1\n"); memory->destroy(neighsum); - //printf("----- 3-1-1-1-1-1\n"); memory->destroy(icounter); - //printf("----- 4-1-1-1-1-1\n"); memory->destroy(dbiri); - //printf("----- 5-1-1-1-1-1\n"); } - //printf("---------------- 9\n"); } /* ---------------------------------------------------------------------- */ @@ -265,7 +247,6 @@ void ComputeSnap::init() error->all(FLERR,"Compute snap requires a pair style be defined"); if (cutmax > force->pair->cutforce){ - //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); } @@ -279,7 +260,6 @@ void ComputeSnap::init() // allocate memory for global array - //printf("----- dbirjflag: %d\n", dbirjflag); memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, @@ -319,18 +299,10 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { - //printf(" -----2 dbirjflag: %d\n", dbirjflag); if (dbirjflag){ - //printf("----- dbirjflag true.\n"); get_dbirj_length(); - //printf("----- got dbirj_length\n"); } - //else{ - // printf("----- dbirjflag false.\n"); - //} - //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); - //else{ int ntotal = atom->nlocal + atom->nghost; invoked_array = update->ntimestep; @@ -343,11 +315,10 @@ void ComputeSnap::compute_array() memory->create(snap_peratom,nmax,size_peratom, "snap:snap_peratom"); } + // clear global array - //printf("size_array_rows: %d\n", size_array_rows); for (int irow = 0; irow < size_array_rows; irow++){ for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ - //printf("%d %d\n", irow, icoeff); snap[irow][icoeff] = 0.0; } } @@ -372,18 +343,13 @@ void ComputeSnap::compute_array() double** const x = atom->x; const int* const mask = atom->mask; - //printf("----- inum: %d\n", inum); - //printf("----- NEIGHMASK: %d\n", NEIGHMASK); int ninside; int numneigh_sum = 0; int dbirj_row_indx; for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; - //printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); const int i = ilist[ii]; - //printf("----- ii, i: %d %d\n", ii, i); - //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); if (mask[i] & groupbit) { const double xtmp = x[i][0]; @@ -408,12 +374,8 @@ void ComputeSnap::compute_array() // typej = types of neighbors of I within cutoff // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi - /* - This loop assigns quantities in snaptr. - snaptr is a SNA class instance, see sna.h + // assign quantities in snaptr - */ - //int ninside = 0; ninside=0; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; @@ -428,7 +390,6 @@ void ComputeSnap::compute_array() if (chemflag) jelem = map[jtype]; if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { - //printf("cutsq: %f\n", cutsq[itype][jtype]); snaptr->rij[ninside][0] = delx; snaptr->rij[ninside][1] = dely; snaptr->rij[ninside][2] = delz; @@ -444,88 +405,36 @@ void ComputeSnap::compute_array() } } - /* - Now that we have assigned neighbor quantities with previous loop, we are ready to compute things. - Here we compute the wigner functions (U), Z is some other quantity, and bi is bispectrum. - */ + // compute bispectrum for atom i + snaptr->compute_ui(ninside, ielem); snaptr->compute_zi(); snaptr->compute_bi(ielem); - /* - Looks like this loop computes derivatives. - How does snaptr know what atom I we're dealing with? - I think it only needs neighbor info, and then it goes from there. - */ - //printf("----- Derivative loop - looping over neighbors j.\n"); - //printf("----- ninside: %d\n", ninside); // numneighs of I within cutoff + // loop over neighbors for descriptors derivatives + for (int jj = 0; jj < ninside; jj++) { - //printf("----- jj: %d\n", jj); const int j = snaptr->inside[jj]; - //printf("----- jj, j, jtag: %d %d %d\n", jj, j, atom->tag[j]); - //int dbirj_row_indx = 3*neighsum[i] + 3*jj ; // THIS IS WRONG, SEE NEXT LINE. - //int dbirj_row_indx = 3*neighsum[j] + 3*i_indx; // need to get i_indx. - // How to get i_indx? - /* - i_indx must start at zero and end at (nneighs[j]-1). - We can start a counter for each atom j. - Maybe this icounter can serve as an index for i as a neighbor of j. - icounter starts at zero and ends at (nneighs[j]-1). - */ - //icounter[atom->tag[j]-1] += 1; + if (dbirjflag){ - dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. - //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); + dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; icounter[atom->tag[j]-1] += 1; } - //int dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. - //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); - //icounter[atom->tag[j]-1] += 1; - /* - j is an atom index starting from 0. - Use atom->tag[j] to get the atom in the box (index starts at 1). - Need to make sure that the order of these ij pairs is the same when multiplying by dE/dD later. - */ - //printf("----- jj, j, jtag: %d %d %d\n", jj, j, atom->tag[j]); + snaptr->compute_duidrj(jj); snaptr->compute_dbidrj(); // Accumulate dBi/dRi, -dBi/dRj - /* - snap_peratom[i] has type double * because each atom index has indices for descriptors. - */ double *snadi = snap_peratom[i]+typeoffset_local; double *snadj = snap_peratom[j]+typeoffset_local; - //printf("----- ncoeff: %d\n", ncoeff); - //printf("snadi: %f %f %f %f %f\n", snadi[0], snadi[1], snadi[2], snadi[3], snadi[4]); - //printf("----- typeoffset_local: %d\n", typeoffset_local); - //printf("snadi: "); - //for (int s=0; s<(ncoeff*3); s++){ - // printf("%f ", snadi[s]); - //} - /* - printf("snadj: "); - for (int s=0; s<(ncoeff*3); s++){ - printf("%f ", snadj[s]); - } - */ - //printf("\n"); - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - //printf("----- dblist[icoeff]: %f %f %f\n", snaptr->dblist[icoeff][0], snaptr->dblist[icoeff][1], snaptr->dblist[icoeff][2]); - /* - I think these are the descriptor derivatives. - Desriptor derivatives wrt atom i. - What exactly is being summed here? - This is a loop over descriptors or coeff k. - */ + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + snadi[icoeff] += snaptr->dblist[icoeff][0]; snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - /* - Descriptor derivatives wrt atom j - */ + snadj[icoeff] -= snaptr->dblist[icoeff][0]; snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; @@ -546,11 +455,11 @@ void ComputeSnap::compute_array() dbirj[dbirj_row_indx+2][ncoeff+1] = atom->tag[j]-1; dbirj[dbirj_row_indx+2][ncoeff+2] = 2; } - // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i. + // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i dbiri[3*(atom->tag[i]-1)+0][icoeff] -= snaptr->dblist[icoeff][0]; dbiri[3*(atom->tag[i]-1)+1][icoeff] -= snaptr->dblist[icoeff][1]; dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; - // Get last columns + // Get last columns which are i, j, and Cartesian index if (icoeff==(ncoeff-1)){ dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; @@ -617,18 +526,13 @@ void ComputeSnap::compute_array() } } - } // for (int jj = 0; jj < ninside; jj++) - //printf("---- irow after jj loop: %d\n", irow); - - // Accumulate Bi - //printf("----- Accumulate Bi.\n"); + } // linear contributions int k; if (dbirjflag) k = 0; else k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++){ - //printf("----- %d %f\n", icoeff, snaptr->blist[icoeff]); snap[irow][k++] += snaptr->blist[icoeff]; } @@ -649,85 +553,25 @@ void ComputeSnap::compute_array() numneigh_sum += ninside; } // for (int ii = 0; ii < inum; ii++) - //printf("----- bik_rows: %d\n", bik_rows); - //printf("----- bikflag: %d\n", bikflag); - - // Check icounter. - /* - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - printf("icounter[i]: %d\n", icounter[i]); - } - } - */ - - // Sum all the derivatives we calculated to check usual compute snap output. - /* - if (dbirjflag){ - fh_d = fopen("DEBUG", "w"); - int row_indx=0; - double sum; - for (int ii=0; iintypes); - //for (int itype = 0; itype < atom->ntypes; itype++) { int dbiri_indx; int irow; for (int itype=0; itype<1; itype++){ const int typeoffset_local = ndims_peratom*nperdim*itype; const int typeoffset_global = nperdim*itype; - //printf("----- nperdim: %d\n", nperdim); for (int icoeff = 0; icoeff < nperdim; icoeff++) { - //printf("----- icoeff: %d\n", icoeff); dbiri_indx=0; for (int i = 0; i < atom->nlocal; i++) { - //printf("i: %d\n", i); - //int dbiri_indx = 0; - //int irow; for (int jj=0; jjtag[i]-1] + 3*jj; int snap_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*jj; - //printf("snap_row_indx: %d\n", snap_row_indx); - //int irow = dbirj_row_indx+bik_rows; irow = snap_row_indx + bik_rows; - //printf(" row_indx, irow: %d %d\n", dbirj_row_indx, irow); + // x-coordinate snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; if (icoeff==(ncoeff-1)){ @@ -736,7 +580,7 @@ void ComputeSnap::compute_array() snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+0][ncoeff+2]; } irow++; - //printf(" irow: %d\n", irow); + // y-coordinate snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; if (icoeff==(ncoeff-1)){ @@ -745,7 +589,7 @@ void ComputeSnap::compute_array() snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+1][ncoeff+2]; } irow++; - //printf(" irow: %d\n", irow); + // z-coordinate snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+2][icoeff]; if (icoeff==(ncoeff-1)){ @@ -755,11 +599,11 @@ void ComputeSnap::compute_array() } dbiri_indx = dbiri_indx+3; } + // Put dBi/dRi at end of each dBj/dRi chunk. - //int dbiri_row_indx; + irow = dbiri_indx + bik_rows; - //printf("dbiri_indx: %d\n", dbiri_indx); - //printf("dbiri: %f %f %f\n", dbiri[3*i+0][icoeff], dbiri[3*i+1][icoeff], dbiri[3*i+2][icoeff]); + // x-coordinate snap[irow][icoeff+typeoffset_global] += dbiri[3*i+0][icoeff]; if (icoeff==(ncoeff-1)){ @@ -768,7 +612,7 @@ void ComputeSnap::compute_array() snap[irow][ncoeff+2] += dbiri[3*i+0][ncoeff+2]; } irow++; - //printf(" irow: %d\n", irow); + // y-coordinate snap[irow][icoeff+typeoffset_global] += dbiri[3*i+1][icoeff]; if (icoeff==(ncoeff-1)){ @@ -777,7 +621,8 @@ void ComputeSnap::compute_array() snap[irow][ncoeff+2] += dbiri[3*i+1][ncoeff+2]; } irow++; - //printf(" irow: %d\n", irow); + + // z-coordinate snap[irow][icoeff+typeoffset_global] += dbiri[3*i+2][icoeff]; if (icoeff==(ncoeff-1)){ snap[irow][ncoeff] += dbiri[3*i+2][ncoeff]; @@ -789,31 +634,21 @@ void ComputeSnap::compute_array() } } } - //printf(" Accumulated to global array.\n"); } else{ - //printf("----- Accumulate bispecturm force contributions to global array.\n"); + // accumulate bispectrum force contributions to global array - //printf("----- ntotal, nmax, natoms: %d %d %d\n", ntotal, nmax, atom->natoms); + for (int itype = 0; itype < atom->ntypes; itype++) { const int typeoffset_local = ndims_peratom*nperdim*itype; const int typeoffset_global = nperdim*itype; - //printf("----- nperdim: %d\n", nperdim); - /*nperdim = ncoeff set previsouly*/ for (int icoeff = 0; icoeff < nperdim; icoeff++) { - //printf("----- icoeff: %d\n", icoeff); for (int i = 0; i < ntotal; i++) { double *snadi = snap_peratom[i]+typeoffset_local; int iglobal = atom->tag[i]; - if (icoeff==4){ - if ( (snadi[icoeff] != 0.0) || (snadi[icoeff+yoffset] != 0.0) || (snadi[icoeff+zoffset] != 0.0) ){ - //printf("%d %d %f %f %f\n", i, iglobal, snadi[icoeff], snadi[icoeff+yoffset], snadi[icoeff+zoffset]); - } - } int irow = 3*(iglobal-1)+bik_rows; - //printf("----- snadi[icoeff]: %f\n", snadi[icoeff]); snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; @@ -822,15 +657,11 @@ void ComputeSnap::compute_array() } } - //printf("----- Accumulate forces to global array.\n"); - /* - These are the last columns. - */ // accumulate forces to global array + if (dbirjflag){ + // for dbirjflag=1, put forces at last 3 columns of bik rows for (int i=0; inlocal; i++){ - - int iglobal = atom->tag[i]; snap[iglobal-1][ncoeff+0] = atom->f[i][0]; snap[iglobal-1][ncoeff+1] = atom->f[i][1]; @@ -842,11 +673,8 @@ void ComputeSnap::compute_array() for (int i = 0; i < atom->nlocal; i++) { int iglobal = atom->tag[i]; int irow = 3*(iglobal-1)+bik_rows; - //printf("---- irow: %d\n", irow); snap[irow++][lastcol] = atom->f[i][0]; - //printf("---- irow: %d\n", irow); snap[irow++][lastcol] = atom->f[i][1]; - //printf("---- irow: %d\n", irow); snap[irow][lastcol] = atom->f[i][2]; } } @@ -854,7 +682,7 @@ void ComputeSnap::compute_array() // accumulate bispectrum virial contributions to global array if (dbirjflag){ - + // no virial terms for dbirj yet } else{ dbdotr_compute(); @@ -865,8 +693,8 @@ void ComputeSnap::compute_array() // assign energy to last column if (dbirjflag){ // Assign reference energy right after the dbirj rows, first column. - int irow = bik_rows + dbirj_rows + 3*natoms; // add 3N for the dBi/dRi rows - //printf("irow bik_rows dbirj_rows: %d %d %d\n", irow, bik_rows, dbirj_rows); + // Add 3N for the dBi/dRi rows. + int irow = bik_rows + dbirj_rows + 3*natoms; double reference_energy = c_pe->compute_scalar(); snapall[irow][0] = reference_energy; } @@ -882,7 +710,7 @@ void ComputeSnap::compute_array() c_virial->compute_vector(); if (dbirjflag){ - + // no virial terms for dbirj yet } else{ int irow = 3*natoms+bik_rows; @@ -894,7 +722,6 @@ void ComputeSnap::compute_array() snapall[irow][lastcol] = c_virial->vector[3]; } - //}// else } /* ---------------------------------------------------------------------- @@ -906,14 +733,7 @@ void ComputeSnap::dbdotr_compute() { double **x = atom->x; - int irow0; - if (dbirjflag){ - irow0 = bik_rows+dbirj_rows+(3*natoms); - } - else{ - irow0 = bik_rows+ndims_force*natoms; - } - //int irow0 = bik_rows+ndims_force*natoms; + int irow0 = bik_rows+ndims_force*natoms; // sum over bispectrum contributions to forces // on all particles including ghosts @@ -946,9 +766,11 @@ void ComputeSnap::dbdotr_compute() void ComputeSnap::get_dbirj_length() { + int rank = universe->me; // for MPI debugging + memory->destroy(snap); memory->destroy(snapall); - // invoke full neighbor list (will copy or build if necessary) + // invoke full neighbor list neighbor->build_one(list); dbirj_rows = 0; const int inum = list->inum; @@ -958,12 +780,20 @@ void ComputeSnap::get_dbirj_length() int * const type = atom->type; const int* const mask = atom->mask; double** const x = atom->x; - //printf("----- inum: %d\n", inum); + /* memory->create(neighsum, inum, "snap:neighsum"); memory->create(nneighs, inum, "snap:nneighs"); memory->create(icounter, inum, "snap:icounter"); memory->create(dbiri, 3*atom->nlocal,ncoeff+3, "snap:dbiri"); - for (int ii=0; ii<3*atom->nlocal; ii++){ + */ + memory->create(neighsum, natoms, "snap:neighsum"); + memory->create(nneighs, natoms, "snap:nneighs"); + memory->create(icounter, natoms, "snap:icounter"); + memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); + if (atom->nlocal != natoms){ + error->all(FLERR,"Compute snap dbirjflag=1 does not support parallelism."); + } + for (int ii=0; ii<3*natoms; ii++){ for (int icoeff=0; icoeffcreate(dbirj, dbirj_rows, ncoeff+3, "snap:dbirj"); for (int i=0; inlocal; // Add 3*N for dBi/dRi - //printf("----- dbirj_rows: %d\n", dbirj_rows); - //printf("----- end of dbirj length.\n"); memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); From 0e6bbf8dffed7483bca320d66cc0347070dc0c48 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 17 Jun 2022 14:16:03 -0600 Subject: [PATCH 13/75] Remove files from other branch. --- src/fix_move.cpp | 247 ++--------------------------------------------- src/fix_move.h | 55 ----------- 2 files changed, 8 insertions(+), 294 deletions(-) diff --git a/src/fix_move.cpp b/src/fix_move.cpp index 7cf5567028..37e8647671 100644 --- a/src/fix_move.cpp +++ b/src/fix_move.cpp @@ -35,16 +35,11 @@ #include #include -#include -#include -#include -#include - using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; -enum { LINEAR, WIGGLE, ROTATE, VARIABLE, TRANSROT, WIGGLE_EIGEN }; +enum { LINEAR, WIGGLE, ROTATE, VARIABLE, TRANSROT }; enum { EQUAL, ATOM }; #define INERTIA 0.2 // moment of inertia prefactor for ellipsoid @@ -192,80 +187,7 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } else error->all(FLERR, "Illegal fix move command"); - } - // Fix move wiggle_eigen - else if (strcmp(arg[3], "wiggle_eigen") == 0) { - int natoms = atom->natoms; - int nlocal = atom->nlocal; - printf("----- fix move wiggle_eigen initialize -----\n"); - printf("natoms: %d\n", natoms); - memory->create(emat,natoms*3,natoms*3,"move:emat"); - memory->create(freq,natoms*3,"move:freq"); - - // Read EMAT - std::ifstream readfile2; - for (int i = 0; i < natoms*3; i++) { - for (int j = 0; j < natoms*3; j++) { - emat[i][j] = 0.0; - } - } - readfile2.open("../EMAT"); - //readfile2.open("ev_real.txt"); - if (!readfile2.is_open()) { - //printf("ASDFASDF"); - printf("Unable to open ../EMAT\n"); - exit(1); - } - //printf("natoms: %d\n", natoms); - for (int i=0;i<3*natoms;i++){ - for (int j=0;j<3*natoms;j++){ - readfile2>>emat[i][j]; - } - } - - // Read FREQUENCIES - std::ifstream readfile3; - for (int i = 0; i < natoms*3; i++) { - freq[i]=0.0; - } - readfile3.open("FREQUENCIES"); - //readfile2.open("ev_real.txt"); - - if (!readfile3.is_open()) { - printf("Unable to open FREQUENCIES.\n"); - exit(1); - } - //printf("natoms: %d\n", natoms); - for (int i=0;i<3*natoms;i++){ - readfile3>>freq[i]; - } - - - if (narg < 8) error->all(FLERR, "Illegal fix move command"); - iarg = 8; - mstyle = WIGGLE_EIGEN; - if (strcmp(arg[4], "NULL") == 0) - axflag = 0; - else { - axflag = 1; - ax = utils::numeric(FLERR, arg[4], false, lmp); - } - if (strcmp(arg[5], "NULL") == 0) - ayflag = 0; - else { - ayflag = 1; - ay = utils::numeric(FLERR, arg[5], false, lmp); - } - if (strcmp(arg[6], "NULL") == 0) - azflag = 0; - else { - azflag = 1; - az = utils::numeric(FLERR, arg[6], false, lmp); - } - period = utils::numeric(FLERR, arg[7], false, lmp); - if (period <= 0.0) error->all(FLERR, "Illegal fix move command"); - } - else + } else error->all(FLERR, "Illegal fix move command"); // optional args @@ -314,10 +236,6 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : if (axflag) ax *= xscale; if (ayflag) ay *= yscale; if (azflag) az *= zscale; - } else if (mstyle == WIGGLE_EIGEN) { - if (axflag) ax *= xscale; - if (ayflag) ay *= yscale; - if (azflag) az *= zscale; } else if (mstyle == ROTATE) { point[0] *= xscale; point[1] *= yscale; @@ -334,7 +252,7 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : // set omega_rotate from period - if ((mstyle == WIGGLE) || (mstyle == ROTATE) || (mstyle == TRANSROT) || (mstyle == WIGGLE_EIGEN)) + if ((mstyle == WIGGLE) || (mstyle == ROTATE) || (mstyle == TRANSROT)) omega_rotate = MY_2PI / period; // runit = unit vector along rotation axis @@ -377,10 +295,10 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : // AtomVec pointers to retrieve per-atom storage of extra quantities - avec_ellipsoid = dynamic_cast( atom->style_match("ellipsoid")); - avec_line = dynamic_cast( atom->style_match("line")); - avec_tri = dynamic_cast( atom->style_match("tri")); - avec_body = dynamic_cast( atom->style_match("body")); + avec_ellipsoid = dynamic_cast(atom->style_match("ellipsoid")); + avec_line = dynamic_cast(atom->style_match("line")); + avec_tri = dynamic_cast(atom->style_match("tri")); + avec_body = dynamic_cast(atom->style_match("body")); // xoriginal = initial unwrapped positions of atoms // toriginal = initial theta of lines @@ -462,11 +380,6 @@ FixMove::~FixMove() memory->destroy(displace); memory->destroy(velocity); - if (mstyle==WIGGLE_EIGEN){ - memory->destroy(emat); - memory->destroy(freq); - } - delete[] xvarstr; delete[] yvarstr; delete[] zvarstr; @@ -582,7 +495,7 @@ void FixMove::init() velocity = nullptr; if (utils::strmatch(update->integrate_style, "^respa")) - nlevels_respa = (dynamic_cast( update->integrate))->nlevels; + nlevels_respa = (dynamic_cast(update->integrate))->nlevels; } /* ---------------------------------------------------------------------- @@ -614,7 +527,6 @@ void FixMove::initial_integrate(int /*vflag*/) int *tri = atom->tri; int *body = atom->body; int *mask = atom->mask; - int *tag = atom->tag; int nlocal = atom->nlocal; @@ -740,142 +652,6 @@ void FixMove::initial_integrate(int /*vflag*/) // X = P + C + A cos(w*dt) + B sin(w*dt) // V = w R0 cross (A cos(w*dt) + B sin(w*dt)) -} else if (mstyle == WIGGLE_EIGEN) { - //printf("----- Wiggling!-----\n"); - int modeindx = 10; - //printf("%f %f\n", omega_rotate, MY_2PI*freq[modeindx]); - omega_rotate = MY_2PI*freq[modeindx]; - double arg = omega_rotate * delta; - double sine = sin(arg); - double cosine = cos(arg); - - //printf("arg: %f\n", arg); - for (int alpha=0; alpha<3; alpha++){ - for (int i=0; iremap_near(x[i], xold); - } - - } - - - } - - /* - for (int i=0; iremap_near(x[i], xold); - } - - } - */ - - /* - for (int i = 0; i < nlocal; i++) { - if (mask[i] & groupbit) { - xold[0] = x[i][0]; - xold[1] = x[i][1]; - xold[2] = x[i][2]; - - if (axflag) { - v[i][0] = ax * omega_rotate * cosine; - x[i][0] = xoriginal[i][0] + ax * sine; - } else if (rmass) { - dtfm = dtf / rmass[i]; - v[i][0] += dtfm * f[i][0]; - x[i][0] += dtv * v[i][0]; - } else { - dtfm = dtf / mass[type[i]]; - v[i][0] += dtfm * f[i][0]; - x[i][0] += dtv * v[i][0]; - } - - if (ayflag) { - v[i][1] = ay * omega_rotate * cosine; - x[i][1] = xoriginal[i][1] + ay * sine; - } else if (rmass) { - dtfm = dtf / rmass[i]; - v[i][1] += dtfm * f[i][1]; - x[i][1] += dtv * v[i][1]; - } else { - dtfm = dtf / mass[type[i]]; - v[i][1] += dtfm * f[i][1]; - x[i][1] += dtv * v[i][1]; - } - - if (azflag) { - v[i][2] = az * omega_rotate * cosine; - x[i][2] = xoriginal[i][2] + az * sine; - } else if (rmass) { - dtfm = dtf / rmass[i]; - v[i][2] += dtfm * f[i][2]; - x[i][2] += dtv * v[i][2]; - } else { - dtfm = dtf / mass[type[i]]; - v[i][2] += dtfm * f[i][2]; - x[i][2] += dtv * v[i][2]; - } - - domain->remap_near(x[i], xold); - } - } - */ - // for rotate by right-hand rule around omega: - // P = point = vector = point of rotation - // R = vector = axis of rotation - // w = omega of rotation (from period) - // X0 = xoriginal = initial coord of atom - // R0 = runit = unit vector for R - // D = X0 - P = vector from P to X0 - // C = (D dot R0) R0 = projection of atom coord onto R line - // A = D - C = vector from R line to X0 - // B = R0 cross A = vector perp to A in plane of rotation - // A,B define plane of circular rotation around R line - // X = P + C + A cos(w*dt) + B sin(w*dt) - // V = w R0 cross (A cos(w*dt) + B sin(w*dt)) - } else if (mstyle == ROTATE) { double arg = omega_rotate * delta; double cosine = cos(arg); @@ -1527,13 +1303,6 @@ void FixMove::set_arrays(int i) if (ayflag) xoriginal[i][1] -= ay * sine; if (azflag) xoriginal[i][2] -= az * sine; -} else if (mstyle == WIGGLE_EIGEN) { - double arg = omega_rotate * delta; - double sine = sin(arg); - if (axflag) xoriginal[i][0] -= ax * sine; - if (ayflag) xoriginal[i][1] -= ay * sine; - if (azflag) xoriginal[i][2] -= az * sine; - } else if (mstyle == ROTATE) { double a[3], b[3], c[3], d[3], disp[3], ddotr; double arg = -omega_rotate * delta; diff --git a/src/fix_move.h b/src/fix_move.h index 8023a66c1b..e6b253a2a2 100644 --- a/src/fix_move.h +++ b/src/fix_move.h @@ -73,11 +73,6 @@ class FixMove : public Fix { int maxatom; double **displace, **velocity; - // fix move wiggle_eigen variables - - double **emat; - double *freq; - class AtomVecEllipsoid *avec_ellipsoid; class AtomVecLine *avec_line; class AtomVecTri *avec_tri; @@ -88,53 +83,3 @@ class FixMove : public Fix { #endif #endif - -/* ERROR/WARNING messages: - -E: Illegal ... command - -Self-explanatory. Check the input script syntax and compare to the -documentation for the command. You can use -echo screen as a -command-line option when running LAMMPS to see the offending line. - -E: Fix move cannot set linear z motion for 2d problem - -Self-explanatory. - -E: Fix move cannot set wiggle z motion for 2d problem - -Self-explanatory. - -E: Fix move cannot rotate around non z-axis for 2d problem - -UNDOCUMENTED - -E: Fix move cannot define z or vz variable for 2d problem - -Self-explanatory. - -E: Zero length rotation vector with fix move - -Self-explanatory. - -E: Variable name for fix move does not exist - -Self-explanatory. - -E: Variable for fix move is invalid style - -Only equal-style variables can be used. - -E: Cannot add atoms to fix move variable - -Atoms can not be added afterwards to this fix option. - -E: Resetting timestep size is not allowed with fix move - -This is because fix move is moving atoms based on elapsed time. - -U: Fix move cannot rotate aroung non z-axis for 2d problem - -Self-explanatory. - -*/ From 2396c16026c040b95845905797eb524e56e9a655 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Wed, 22 Jun 2022 09:32:41 -0600 Subject: [PATCH 14/75] Update example and docs. --- doc/src/compute_sna_atom.rst | 43 ++- examples/snap/README.md | 10 +- examples/snap/compute_snap_dgrad.py | 24 +- src/ML-SNAP/compute_snap.cpp | 146 ++++---- src/ML-SNAP/compute_snap.h | 6 +- src/ML-SNAP/compute_snapneigh.cpp | 563 ---------------------------- src/ML-SNAP/compute_snapneigh.h | 73 ---- 7 files changed, 139 insertions(+), 726 deletions(-) delete mode 100644 src/ML-SNAP/compute_snapneigh.cpp delete mode 100644 src/ML-SNAP/compute_snapneigh.h diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index 54a6df02a2..e3255f201d 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -33,7 +33,7 @@ Syntax * R_1, R_2,... = list of cutoff radii, one for each type (distance units) * w_1, w_2,... = list of neighbor weights, one for each type * zero or more keyword/value pairs may be appended -* keyword = *rmin0* or *switchflag* or *bzeroflag* or *quadraticflag* or *chem* or *bnormflag* or *wselfallflag* or *bikflag* or *switchinnerflag* or *sinner* or *dinner* +* keyword = *rmin0* or *switchflag* or *bzeroflag* or *quadraticflag* or *chem* or *bnormflag* or *wselfallflag* or *bikflag* or *switchinnerflag* or *sinner* or *dinner* or *dgradflag* .. parsed-literal:: @@ -66,6 +66,9 @@ Syntax *sinnerlist* = *ntypes* values of *Sinner* (distance units) *dinner* values = *dinnerlist* *dinnerlist* = *ntypes* values of *Dinner* (distance units) + *dgradflag* value = *0* or *1* + *0* = bispectrum descriptor gradients are summed over neighbors + *1* = bispectrum descriptor gradients are not summed over neighbors Examples """""""" @@ -340,6 +343,14 @@ When the central atom and the neighbor atom have different types, the values of :math:`S_{inner}` and :math:`D_{inner}` are the arithmetic means of the values for both types. +The keyword *dgradflag* determines whether or not to sum the bispectrum descriptor gradients over neighboring atoms *i'* +as explained with *snad/atom* above. If *dgradflag* is set to 1 then the descriptor gradient rows of the global snap array +are not summed over atoms *i'*. Instead, each row corresponds to a single term :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` +where :math:`a` is the Cartesian direction for the gradient. This also changes +the number of columns to be equal to the number of bispectrum components, with 3 +additional columns representing the indices :math:`i`, :math:`j`, and :math:`a`, +as explained more in the Output info section below. The option *dgradflag=1* must be used with *bikflag=1*. + .. note:: If you have a bonded system, then the settings of :doc:`special_bonds @@ -435,6 +446,36 @@ components. For the purposes of handling contributions to force, virial, and quadratic combinations, these :math:`N_{elem}^3` sub-blocks are treated as a single block of :math:`K N_{elem}^3` columns. +If the *bik* keyword is set to 1, then the first :math:`N` rows of the snap array +correspond to :math:`B_{i,k}` instead of the sum over atoms :math:`i`. In this case, the entries in the final column for these rows +are set to zero. + +If the *dgradflag* keyword is set to 1, this changes the structure of the snap array completely. +Here the *snad/atom* quantities are replaced with rows corresponding to descriptor +gradient components + +.. math:: + + \frac{\partial {B_{i,k} }}{\partial {r}^a_j} + +where :math:`a` is the Cartesian direction for the gradient. The rows are organized in chunks, where each chunk corresponds to +an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to neighbors :math:`i` of :math:`j`. +The number of rows in the atom :math:`j` chunk is therefore equal to the number of neighbors :math:`N_{neighs}[j]` within the SNAP +potential cutoff radius of atom :math:`j`, times 3 for each Cartesian direction. +The total number of rows for these descriptor gradients is therefore + +.. math:: + + 3 \sum_j^{N} N_{neighs}[j]. + +For *dgradflag=1*, the number of columns is equal to the number of bispectrum components, +plus 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a` which +identify the atoms :math:`i` and :math:`j`, and Cartesian direction :math:`a` for which +a particular gradient :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` belongs to. The reference energy and forces are also located in different parts of the array. +The last 3 columns of the first :math:`N` rows belong to the reference potential force components. +The first column of the last row, after the first :math:`N + 3 \sum_j^{N} N_{neighs}[j]` rows, +contains the reference potential energy. The virial components are not used with this option. + These values can be accessed by any command that uses per-atom values from a compute as input. See the :doc:`Howto output ` doc page for an overview of LAMMPS output options. To see how this command diff --git a/examples/snap/README.md b/examples/snap/README.md index 7c34fe7021..e1bd54ff37 100644 --- a/examples/snap/README.md +++ b/examples/snap/README.md @@ -1,9 +1 @@ -See `compute_snap_dgrad.py` for a test that compares the dBi/dRj from compute snap (`dbirjflag=1`) to the sum of dBi/dRj from usual compute snap (`dbirjflag=0`). - -The format of the global array from `dbirjflag=1` is as follows. - -The first N rows belong to bispectrum components for each atom, if we use `bikflag=1`. The first `K` columns correspond to each bispectrum coefficient. The final 3 columns contain reference force components for each atom. - -The rows after the first N rows contain dBi/dRj values for all pairs. These values are arranged in row chunks for each atom `j`, where all the rows in a chunk are associated with the neighbors `i` of `j`, as well as the self-terms where `i=j`. So for atom `j`, the number of rows is equal to the number of atoms within the SNAP cutoff, plus 1 for the `i=j` terms, times 3 for each Cartesian component. The total number of dBi/dRj rows is therefore equal to `N*(Nneigh+1)*3`, and `Nneigh` may be different for each atom. To facilitate with determining which row belong to which atom pair `ij`, the last 3 columns contain indices; the 3rd to last column contains global indices of atoms `i` (the neighbors), the 2nd to last column contains global indices of atoms `j`, and the last column contains an index 0,1,2 for the Cartesian component. Like the `bik` rows, the first `K` columns correspond to each bispectrum coefficient. - -Finally, the first column of the last row contains the reference energy. +See `compute_snap_dgrad.py` for a test that compares the dBi/dRj from compute snap (`dgradflag=1`) to the sum of dBi/dRj from usual compute snap (`dgradflag=0`). diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index dc06ae1576..84d0b8113a 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -1,8 +1,8 @@ """ compute_snap_dgrad.py Purpose: Demonstrate extraction of descriptor gradient (dB/dR) array from compute snap. - Show that dBi/dRj components summed over neighbors i yields same output as regular compute snap with dbirjflag=0. - This shows that the dBi/dRj components extracted with dbirjflag=1 are correct. + Show that dBi/dRj components summed over neighbors i yields same output as regular compute snap with dgradflag=0. + This shows that the dBi/dRj components extracted with dgradflag=1 are correct. Serial syntax: python compute_snap_dgrad.py Parallel syntax: @@ -24,7 +24,7 @@ from lammps import lammps, LMP_TYPE_ARRAY, LMP_STYLE_GLOBAL cmds = ["-screen", "none", "-log", "none"] lmp = lammps(cmdargs=cmds) -def run_lammps(dbirjflag): +def run_lammps(dgradflag): lmp.command("clear") lmp.command("units metal") lmp.command("boundary p p p") @@ -36,7 +36,7 @@ def run_lammps(dbirjflag): lmp.command("mass * 180.88") lmp.command("displace_atoms all random 0.01 0.01 0.01 123456") # Pair style - snap_options=f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dbirjflag {dbirjflag}' + snap_options=f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dgradflag {dgradflag}' lmp.command(f"pair_style zero {rcutfac}") lmp.command(f"pair_coeff * *") lmp.command(f"pair_style zbl {zblcutinner} {zblcutouter}") @@ -70,7 +70,7 @@ quadratic=0 bzero=0 switch=0 bikflag=1 -dbirjflag=1 +dgradflag=1 # Declare reference potential variables zblcutinner=4.0 @@ -84,7 +84,8 @@ else: nd = int(m*(m+1)*(m+2)/3) print(f"Number of descriptors based on twojmax : {nd}") -# Run lammps with dbirjflag on +# Run lammps with dgradflag on +print("Running with dgradflag on") run_lammps(1) # Get global snap array @@ -93,12 +94,12 @@ lmp_snap = lmp.numpy.extract_compute("snap",0, 2) # Extract dBj/dRi (includes dBi/dRi) natoms = lmp.get_natoms() fref1 = lmp_snap[0:natoms,-3:].flatten() -eref1 = lmp_snap[-6,0] -dbdr_length = np.shape(lmp_snap)[0]-(natoms)-6 # Length of neighborlist pruned dbdr array +eref1 = lmp_snap[-1,0] #lmp_snap[-6,0] +dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 #-6 # Length of neighborlist pruned dbdr array dBdR = lmp_snap[natoms:(natoms+dbdr_length),:] force_indices = lmp_snap[natoms:(natoms+dbdr_length),-3:].astype(np.int32) -# Sum over neighbors j for each atom i, like dbirjflag=0 does. +# Sum over neighbors j for each atom i, like dgradflag=0 does. array1 = np.zeros((3*natoms,nd)) a = 0 for k in range(0,nd): @@ -111,7 +112,8 @@ for k in range(0,nd): if (a>2): a=0 -# Run lammps with dbirjflag off +# Run lammps with dgradflag off +print("Running with dgradflag off") run_lammps(0) # Get global snap array @@ -121,7 +123,7 @@ fref2 = lmp_snap[natoms:(natoms+3*natoms),-1] eref2 = lmp_snap[0,-1] array2 = lmp_snap[natoms:natoms+(3*natoms), nd:-1] -# Sum the arrays obtained from dbirjflag on and off. +# Sum the arrays obtained from dgradflag on and off. summ = array1 + array2 #np.savetxt("sum.dat", summ) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index d1b0980dfb..88360a5005 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -56,7 +56,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : bzeroflag = 1; quadraticflag = 0; bikflag = 0; - dbirjflag = 0; + dgradflag = 0; chemflag = 0; bnormflag = 0; wselfallflag = 0; @@ -148,10 +148,10 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : error->all(FLERR,"Illegal compute snap command"); bikflag = atoi(arg[iarg+1]); iarg += 2; - } else if (strcmp(arg[iarg],"dbirjflag") == 0) { + } else if (strcmp(arg[iarg],"dgradflag") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute snap command"); - dbirjflag = atoi(arg[iarg+1]); + dgradflag = atoi(arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { if (iarg+2 > narg) @@ -185,6 +185,9 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (!switchinnerflag && (sinnerflag || dinnerflag)) error->all(FLERR,"Illegal compute snap command: switchinnerflag = 0, unexpected sinner/dinner keyword"); + if (dgradflag && !bikflag) + error->all(FLERR,"Illegal compute snap command: dgradflag=1 requires bikflag=1"); + snaptr = new SNA(lmp, rfac0, twojmax, rmin0, switchflag, bzeroflag, chemflag, bnormflag, wselfallflag, @@ -200,9 +203,9 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : natoms = atom->natoms; bik_rows = 1; if (bikflag) bik_rows = natoms; - dbirj_rows = ndims_force*natoms; - size_array_rows = bik_rows+dbirj_rows+ndims_virial; - if (dbirjflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[j], and cartesian index + dgrad_rows = ndims_force*natoms; + size_array_rows = bik_rows+dgrad_rows+ndims_virial; + if (dgradflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[j], and cartesian index else size_array_cols = nperdim*atom->ntypes+1; lastcol = size_array_cols-1; @@ -230,8 +233,8 @@ ComputeSnap::~ComputeSnap() memory->destroy(sinnerelem); memory->destroy(dinnerelem); } - if (dbirjflag){ - memory->destroy(dbirj); + if (dgradflag){ + memory->destroy(dgrad); memory->destroy(nneighs); memory->destroy(neighsum); memory->destroy(icounter); @@ -260,10 +263,19 @@ void ComputeSnap::init() // allocate memory for global array - memory->create(snap,size_array_rows,size_array_cols, - "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, - "snap:snapall"); + if (dgradflag){ + // Initially allocate natoms^2 rows, will prune with neighborlist later + memory->create(snap,natoms*natoms,ncoeff+3, + "snap:snap"); + memory->create(snapall,natoms*natoms,ncoeff+3, + "snap:snapall"); + } + else{ + memory->create(snap,size_array_rows,size_array_cols, + "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, + "snap:snapall"); + } array = snapall; // find compute for reference energy @@ -299,8 +311,8 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { - if (dbirjflag){ - get_dbirj_length(); + if (dgradflag){ + get_dgrad_length(); } int ntotal = atom->nlocal + atom->nghost; @@ -345,7 +357,7 @@ void ComputeSnap::compute_array() const int* const mask = atom->mask; int ninside; int numneigh_sum = 0; - int dbirj_row_indx; + int dgrad_row_indx; for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; @@ -416,8 +428,8 @@ void ComputeSnap::compute_array() for (int jj = 0; jj < ninside; jj++) { const int j = snaptr->inside[jj]; - if (dbirjflag){ - dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; + if (dgradflag){ + dgrad_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; icounter[atom->tag[j]-1] += 1; } @@ -440,20 +452,20 @@ void ComputeSnap::compute_array() snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - if (dbirjflag){ - dbirj[dbirj_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; - dbirj[dbirj_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; - dbirj[dbirj_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; + if (dgradflag){ + dgrad[dgrad_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; + dgrad[dgrad_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; + dgrad[dgrad_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; if (icoeff==(ncoeff-1)){ - dbirj[dbirj_row_indx+0][ncoeff] = atom->tag[i]-1; - dbirj[dbirj_row_indx+0][ncoeff+1] = atom->tag[j]-1; - dbirj[dbirj_row_indx+0][ncoeff+2] = 0; - dbirj[dbirj_row_indx+1][ncoeff] = atom->tag[i]-1; - dbirj[dbirj_row_indx+1][ncoeff+1] = atom->tag[j]-1; - dbirj[dbirj_row_indx+1][ncoeff+2] = 1; - dbirj[dbirj_row_indx+2][ncoeff] = atom->tag[i]-1; - dbirj[dbirj_row_indx+2][ncoeff+1] = atom->tag[j]-1; - dbirj[dbirj_row_indx+2][ncoeff+2] = 2; + dgrad[dgrad_row_indx+0][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+0][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+0][ncoeff+2] = 0; + dgrad[dgrad_row_indx+1][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+1][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+1][ncoeff+2] = 1; + dgrad[dgrad_row_indx+2][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+2][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+2][ncoeff+2] = 2; } // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i dbiri[3*(atom->tag[i]-1)+0][icoeff] -= snaptr->dblist[icoeff][0]; @@ -530,7 +542,7 @@ void ComputeSnap::compute_array() // linear contributions int k; - if (dbirjflag) k = 0; + if (dgradflag) k = 0; else k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++){ snap[irow][k++] += snaptr->blist[icoeff]; @@ -555,7 +567,7 @@ void ComputeSnap::compute_array() // Accumulate contributions to global array - if (dbirjflag){ + if (dgradflag){ int dbiri_indx; int irow; @@ -568,34 +580,34 @@ void ComputeSnap::compute_array() for (int jj=0; jjtag[i]-1] + 3*jj; + int dgrad_row_indx = 3*neighsum[atom->tag[i]-1] + 3*jj; int snap_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*jj; irow = snap_row_indx + bik_rows; // x-coordinate - snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+0][icoeff]; + snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+0][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbirj[dbirj_row_indx+0][ncoeff]; - snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+0][ncoeff+1]; - snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+0][ncoeff+2]; + snap[irow][ncoeff] += dgrad[dgrad_row_indx+0][ncoeff]; + snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; + snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; } irow++; // y-coordinate - snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+1][icoeff]; + snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+1][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbirj[dbirj_row_indx+1][ncoeff]; - snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+1][ncoeff+1]; - snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+1][ncoeff+2]; + snap[irow][ncoeff] += dgrad[dgrad_row_indx+1][ncoeff]; + snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; + snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; } irow++; // z-coordinate - snap[irow][icoeff+typeoffset_global] += dbirj[dbirj_row_indx+2][icoeff]; + snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+2][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbirj[dbirj_row_indx+2][ncoeff]; - snap[irow][ncoeff+1] += dbirj[dbirj_row_indx+2][ncoeff+1]; - snap[irow][ncoeff+2] += dbirj[dbirj_row_indx+2][ncoeff+2]; + snap[irow][ncoeff] += dgrad[dgrad_row_indx+2][ncoeff]; + snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; + snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; } dbiri_indx = dbiri_indx+3; } @@ -659,8 +671,8 @@ void ComputeSnap::compute_array() // accumulate forces to global array - if (dbirjflag){ - // for dbirjflag=1, put forces at last 3 columns of bik rows + if (dgradflag){ + // for dgradflag=1, put forces at last 3 columns of bik rows for (int i=0; inlocal; i++){ int iglobal = atom->tag[i]; snap[iglobal-1][ncoeff+0] = atom->f[i][0]; @@ -681,8 +693,8 @@ void ComputeSnap::compute_array() // accumulate bispectrum virial contributions to global array - if (dbirjflag){ - // no virial terms for dbirj yet + if (dgradflag){ + // no virial terms for dgrad yet } else{ dbdotr_compute(); @@ -691,10 +703,10 @@ void ComputeSnap::compute_array() // sum up over all processes MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); // assign energy to last column - if (dbirjflag){ - // Assign reference energy right after the dbirj rows, first column. + if (dgradflag){ + // Assign reference energy right after the dgrad rows, first column. // Add 3N for the dBi/dRi rows. - int irow = bik_rows + dbirj_rows + 3*natoms; + int irow = bik_rows + dgrad_rows + 3*natoms; double reference_energy = c_pe->compute_scalar(); snapall[irow][0] = reference_energy; } @@ -709,10 +721,11 @@ void ComputeSnap::compute_array() // switch to Voigt notation c_virial->compute_vector(); - if (dbirjflag){ - // no virial terms for dbirj yet + if (dgradflag){ + // no virial terms for dgrad yet } else{ + c_virial->compute_vector(); int irow = 3*natoms+bik_rows; snapall[irow++][lastcol] = c_virial->vector[0]; snapall[irow++][lastcol] = c_virial->vector[1]; @@ -760,10 +773,10 @@ void ComputeSnap::dbdotr_compute() } /* ---------------------------------------------------------------------- - compute dbirj length + compute dgrad length ------------------------------------------------------------------------- */ -void ComputeSnap::get_dbirj_length() +void ComputeSnap::get_dgrad_length() { int rank = universe->me; // for MPI debugging @@ -772,7 +785,7 @@ void ComputeSnap::get_dbirj_length() memory->destroy(snapall); // invoke full neighbor list neighbor->build_one(list); - dbirj_rows = 0; + dgrad_rows = 0; const int inum = list->inum; const int* const ilist = list->ilist; const int* const numneigh = list->numneigh; @@ -791,7 +804,7 @@ void ComputeSnap::get_dbirj_length() memory->create(icounter, natoms, "snap:icounter"); memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); if (atom->nlocal != natoms){ - error->all(FLERR,"Compute snap dbirjflag=1 does not support parallelism."); + error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism."); } for (int ii=0; ii<3*natoms; ii++){ for (int icoeff=0; icoeff1e-20) { - dbirj_rows += 1; //jnum + 1; + dgrad_rows += 1; //jnum + 1; jnum_cutoff += 1; nneighs[i]+=1; } @@ -830,7 +843,7 @@ void ComputeSnap::get_dbirj_length() } } - dbirj_rows *= ndims_force; + dgrad_rows *= ndims_force; // Loop over all atoms again to calculate neighsum. for (int ii = 0; ii < inum; ii++) { @@ -850,14 +863,15 @@ void ComputeSnap::get_dbirj_length() } } - memory->create(dbirj, dbirj_rows, ncoeff+3, "snap:dbirj"); - for (int i=0; icreate(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); + for (int i=0; inlocal; // Add 3*N for dBi/dRi + // Set size array rows which now depends on dgrad_rows. + //size_array_rows = bik_rows+dgrad_rows+ndims_virial+3*atom->nlocal; // Add 3*N for dBi/dRi + size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); @@ -871,7 +885,7 @@ void ComputeSnap::get_dbirj_length() double ComputeSnap::compute_scalar() { - if (dbirjflag) get_dbirj_length(); + if (dgradflag) get_dgrad_length(); return size_array_rows; } diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 3b03512efc..447f6d286d 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -56,8 +56,8 @@ class ComputeSnap : public Compute { int quadraticflag; //int bikflag; //int bik_rows; - int bikflag, bik_rows, dbirjflag, dbirj_rows; - double **dbirj; + int bikflag, bik_rows, dgradflag, dgrad_rows; + double **dgrad; double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j int *nneighs; // number of neighs inside the snap cutoff. int *neighsum; @@ -67,7 +67,7 @@ class ComputeSnap : public Compute { Compute *c_virial; void dbdotr_compute(); - void get_dbirj_length(); + void get_dgrad_length(); }; } // namespace LAMMPS_NS diff --git a/src/ML-SNAP/compute_snapneigh.cpp b/src/ML-SNAP/compute_snapneigh.cpp deleted file mode 100644 index eef18a93f4..0000000000 --- a/src/ML-SNAP/compute_snapneigh.cpp +++ /dev/null @@ -1,563 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 "compute_snapneigh.h" - -#include "sna.h" -#include "atom.h" -#include "update.h" -#include "modify.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "force.h" -#include "pair.h" -#include "comm.h" -#include "memory.h" -#include "error.h" - -#include - -using namespace LAMMPS_NS; - -enum{SCALAR,VECTOR,ARRAY}; - -ComputeSnapneigh::ComputeSnapneigh(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), radelem(nullptr), wjelem(nullptr), - sinnerelem(nullptr), dinnerelem(nullptr) -{ - - array_flag = 1; - //vector_flag = 1; - extarray = 0; - - double rfac0, rmin0; - int twojmax, switchflag, bzeroflag, bnormflag, wselfallflag; - - int ntypes = atom->ntypes; - int nargmin = 6+2*ntypes; - - if (narg < nargmin) error->all(FLERR,"Illegal compute snap command"); - - // default values - - rmin0 = 0.0; - switchflag = 1; - bzeroflag = 1; - quadraticflag = 0; - bikflag = 0; - dbirjflag = 0; - chemflag = 0; - bnormflag = 0; - wselfallflag = 0; - switchinnerflag = 0; - nelements = 1; - - // process required arguments - - memory->create(radelem,ntypes+1,"snapneigh:radelem"); // offset by 1 to match up with types - memory->create(wjelem,ntypes+1,"snapneigh:wjelem"); - rcutfac = atof(arg[3]); - rfac0 = atof(arg[4]); - twojmax = atoi(arg[5]); - for (int i = 0; i < ntypes; i++) - radelem[i+1] = atof(arg[6+i]); - for (int i = 0; i < ntypes; i++) - wjelem[i+1] = atof(arg[6+ntypes+i]); - - // construct cutsq - - double cut; - cutmax = 0.0; - memory->create(cutsq,ntypes+1,ntypes+1,"snapneigh:cutsq"); - for (int i = 1; i <= ntypes; i++) { - cut = 2.0*radelem[i]*rcutfac; - //printf("cut: %f\n", cut); - if (cut > cutmax) cutmax = cut; - cutsq[i][i] = cut*cut; - for (int j = i+1; j <= ntypes; j++) { - cut = (radelem[i]+radelem[j])*rcutfac; - cutsq[i][j] = cutsq[j][i] = cut*cut; - } - } - - // set local input checks - - int sinnerflag = 0; - int dinnerflag = 0; - - // process optional args - - int iarg = nargmin; - - while (iarg < narg) { - if (strcmp(arg[iarg],"rmin0") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - rmin0 = atof(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"bzeroflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - bzeroflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"switchflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - switchflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"quadraticflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - quadraticflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"chem") == 0) { - if (iarg+2+ntypes > narg) - error->all(FLERR,"Illegal compute snap command"); - chemflag = 1; - memory->create(map,ntypes+1,"compute_snapneigh:map"); - nelements = utils::inumeric(FLERR,arg[iarg+1],false,lmp); - for (int i = 0; i < ntypes; i++) { - int jelem = utils::inumeric(FLERR,arg[iarg+2+i],false,lmp); - if (jelem < 0 || jelem >= nelements) - error->all(FLERR,"Illegal compute snap command"); - map[i+1] = jelem; - } - iarg += 2+ntypes; - } else if (strcmp(arg[iarg],"bnormflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - bnormflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"wselfallflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - wselfallflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"bikflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - bikflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"dbirjflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - dbirjflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - switchinnerflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"sinner") == 0) { - iarg++; - if (iarg+ntypes > narg) - error->all(FLERR,"Illegal compute snap command"); - memory->create(sinnerelem,ntypes+1,"snapneigh:sinnerelem"); - for (int i = 0; i < ntypes; i++) - sinnerelem[i+1] = utils::numeric(FLERR,arg[iarg+i],false,lmp); - sinnerflag = 1; - iarg += ntypes; - } else if (strcmp(arg[iarg],"dinner") == 0) { - iarg++; - if (iarg+ntypes > narg) - error->all(FLERR,"Illegal compute snap command"); - memory->create(dinnerelem,ntypes+1,"snapneigh:dinnerelem"); - for (int i = 0; i < ntypes; i++) - dinnerelem[i+1] = utils::numeric(FLERR,arg[iarg+i],false,lmp); - dinnerflag = 1; - iarg += ntypes; - } else error->all(FLERR,"Illegal compute snap command"); - } - - if (switchinnerflag && !(sinnerflag && dinnerflag)) - error->all(FLERR,"Illegal compute snap command: switchinnerflag = 1, missing sinner/dinner keyword"); - - if (!switchinnerflag && (sinnerflag || dinnerflag)) - error->all(FLERR,"Illegal compute snap command: switchinnerflag = 0, unexpected sinner/dinner keyword"); - - /* - snaptr = new SNA(lmp, rfac0, twojmax, - rmin0, switchflag, bzeroflag, - chemflag, bnormflag, wselfallflag, - nelements, switchinnerflag); - */ - - //ncoeff = snaptr->ncoeff; - nperdim = ncoeff; - if (quadraticflag) nperdim += (ncoeff*(ncoeff+1))/2; - ndims_force = 3; - ndims_virial = 6; - yoffset = nperdim; - zoffset = 2*nperdim; - natoms = atom->natoms; - bik_rows = 1; - if (bikflag) bik_rows = natoms; - //size_array_rows = bik_rows+ndims_force*natoms+ndims_virial; - dbirj_rows = ndims_force*natoms; - size_array_rows = bik_rows+dbirj_rows+ndims_virial; - size_array_cols = nperdim*atom->ntypes+1; - lastcol = size_array_cols-1; - - ndims_peratom = ndims_force; - size_peratom = ndims_peratom*nperdim*atom->ntypes; - - nmax = 0; - -} - -/* ---------------------------------------------------------------------- */ - -ComputeSnapneigh::~ComputeSnapneigh() -{ - - memory->destroy(neighs); - memory->destroy(radelem); - memory->destroy(wjelem); - memory->destroy(cutsq); - //delete snaptr; - - if (chemflag) memory->destroy(map); - - if (switchinnerflag) { - memory->destroy(sinnerelem); - memory->destroy(dinnerelem); - } - - if (dbirjflag){ - //printf("dbirj_rows: %d\n", dbirj_rows); - //memory->destroy(dbirj); - memory->destroy(nneighs); - memory->destroy(neighsum); - memory->destroy(icounter); - //memory->destroy(dbiri); - } -} - -/* ---------------------------------------------------------------------- */ - -void ComputeSnapneigh::init() -{ - if (force->pair == nullptr) - error->all(FLERR,"Compute snap requires a pair style be defined"); - - if (cutmax > force->pair->cutforce){ - //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); - error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); - } - - // need an occasional full neighbor list - - neighbor->add_request(this, NeighConst::REQ_FULL | NeighConst::REQ_OCCASIONAL); - -} - - -/* ---------------------------------------------------------------------- */ - -void ComputeSnapneigh::init_list(int /*id*/, NeighList *ptr) -{ - list = ptr; -} - -/* ---------------------------------------------------------------------- */ - -void ComputeSnapneigh::compute_array() -{ - - if (dbirjflag){ - //printf("----- dbirjflag true.\n"); - get_dbirj_length(); - //printf("----- got dbirj_length\n"); - } - - //printf("----- cutmax cutforce: %f %f\n", cutmax, force->pair->cutforce); - //else{ - int ntotal = atom->nlocal + atom->nghost; - - invoked_array = update->ntimestep; - - // invoke full neighbor list (will copy or build if necessary) - neighbor->build_one(list); - - const int inum = list->inum; - const int* const ilist = list->ilist; - const int* const numneigh = list->numneigh; - int** const firstneigh = list->firstneigh; - int * const type = atom->type; - - // compute sna derivatives for each atom in group - // use full neighbor list to count atoms less than cutoff - - double** const x = atom->x; - const int* const mask = atom->mask; - //printf("----- inum: %d\n", inum); - //printf("----- NEIGHMASK: %d\n", NEIGHMASK); - int ninside; - int numneigh_sum = 0; - int dbirj_row_indx; - int dbiri_indx=0; - for (int ii = 0; ii < inum; ii++) { - int irow = 0; - if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; - //printf("----- i, itag: %d %d\n", ilist[ii] & NEIGHMASK, atom->tag[ilist[ii]]); - const int i = ilist[ii]; - //printf("----- ii, i: %d %d\n", ii, i); - //printf("----- mask[i] groupbit: %d %d\n", mask[i], groupbit); - if (mask[i] & groupbit) { - - const double xtmp = x[i][0]; - const double ytmp = x[i][1]; - const double ztmp = x[i][2]; - const int itype = type[i]; - int ielem = 0; - if (chemflag) - ielem = map[itype]; - const double radi = radelem[itype]; - const int* const jlist = firstneigh[i]; - const int jnum = numneigh[i]; - const int typeoffset_local = ndims_peratom*nperdim*(itype-1); - const int typeoffset_global = nperdim*(itype-1); - - // insure rij, inside, and typej are of size jnum - - //snaptr->grow_rij(jnum); - - // rij[][3] = displacements between atom I and those neighbors - // inside = indices of neighbors of I within cutoff - // typej = types of neighbors of I within cutoff - // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi - - /* - This loop assigns quantities in snaptr. - snaptr is a SNA class instance, see sna.h - - */ - //int ninside = 0; - ninside=0; - for (int jj = 0; jj < jnum; jj++) { - int j = jlist[jj]; - j &= NEIGHMASK; - - const double delx = x[j][0] - xtmp; - const double dely = x[j][1] - ytmp; - const double delz = x[j][2] - ztmp; - const double rsq = delx*delx + dely*dely + delz*delz; - int jtype = type[j]; - int jelem = 0; - if (chemflag) - jelem = map[jtype]; - if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { - if (dbirjflag){ - //dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; // THIS IS WRONG, SEE NEXT VAR. - //dbirj_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*icounter[atom->tag[j]-1]; // 3*tagi is to leave space for dBi/dRi - dbirj_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] + 3*(atom->tag[j]-1); // THIS IS WRONG, SEE NEXT VAR. - //printf("--- %d %d %d %d\n", dbirj_row_indx, 3*neighsum[atom->tag[i]-1], 3*(atom->tag[i]-1), jj); - //printf("jtag, icounter, dbirj_row_indx: %d, %d, %d %d %d\n", atom->tag[j], icounter[atom->tag[j]-1], dbirj_row_indx+0, dbirj_row_indx+1, dbirj_row_indx+2); - icounter[atom->tag[j]-1] += 1; - - neighs[dbirj_row_indx+0][0] = atom->tag[i]; - neighs[dbirj_row_indx+1][0] = atom->tag[i]; - neighs[dbirj_row_indx+2][0] = atom->tag[i]; - - neighs[dbirj_row_indx+0][1] = atom->tag[j]; - neighs[dbirj_row_indx+1][1] = atom->tag[j]; - neighs[dbirj_row_indx+2][1] = atom->tag[j]; - - neighs[dbirj_row_indx+0][2] = 0; - neighs[dbirj_row_indx+1][2] = 1; - neighs[dbirj_row_indx+2][2] = 2; - - dbiri_indx = dbiri_indx+3; - } - } - } - - //printf("--- dbiri_indx: %d\n", dbiri_indx); - // Put dBi/dRi in - - neighs[dbiri_indx+0][0] = atom->tag[i]; - neighs[dbiri_indx+1][0] = atom->tag[i]; - neighs[dbiri_indx+2][0] = atom->tag[i]; - - neighs[dbiri_indx+0][1] = atom->tag[i]; - neighs[dbiri_indx+1][1] = atom->tag[i]; - neighs[dbiri_indx+2][1] = atom->tag[i]; - - neighs[dbiri_indx+0][2] = 0; - neighs[dbiri_indx+1][2] = 1; - neighs[dbiri_indx+2][2] = 2; - - dbiri_indx = dbiri_indx+3; - } - - numneigh_sum += ninside; - } // for (int ii = 0; ii < inum; ii++) - - - // Check icounter. - /* - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - printf("icounter[i]: %d\n", icounter[i]); - } - } - */ - - - // sum up over all processes - // I'll need to do something like this... - /* - MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); - // assign energy to last column - for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; - int irow = 0; - double reference_energy = c_pe->compute_scalar(); - snapall[irow][lastcol] = reference_energy; - */ - - /* - for (int i=0; ibuild_one(list); - dbirj_rows = 0; - const int inum = list->inum; - const int* const ilist = list->ilist; - const int* const numneigh = list->numneigh; - int** const firstneigh = list->firstneigh; - int * const type = atom->type; - const int* const mask = atom->mask; - double** const x = atom->x; - //printf("----- inum: %d\n", inum); - memory->create(neighsum, atom->nlocal, "snapneigh:neighsum"); - memory->create(nneighs, atom->nlocal, "snapneigh:nneighs"); - memory->create(icounter, atom->nlocal, "snapneigh:icounter"); - //memory->create(dbiri, 3*inum,ncoeff, "snapneigh:dbiri"); - /* - for (int ii=0; ii1e-20) { - dbirj_rows += 1; //jnum + 1; - jnum_cutoff += 1; - nneighs[i]+=1; - } - } - //printf("----- jnum_cutoff: %d\n", jnum_cutoff); - } - } - - dbirj_rows *= ndims_force; - - // Loop over all atoms again to calculate neighsum. - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - //printf("nneighs[i]: %d\n", nneighs[i]); - //neighsum[i] = 0; - //printf("i nneighs[i]: %d %d\n", i, nneighs[i]); - if (i==0){ - neighsum[i]=0; - } - else{ - for (int jj=0; jj < ii; jj++){ - const int j = ilist[jj]; - if (mask[j] & groupbit) { - //printf(" j nneighs[j-1]: %d %d\n", j, nneighs[j]); - neighsum[i] += nneighs[j]; - } - } - //neighsum[i] += 1; // Add 1 for the self term dBi/dRi - } - } - //printf("%d\n", neighsum[i]); - } - - size_array_rows = dbirj_rows+(3*atom->nlocal); - size_array_cols = 3; - //memory->create(dbirj, dbirj_rows, ncoeff, "snapneigh:dbirj"); - memory->create(neighs, size_array_rows, size_array_cols, "snapneigh:neighs"); - - //vector = neighs; - array = neighs; - // Set size array rows which now depends on dbirj_rows. - //size_array_rows = bik_rows+dbirj_rows+ndims_virial; - //printf("----- dbirj_rows: %d\n", dbirj_rows); - //printf("----- end of dbirj length.\n"); - -} - -/* ---------------------------------------------------------------------- - compute array length -------------------------------------------------------------------------- */ - -double ComputeSnapneigh::compute_scalar() -{ - if (dbirjflag) get_dbirj_length(); - return size_array_rows; -} - -/* ---------------------------------------------------------------------- - memory usage -------------------------------------------------------------------------- */ - -double ComputeSnapneigh::memory_usage() -{ - - double bytes = (double)size_array_rows*size_array_cols * - sizeof(double); // array - - return bytes; -} diff --git a/src/ML-SNAP/compute_snapneigh.h b/src/ML-SNAP/compute_snapneigh.h deleted file mode 100644 index 1ac712a101..0000000000 --- a/src/ML-SNAP/compute_snapneigh.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 COMPUTE_CLASS -// clang-format off -ComputeStyle(snapneigh,ComputeSnapneigh); -// clang-format on -#else - -#ifndef LMP_COMPUTE_SNAPNEIGH_H -#define LMP_COMPUTE_SNAPNEIGH_H - -#include "compute.h" - -namespace LAMMPS_NS { - -class ComputeSnapneigh : public Compute { - public: - ComputeSnapneigh(class LAMMPS *, int, char **); - ~ComputeSnapneigh() override; - void init() override; - void init_list(int, class NeighList *) override; - void compute_array() override; - double compute_scalar() override; - double memory_usage() override; - - private: - FILE * fh_d; - int natoms, nmax, size_peratom, lastcol; - int ncoeff, nperdim, yoffset, zoffset; - int ndims_peratom, ndims_force, ndims_virial; - double **cutsq; - class NeighList *list; - double **snap, **snapall; - double **snap_peratom; - double rcutfac; - double *radelem; - double *wjelem; - int *map; // map types to [0,nelements) - int nelements, chemflag; - int switchinnerflag; - double *sinnerelem; - double *dinnerelem; - //class SNA *snaptr; - double cutmax; - int quadraticflag; - //int bikflag; - //int bik_rows; - int bikflag, bik_rows, dbirjflag, dbirj_rows; - double **dbirj; - double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j - int *nneighs; // number of neighs inside the snap cutoff. - int *neighsum; - int *icounter; // counting atoms i for each j. - double **neighs; // neighborlist for neural networks - - void get_dbirj_length(); -}; - -} // namespace LAMMPS_NS - -#endif -#endif From 7c44eac0a6bdc7359beda0a1950e64a89622b0a2 Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Thu, 23 Jun 2022 11:43:14 -0600 Subject: [PATCH 15/75] Added more to README and obtain MPI settings from lammps Python module --- examples/snap/README.md | 14 ++++++- examples/snap/compute.snap.dat | 59 ----------------------------- examples/snap/compute_snap_dgrad.py | 20 +++++----- 3 files changed, 24 insertions(+), 69 deletions(-) delete mode 100644 examples/snap/compute.snap.dat diff --git a/examples/snap/README.md b/examples/snap/README.md index e1bd54ff37..305f920ae8 100644 --- a/examples/snap/README.md +++ b/examples/snap/README.md @@ -1 +1,13 @@ -See `compute_snap_dgrad.py` for a test that compares the dBi/dRj from compute snap (`dgradflag=1`) to the sum of dBi/dRj from usual compute snap (`dgradflag=0`). +This directory contains a variety of tests for the ML-SNAP package. These include: + +in.snap.Ta06A # SNAP linear Ta potential +in.snap.W.2940 # SNAP linear W potential +in.snap.hybrid.WSNAP.HePair # Hybrid overlay pair style for linear SNAP W potential and twobody tables for He-He and W-He +in.snap.WBe.PRB2019 # SNAP linear W/Be potential +in.snap.InP.JCPA2020 # SNAP linear InP potential using chem keyword (explicit multi-element) +in.snap.Mo_Chen # SNAP linear Mo potential +in.snap.compute # SNAP compute for training a linear model +in.snap.compute.quadratic # SNAP compute for training a quadratic model +in.snap.scale.Ni_Zuo_JCPA2020 # SNAP linear Ni potential with thermodynamic integration (fix adapt scale) + +compute_snap_dgrad.py # SNAP compute with dgradflag (dBi/dRj) for training a non-linear model diff --git a/examples/snap/compute.snap.dat b/examples/snap/compute.snap.dat deleted file mode 100644 index 60aa40fdd6..0000000000 --- a/examples/snap/compute.snap.dat +++ /dev/null @@ -1,59 +0,0 @@ -# Time-averaged data for fix snap -# TimeStep Number-of-rows -# Row c_snap[1] c_snap[2] c_snap[3] c_snap[4] c_snap[5] c_snap[6] c_snap[7] c_snap[8] c_snap[9] c_snap[10] c_snap[11] -0 55 -1 0 0 0 0 0 3.12659e+06 1.91282e+06 1.01756e+06 1.18149e+06 419003 2775.75 -2 0 0 0 0 0 0 -2617.97 -11804.8 -32003.5 -14156.5 -126.705 -3 0 0 0 0 0 0 -2414.16 -4239.67 -6275.15 -3852.23 -118.927 -4 0 0 0 0 0 0 2529.98 3883.7 6245.75 2522.89 103.66 -5 0 0 0 0 0 0 411.847 604.579 57.0959 1095.67 -188.806 -6 0 0 0 0 0 0 1541.86 4697.43 11841.7 5519.43 275.079 -7 0 0 0 0 0 0 -2870.68 -1447.5 4412.24 1032.92 -63.9586 -8 0 0 0 0 0 0 1193.62 7012.92 20475.9 9007.1 230.377 -9 0 0 0 0 0 0 4848.36 11241.9 22593.7 11630.3 42.8991 -10 0 0 0 0 0 0 -1770.07 -2679.25 -3788.5 -2555.62 -135.264 -11 0 0 0 0 0 0 -4969.62 -8016.32 -11201.8 -7220.33 -85.5022 -12 0 0 0 0 0 0 1641.76 3596.16 7806.47 3219.57 40.8509 -13 0 0 0 0 0 0 325.571 4349.75 13049 5826.43 27.2534 -14 0 0 0 0 0 0 5920.17 5611.27 846.546 2245.23 83.7477 -15 0 0 0 0 0 0 -888.529 -848.965 -1874.49 -290.268 -68.0047 -16 0 0 0 0 0 0 -1916.74 67.9945 4784.3 2143.56 -39.6058 -17 0 0 0 0 0 0 -4098.57 -10375.2 -22007.6 -10355 -200.101 -18 0 0 0 0 0 0 -2284.58 -6551.33 -15184.8 -7117.19 -67.4731 -19 0 0 0 0 0 0 -2737.86 -632.669 6669.64 2094.01 52.5289 -20 0 0 0 0 0 0 -2329.4 -41.9068 7566.17 1913.97 100.188 -21 0 0 0 0 0 0 -444.112 -2754.7 -8428.65 -3849.65 -122.932 -22 0 0 0 0 0 0 -70.5051 111.212 854.264 255.733 65.2259 -23 0 0 0 0 0 0 3554.61 12874.2 31397 14566.8 47.5973 -24 0 0 0 0 0 0 1865.24 2108.07 1180.27 1465.26 91.3443 -25 0 0 0 0 0 0 -889.973 2561.32 11256.4 4537.35 77.4022 -26 0 0 0 0 0 0 3550.36 106.913 -9710.14 -2944.98 144.241 -27 0 0 0 0 0 0 -4712.47 -8838.63 -14464.9 -8091.56 -224.069 -28 0 0 0 0 0 0 -2024.94 -4432.38 -9505.05 -4018.8 -207.602 -29 0 0 0 0 0 0 2379.69 4724.47 7670.76 5006.86 -23.6309 -30 0 0 0 0 0 0 376.992 1771.26 5976.85 2024.35 134.961 -31 0 0 0 0 0 0 1237.27 -1519.65 -9085.33 -3530.88 -43.4288 -32 0 0 0 0 0 0 583.161 6064.47 18404.5 7643.32 243.05 -33 0 0 0 0 0 0 -2538.86 -2021.15 691.987 -389.262 -141.239 -34 0 0 0 0 0 0 2885.38 5612.51 9715.93 5772.93 193.908 -35 0 0 0 0 0 0 -6048.23 -11209.3 -18774.1 -10567.4 -252.412 -36 0 0 0 0 0 0 -1418.32 -3619.88 -5764.64 -4231.84 203.031 -37 0 0 0 0 0 0 3007.44 1474.23 -3713.21 -994.284 140.462 -38 0 0 0 0 0 0 4888.42 4654.63 805.35 2190.37 43.3575 -39 0 0 0 0 0 0 969.58 3277.56 6218.65 3924.82 -58.9942 -40 0 0 0 0 0 0 2987.73 4234.51 5529.54 3085.54 43.2781 -41 0 0 0 0 0 0 810.067 -1872.94 -8730.18 -3125.43 -210.33 -42 0 0 0 0 0 0 2844.79 2986.48 1115.95 1588.01 123.161 -43 0 0 0 0 0 0 134.538 -4097.82 -14380.1 -6204.27 -19.7911 -44 0 0 0 0 0 0 -2999.2 -2447.09 1548.16 -1098.43 162.086 -45 0 0 0 0 0 0 -2288.5 -5930.54 -12773.2 -6503.71 -200.232 -46 0 0 0 0 0 0 -2625.62 -6290.98 -12970.9 -6562.73 -182.126 -47 0 0 0 0 0 0 -228.949 4114.07 13655.9 5798.77 32.8425 -48 0 0 0 0 0 0 2900.97 5126.05 7340.27 4953.94 90.5452 -49 0 0 0 0 0 0 1798.49 -1194.98 -9074.02 -3404.76 -11.9431 -50 0 0 0 0 0 0 -3.09692e+06 -3.518e+06 -4.33318e+06 -2.30338e+06 1.32116e+08 -51 0 0 0 0 0 0 -3.10721e+06 -3.53165e+06 -4.34977e+06 -2.31581e+06 1.28785e+08 -52 0 0 0 0 0 0 -3.10871e+06 -3.53788e+06 -4.36295e+06 -2.32103e+06 1.4248e+08 -53 0 0 0 0 0 0 3585.35 6805.98 11450.9 6458.62 914589 -54 0 0 0 0 0 0 -6674.27 -11551.6 -17884.1 -10474.7 -2.08251e+06 -55 0 0 0 0 0 0 -11913.9 -22733.1 -38858.2 -21261 -7.73337e+06 diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index 84d0b8113a..51a3c08132 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -13,14 +13,14 @@ from __future__ import print_function import sys import ctypes import numpy as np - -# uncomment this if running in parallel via mpi4py -#me = 0 -#from mpi4py import MPI -#me = MPI.COMM_WORLD.Get_rank() -#nprocs = MPI.COMM_WORLD.Get_size() - from lammps import lammps, LMP_TYPE_ARRAY, LMP_STYLE_GLOBAL + +# get MPI settings from LAMMPS + +lmp = lammps() +me = lmp.extract_setting("world_rank") +nprocs = lmp.extract_setting("world_size") + cmds = ["-screen", "none", "-log", "none"] lmp = lammps(cmdargs=cmds) @@ -82,10 +82,12 @@ if (twojmax % 2 == 0): nd = int(m*(m+1)*(2*m+1)/6) else: nd = int(m*(m+1)*(m+2)/3) -print(f"Number of descriptors based on twojmax : {nd}") +if me == 0: + print(f"Number of descriptors based on twojmax : {nd}") # Run lammps with dgradflag on -print("Running with dgradflag on") +if me == 0: + print("Running with dgradflag on") run_lammps(1) # Get global snap array From 447c83662955547687d4018a2084aa45132077b2 Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Thu, 23 Jun 2022 11:56:46 -0600 Subject: [PATCH 16/75] Reverted in.snap.compute --- examples/snap/in.snap.compute | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/snap/in.snap.compute b/examples/snap/in.snap.compute index 005ba8b1a3..b0c7314882 100644 --- a/examples/snap/in.snap.compute +++ b/examples/snap/in.snap.compute @@ -3,7 +3,7 @@ # initialize simulation variable nsteps index 0 -variable nrep equal 2 +variable nrep equal 1 variable a equal 2.0 units metal @@ -81,7 +81,7 @@ thermo 100 # test output: 1: total potential energy # 2: xy component of stress tensor -# 3: Sum(B_{000}^i, all i of type 2) +# 3: Sum(B_{000}^i, all i of type 2) # 4: xz component of Sum(Sum(r_j*dB_{222}^i/dR[j]), all i of type 2), all j) # 5: y component of -Sum(d(B_{222}^i)/dR[2]), all i of type 2) # @@ -89,7 +89,7 @@ thermo 100 thermo_style custom & pe pxy c_bsum2[1] c_vbsum[55] v_db_2_25 & - c_snap[1][11] c_snap[13][11] c_snap[1][6] c_snap[12][10] c_snap[6][10] + c_snap[1][11] c_snap[13][11] c_snap[1][6] c_snap[12][10] c_snap[6][10] thermo_modify norm no # dump mydump_db all custom 1000 dump_db id c_db[*] From 2bc50791aad559ef9b57045c848ad5b810d05645 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 23 Jun 2022 12:16:42 -0600 Subject: [PATCH 17/75] Clean up files. --- python/examples/in.lj | 25 ------------------------- python/lammps/mliap/__init__.py | 3 +-- src/ML-SNAP/compute_snap.h | 2 -- src/ML-SNAP/sna.cpp | 3 --- 4 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 python/examples/in.lj diff --git a/python/examples/in.lj b/python/examples/in.lj deleted file mode 100644 index 3dc80bdfea..0000000000 --- a/python/examples/in.lj +++ /dev/null @@ -1,25 +0,0 @@ -# 3d Lennard-Jones melt - -units lj -atom_style atomic -atom_modify map array - -lattice fcc 0.8442 -region box block 0 4 0 4 0 4 -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 - -variable fx atom fx - -run 10 diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py index 57a9eadd7e..57fe97d803 100644 --- a/python/lammps/mliap/__init__.py +++ b/python/lammps/mliap/__init__.py @@ -4,8 +4,7 @@ # try to improperly start up a new interpreter. import sysconfig import ctypes -#library = sysconfig.get_config_vars('INSTSONAME')[0] -library="/usr/local/Cellar/python@3.10/3.10.4/Frameworks/Python.framework/Versions/3.10/Python" +library = sysconfig.get_config_vars('INSTSONAME')[0] try: pylib = ctypes.CDLL(library) except OSError as e: diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 447f6d286d..8dc72c3bd5 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -54,8 +54,6 @@ class ComputeSnap : public Compute { class SNA *snaptr; double cutmax; int quadraticflag; - //int bikflag; - //int bik_rows; int bikflag, bik_rows, dgradflag, dgrad_rows; double **dgrad; double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j diff --git a/src/ML-SNAP/sna.cpp b/src/ML-SNAP/sna.cpp index d71eeaa7cb..30ae30ae4f 100644 --- a/src/ML-SNAP/sna.cpp +++ b/src/ML-SNAP/sna.cpp @@ -776,7 +776,6 @@ void SNA::compute_dbidrj() int elem3 = elem_duarray; - //printf("----- idxb_max: %d\n", idxb_max); for (int jjb = 0; jjb < idxb_max; jjb++) { const int j1 = idxb[jjb].j1; const int j2 = idxb[jjb].j2; @@ -1336,8 +1335,6 @@ double SNA::memory_usage() void SNA::create_twojmax_arrays() { - //printf("----- idxb_max: %d\n", idxb_max); - //printf("----- ntriples: %d\n", ntriples); int jdimpq = twojmax + 2; memory->create(rootpqarray, jdimpq, jdimpq, "sna:rootpqarray"); From 8e3a1e84a629e4776683c9acba3500abcdefed42 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 23 Jun 2022 12:32:40 -0600 Subject: [PATCH 18/75] More cleaning up. --- python/examples/simple.py | 20 ++++++++++---------- src/ML-SNAP/compute_snap.h | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/python/examples/simple.py b/python/examples/simple.py index 2f9c191165..8925ce48c0 100755 --- a/python/examples/simple.py +++ b/python/examples/simple.py @@ -5,10 +5,10 @@ # Purpose: mimic operation of examples/COUPLE/simple/simple.cpp via Python # Serial syntax: simple.py in.lammps -# in.simple = LAMMPS input script +# in.lammps = LAMMPS input script -# Parallel syntax: mpirun -np 4 python simple.py in.simple -# in.simple = LAMMPS input script +# Parallel syntax: mpirun -np 4 simple.py in.lammps +# in.lammps = LAMMPS input script # also need to uncomment mpi4py sections below from __future__ import print_function @@ -27,9 +27,9 @@ infile = sys.argv[1] me = 0 # uncomment this if running in parallel via mpi4py -from mpi4py import MPI -me = MPI.COMM_WORLD.Get_rank() -nprocs = MPI.COMM_WORLD.Get_size() +#from mpi4py import MPI +#me = MPI.COMM_WORLD.Get_rank() +#nprocs = MPI.COMM_WORLD.Get_size() from lammps import lammps lmp = lammps() @@ -122,10 +122,10 @@ if me == 0: print("Gather post scatter subset:", boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) -#lmp.reset_box([0,0,0],[10,10,8],0,0,0) +lmp.reset_box([0,0,0],[10,10,8],0,0,0) -#boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() -#if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) +boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box() +if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change) # uncomment if running in parallel via mpi4py -print("Proc %d out of %d procs has" % (me,nprocs), lmp) +#print("Proc %d out of %d procs has" % (me,nprocs), lmp) diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 8dc72c3bd5..2a9a94ef80 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -35,7 +35,6 @@ class ComputeSnap : public Compute { double memory_usage() override; private: - FILE * fh_d; int natoms, nmax, size_peratom, lastcol; int ncoeff, nperdim, yoffset, zoffset; int ndims_peratom, ndims_force, ndims_virial; From 92ae5f656c000fdbdbf9b88f04b6cfd8ec28f6e7 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 23 Jun 2022 12:45:20 -0600 Subject: [PATCH 19/75] Change docs. --- doc/src/compute_sna_atom.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index e3255f201d..eddd65bac5 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -459,7 +459,8 @@ gradient components \frac{\partial {B_{i,k} }}{\partial {r}^a_j} where :math:`a` is the Cartesian direction for the gradient. The rows are organized in chunks, where each chunk corresponds to -an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to neighbors :math:`i` of :math:`j`. +an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom +:math:`j` chunk correspond to atoms :math:`i` that have :math:`j` as a neighbor. The number of rows in the atom :math:`j` chunk is therefore equal to the number of neighbors :math:`N_{neighs}[j]` within the SNAP potential cutoff radius of atom :math:`j`, times 3 for each Cartesian direction. The total number of rows for these descriptor gradients is therefore From 14d472d6911e4a3ea2709fb215452fabb0ab3071 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 23 Jun 2022 16:30:53 -0600 Subject: [PATCH 20/75] First 3 columns are reference forces and indices, instead of last 3 columns. --- doc/src/compute_sna_atom.rst | 37 ++++++++----- examples/snap/compute_snap_dgrad.py | 9 ++-- src/ML-SNAP/compute_snap.cpp | 83 +++++++++++++++-------------- src/ML-SNAP/compute_snap.h | 3 +- 4 files changed, 72 insertions(+), 60 deletions(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index eddd65bac5..e2655585f3 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -343,13 +343,16 @@ When the central atom and the neighbor atom have different types, the values of :math:`S_{inner}` and :math:`D_{inner}` are the arithmetic means of the values for both types. -The keyword *dgradflag* determines whether or not to sum the bispectrum descriptor gradients over neighboring atoms *i'* -as explained with *snad/atom* above. If *dgradflag* is set to 1 then the descriptor gradient rows of the global snap array -are not summed over atoms *i'*. Instead, each row corresponds to a single term :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` +The keyword *dgradflag* determines whether or not to sum the bispectrum +descriptor gradients over neighboring atoms *i'* as explained with *snad/atom* +above. If *dgradflag* is set to 1 then the descriptor gradient rows of the +global snap array are not summed over atoms *i'*. Instead, each row corresponds +to a single term :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` where :math:`a` is the Cartesian direction for the gradient. This also changes the number of columns to be equal to the number of bispectrum components, with 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a`, -as explained more in the Output info section below. The option *dgradflag=1* must be used with *bikflag=1*. +as explained more in the Output info section below. The option *dgradflag=1* +must be used with *bikflag=1*. .. note:: @@ -458,12 +461,14 @@ gradient components \frac{\partial {B_{i,k} }}{\partial {r}^a_j} -where :math:`a` is the Cartesian direction for the gradient. The rows are organized in chunks, where each chunk corresponds to -an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom -:math:`j` chunk correspond to atoms :math:`i` that have :math:`j` as a neighbor. -The number of rows in the atom :math:`j` chunk is therefore equal to the number of neighbors :math:`N_{neighs}[j]` within the SNAP -potential cutoff radius of atom :math:`j`, times 3 for each Cartesian direction. -The total number of rows for these descriptor gradients is therefore +where :math:`a` is the Cartesian direction for the gradient. The rows are +organized in chunks, where each chunk corresponds to an atom :math:`j` in the +system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to +atoms :math:`i` that have :math:`j` as a neighbor. The number of rows in the +atom :math:`j` chunk is therefore equal to the number of neighbors +:math:`N_{neighs}[j]` within the SNAP potential cutoff radius of atom :math:`j` +, times 3 for each Cartesian direction. The total number of rows for these +descriptor gradients is therefore .. math:: @@ -472,10 +477,14 @@ The total number of rows for these descriptor gradients is therefore For *dgradflag=1*, the number of columns is equal to the number of bispectrum components, plus 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a` which identify the atoms :math:`i` and :math:`j`, and Cartesian direction :math:`a` for which -a particular gradient :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` belongs to. The reference energy and forces are also located in different parts of the array. -The last 3 columns of the first :math:`N` rows belong to the reference potential force components. -The first column of the last row, after the first :math:`N + 3 \sum_j^{N} N_{neighs}[j]` rows, -contains the reference potential energy. The virial components are not used with this option. +a particular gradient :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` belongs to. +For the descriptor gradient rows, the first 3 columns contain the indices +:math:`i`, :math:`j`, and :math:`a`, and the remaining columns contain gradients +of different descriptors indexed by :math:`k`. +The first 3 columns of the first :math:`N` rows belong to the reference +potential force components. The first column of the last row, after the first +:math:`N + 3 \sum_j^{N} N_{neighs}[j]` rows, contains the reference potential +energy. The virial components are not used with this option. These values can be accessed by any command that uses per-atom values from a compute as input. See the :doc:`Howto output ` doc diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index 51a3c08132..259e44a504 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -95,11 +95,11 @@ lmp_snap = lmp.numpy.extract_compute("snap",0, 2) # Extract dBj/dRi (includes dBi/dRi) natoms = lmp.get_natoms() -fref1 = lmp_snap[0:natoms,-3:].flatten() +fref1 = lmp_snap[0:natoms,0:3].flatten() eref1 = lmp_snap[-1,0] #lmp_snap[-6,0] -dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 #-6 # Length of neighborlist pruned dbdr array -dBdR = lmp_snap[natoms:(natoms+dbdr_length),:] -force_indices = lmp_snap[natoms:(natoms+dbdr_length),-3:].astype(np.int32) +dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 # Length of neighborlist pruned dbdr array +dBdR = lmp_snap[natoms:(natoms+dbdr_length),3:(nd+3)] +force_indices = lmp_snap[natoms:(natoms+dbdr_length),0:3].astype(np.int32) # Sum over neighbors j for each atom i, like dgradflag=0 does. array1 = np.zeros((3*natoms,nd)) @@ -108,7 +108,6 @@ for k in range(0,nd): for l in range(0,dbdr_length): j = force_indices[l,0] i = force_indices[l,1] - #array1[3*(i-1)+a,k] += dBdR[l,k] array1[3*(i)+a,k] += dBdR[l,k] a = a+1 if (a>2): diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 88360a5005..8a4ff2249e 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -541,11 +541,20 @@ void ComputeSnap::compute_array() } // linear contributions + int k; - if (dgradflag) k = 0; - else k = typeoffset_global; - for (int icoeff = 0; icoeff < ncoeff; icoeff++){ - snap[irow][k++] += snaptr->blist[icoeff]; + if (dgradflag){ + k = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++){ + snap[irow][k+3] += snaptr->blist[icoeff]; + k = k+1; + } + } + else{ + k = typeoffset_global; + for (int icoeff = 0; icoeff < ncoeff; icoeff++){ + snap[irow][k++] += snaptr->blist[icoeff]; + } } // quadratic contributions @@ -585,29 +594,29 @@ void ComputeSnap::compute_array() irow = snap_row_indx + bik_rows; // x-coordinate - snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+0][icoeff]; + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+0][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dgrad[dgrad_row_indx+0][ncoeff]; - snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; - snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; + snap[irow][0] += dgrad[dgrad_row_indx+0][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; } irow++; // y-coordinate - snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+1][icoeff]; + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+1][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dgrad[dgrad_row_indx+1][ncoeff]; - snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; - snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; + snap[irow][0] += dgrad[dgrad_row_indx+1][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; } irow++; // z-coordinate - snap[irow][icoeff+typeoffset_global] += dgrad[dgrad_row_indx+2][icoeff]; + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+2][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dgrad[dgrad_row_indx+2][ncoeff]; - snap[irow][ncoeff+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; - snap[irow][ncoeff+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; + snap[irow][0] += dgrad[dgrad_row_indx+2][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; } dbiri_indx = dbiri_indx+3; } @@ -617,29 +626,29 @@ void ComputeSnap::compute_array() irow = dbiri_indx + bik_rows; // x-coordinate - snap[irow][icoeff+typeoffset_global] += dbiri[3*i+0][icoeff]; + snap[irow][icoeff+3] += dbiri[3*i+0][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbiri[3*i+0][ncoeff]; - snap[irow][ncoeff+1] += dbiri[3*i+0][ncoeff+1]; - snap[irow][ncoeff+2] += dbiri[3*i+0][ncoeff+2]; + snap[irow][0] += dbiri[3*i+0][ncoeff]; + snap[irow][0+1] += dbiri[3*i+0][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+0][ncoeff+2]; } irow++; // y-coordinate - snap[irow][icoeff+typeoffset_global] += dbiri[3*i+1][icoeff]; + snap[irow][icoeff+3] += dbiri[3*i+1][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbiri[3*i+1][ncoeff]; - snap[irow][ncoeff+1] += dbiri[3*i+1][ncoeff+1]; - snap[irow][ncoeff+2] += dbiri[3*i+1][ncoeff+2]; + snap[irow][0] += dbiri[3*i+1][ncoeff]; + snap[irow][0+1] += dbiri[3*i+1][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+1][ncoeff+2]; } irow++; // z-coordinate - snap[irow][icoeff+typeoffset_global] += dbiri[3*i+2][icoeff]; + snap[irow][icoeff+3] += dbiri[3*i+2][icoeff]; if (icoeff==(ncoeff-1)){ - snap[irow][ncoeff] += dbiri[3*i+2][ncoeff]; - snap[irow][ncoeff+1] += dbiri[3*i+2][ncoeff+1]; - snap[irow][ncoeff+2] += dbiri[3*i+2][ncoeff+2]; + snap[irow][0] += dbiri[3*i+2][ncoeff]; + snap[irow][0+1] += dbiri[3*i+2][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+2][ncoeff+2]; } dbiri_indx = dbiri_indx+3; @@ -672,12 +681,12 @@ void ComputeSnap::compute_array() // accumulate forces to global array if (dgradflag){ - // for dgradflag=1, put forces at last 3 columns of bik rows + // for dgradflag=1, put forces at first 3 columns of bik rows for (int i=0; inlocal; i++){ int iglobal = atom->tag[i]; - snap[iglobal-1][ncoeff+0] = atom->f[i][0]; - snap[iglobal-1][ncoeff+1] = atom->f[i][1]; - snap[iglobal-1][ncoeff+2] = atom->f[i][2]; + snap[iglobal-1][0+0] = atom->f[i][0]; + snap[iglobal-1][0+1] = atom->f[i][1]; + snap[iglobal-1][0+2] = atom->f[i][2]; } } @@ -793,18 +802,13 @@ void ComputeSnap::get_dgrad_length() int * const type = atom->type; const int* const mask = atom->mask; double** const x = atom->x; - /* - memory->create(neighsum, inum, "snap:neighsum"); - memory->create(nneighs, inum, "snap:nneighs"); - memory->create(icounter, inum, "snap:icounter"); - memory->create(dbiri, 3*atom->nlocal,ncoeff+3, "snap:dbiri"); - */ + memory->create(neighsum, natoms, "snap:neighsum"); memory->create(nneighs, natoms, "snap:nneighs"); memory->create(icounter, natoms, "snap:icounter"); memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); if (atom->nlocal != natoms){ - error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism."); + error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); } for (int ii=0; ii<3*natoms; ii++){ for (int icoeff=0; icoeffnlocal; // Add 3*N for dBi/dRi size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 2a9a94ef80..dcdf339b72 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -54,7 +54,8 @@ class ComputeSnap : public Compute { double cutmax; int quadraticflag; int bikflag, bik_rows, dgradflag, dgrad_rows; - double **dgrad; + double **dgrad; // First ncoeff columns are descriptor derivatives. + // Last 3 columns are indices i,j,a double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j int *nneighs; // number of neighs inside the snap cutoff. int *neighsum; From 2f1d3205108f9800d4dcefd45004290ca903669b Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Fri, 24 Jun 2022 17:02:12 -0600 Subject: [PATCH 21/75] Cleaned up baseline code, prior to parallelization --- examples/snap/compute_snap_dgrad.py | 147 ++++---- src/ML-SNAP/compute_snap.cpp | 525 ++++++++++++++-------------- 2 files changed, 347 insertions(+), 325 deletions(-) diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index 259e44a504..180734cb0c 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -1,8 +1,8 @@ """ compute_snap_dgrad.py Purpose: Demonstrate extraction of descriptor gradient (dB/dR) array from compute snap. - Show that dBi/dRj components summed over neighbors i yields same output as regular compute snap with dgradflag=0. - This shows that the dBi/dRj components extracted with dgradflag=1 are correct. + Show that dBi/dRj components summed over neighbors i yields same output as regular compute snap with dgradflag = 0. + This shows that the dBi/dRj components extracted with dgradflag = 1 are correct. Serial syntax: python compute_snap_dgrad.py Parallel syntax: @@ -22,9 +22,12 @@ me = lmp.extract_setting("world_rank") nprocs = lmp.extract_setting("world_size") cmds = ["-screen", "none", "-log", "none"] -lmp = lammps(cmdargs=cmds) +lmp = lammps(cmdargs = cmds) def run_lammps(dgradflag): + + # simulation settings + lmp.command("clear") lmp.command("units metal") lmp.command("boundary p p p") @@ -35,99 +38,121 @@ def run_lammps(dgradflag): lmp.command(f"create_atoms {ntypes} box") lmp.command("mass * 180.88") lmp.command("displace_atoms all random 0.01 0.01 0.01 123456") - # Pair style - snap_options=f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dgradflag {dgradflag}' + + # potential settings + + snap_options = f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dgradflag {dgradflag}' lmp.command(f"pair_style zero {rcutfac}") lmp.command(f"pair_coeff * *") lmp.command(f"pair_style zbl {zblcutinner} {zblcutouter}") lmp.command(f"pair_coeff * * {zblz} {zblz}") - # set up compute snap generating global array + + # define compute snap + lmp.command(f"compute snap all snap {snap_options}") - # Run + + # run + lmp.command(f"thermo 100") lmp.command(f"run {nsteps}") -# Declare simulation/structure variables -nsteps=0 -nrep=2 -latparam=2.0 -ntypes=2 -nx=nrep -ny=nrep -nz=nrep +# declare simulation/structure variables -# Declare compute snap variables -twojmax=8 -m = (twojmax/2)+1 -rcutfac=1.0 -rfac0=0.99363 -rmin0=0 -radelem1=2.3 -radelem2=2.0 -wj1=1.0 -wj2=0.96 -quadratic=0 -bzero=0 -switch=0 -bikflag=1 -dgradflag=1 +nsteps = 0 +nrep = 2 +latparam = 2.0 +ntypes = 2 +nx = nrep +ny = nrep +nz = nrep -# Declare reference potential variables -zblcutinner=4.0 -zblcutouter=4.8 -zblz=73 +# declare compute snap variables + +twojmax = 8 +rcutfac = 1.0 +rfac0 = 0.99363 +rmin0 = 0 +radelem1 = 2.3 +radelem2 = 2.0 +wj1 = 1.0 +wj2 = 0.96 +quadratic = 0 +bzero = 0 +switch = 0 +bikflag = 1 + +# define reference potential + +zblcutinner = 4.0 +zblcutouter = 4.8 +zblz = 73 + +# number of descriptors -# Number of descriptors if (twojmax % 2 == 0): + m = twojmax / 2 + 1 nd = int(m*(m+1)*(2*m+1)/6) else: + m = (twojmax + 1) / 2 nd = int(m*(m+1)*(m+2)/3) + if me == 0: print(f"Number of descriptors based on twojmax : {nd}") -# Run lammps with dgradflag on +# run lammps with dgradflag on + if me == 0: print("Running with dgradflag on") -run_lammps(1) -# Get global snap array -lmp_snap = lmp.numpy.extract_compute("snap",0, 2) +dgradflag = 1 +run_lammps(dgradflag) + +# get global snap array + +lmp_snap = lmp.numpy.extract_compute("snap", LMP_STYLE_GLOBAL, LMP_TYPE_ARRAY) + +# extract dBj/dRi (includes dBi/dRi) -# Extract dBj/dRi (includes dBi/dRi) natoms = lmp.get_natoms() fref1 = lmp_snap[0:natoms,0:3].flatten() -eref1 = lmp_snap[-1,0] #lmp_snap[-6,0] -dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 # Length of neighborlist pruned dbdr array +eref1 = lmp_snap[-1,0] +dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 dBdR = lmp_snap[natoms:(natoms+dbdr_length),3:(nd+3)] force_indices = lmp_snap[natoms:(natoms+dbdr_length),0:3].astype(np.int32) -# Sum over neighbors j for each atom i, like dgradflag=0 does. +# sum over atoms i that j is a neighbor of, like dgradflag = 0 does. + array1 = np.zeros((3*natoms,nd)) -a = 0 for k in range(0,nd): for l in range(0,dbdr_length): - j = force_indices[l,0] - i = force_indices[l,1] - array1[3*(i)+a,k] += dBdR[l,k] - a = a+1 - if (a>2): - a=0 + i = force_indices[l,0] + j = force_indices[l,1] + a = force_indices[l,2] + array1[3 * j + a, k] += dBdR[l,k] -# Run lammps with dgradflag off -print("Running with dgradflag off") -run_lammps(0) +# run lammps with dgradflag off -# Get global snap array -lmp_snap = lmp.numpy.extract_compute("snap",0, 2) +if me == 0: + print("Running with dgradflag off") + +dgradflag = 0 +run_lammps(dgradflag) + +# get global snap array + +lmp_snap = lmp.numpy.extract_compute("snap", LMP_STYLE_GLOBAL, LMP_TYPE_ARRAY) natoms = lmp.get_natoms() fref2 = lmp_snap[natoms:(natoms+3*natoms),-1] eref2 = lmp_snap[0,-1] array2 = lmp_snap[natoms:natoms+(3*natoms), nd:-1] -# Sum the arrays obtained from dgradflag on and off. -summ = array1 + array2 -#np.savetxt("sum.dat", summ) +# take difference of arrays obtained from dgradflag on and off. -print(f"Maximum difference in descriptor sums: {np.max(summ)}") -print(f"Maximum difference in reference forces: {np.max(fref1-fref2)}") -print(f"Difference in reference energy: {np.max(eref1-eref2)}") +diffm = array1 - array2 +difff = fref1 - fref2 +diffe = eref1 - eref2 + +if me == 0: + print(f"Max/min difference in dSum(Bi)/dRj: {np.max(diffm)} {np.min(diffm)}") + print(f"Max/min difference in reference forces: {np.max(difff)} {np.min(difff)}") + print(f"Difference in reference energy: {diffe}") diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 8a4ff2249e..52fa685ac2 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -188,6 +188,9 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (dgradflag && !bikflag) error->all(FLERR,"Illegal compute snap command: dgradflag=1 requires bikflag=1"); + if (dgradflag && quadraticflag) + error->all(FLERR,"Illegal compute snap command: dgradflag=1 not implemented for quadratic SNAP"); + snaptr = new SNA(lmp, rfac0, twojmax, rmin0, switchflag, bzeroflag, chemflag, bnormflag, wselfallflag, @@ -436,231 +439,152 @@ void ComputeSnap::compute_array() snaptr->compute_duidrj(jj); snaptr->compute_dbidrj(); - // Accumulate dBi/dRi, -dBi/dRj + // accumulate dBi/dRi, -dBi/dRj - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; + if (!dgradflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } - if (dgradflag){ - dgrad[dgrad_row_indx+0][icoeff] = snaptr->dblist[icoeff][0]; - dgrad[dgrad_row_indx+1][icoeff] = snaptr->dblist[icoeff][1]; - dgrad[dgrad_row_indx+2][icoeff] = snaptr->dblist[icoeff][2]; - if (icoeff==(ncoeff-1)){ - dgrad[dgrad_row_indx+0][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+0][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+0][ncoeff+2] = 0; - dgrad[dgrad_row_indx+1][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+1][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+1][ncoeff+2] = 1; - dgrad[dgrad_row_indx+2][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+2][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+2][ncoeff+2] = 2; - } - // Accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i - dbiri[3*(atom->tag[i]-1)+0][icoeff] -= snaptr->dblist[icoeff][0]; - dbiri[3*(atom->tag[i]-1)+1][icoeff] -= snaptr->dblist[icoeff][1]; - dbiri[3*(atom->tag[i]-1)+2][icoeff] -= snaptr->dblist[icoeff][2]; - // Get last columns which are i, j, and Cartesian index - if (icoeff==(ncoeff-1)){ - dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; + + // diagonal elements of quadratic matrix + + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; + + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; + ncount++; - dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; - } - } + // upper-triangular elements of quadratic matrix + + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; + + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; + + ncount++; + } + + } + } + + } else { - } + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; + // sign convention same as compute snad + + dgrad[dgrad_row_indx+0][icoeff] = -snaptr->dblist[icoeff][0]; + dgrad[dgrad_row_indx+1][icoeff] = -snaptr->dblist[icoeff][1]; + dgrad[dgrad_row_indx+2][icoeff] = -snaptr->dblist[icoeff][2]; - // diagonal elements of quadratic matrix + // accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; + dbiri[3*(atom->tag[i]-1)+0][icoeff] += snaptr->dblist[icoeff][0]; + dbiri[3*(atom->tag[i]-1)+1][icoeff] += snaptr->dblist[icoeff][1]; + dbiri[3*(atom->tag[i]-1)+2][icoeff] += snaptr->dblist[icoeff][2]; + + } - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + dgrad[dgrad_row_indx+0][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+0][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+0][ncoeff+2] = 0; + dgrad[dgrad_row_indx+1][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+1][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+1][ncoeff+2] = 1; + dgrad[dgrad_row_indx+2][ncoeff] = atom->tag[i]-1; + dgrad[dgrad_row_indx+2][ncoeff+1] = atom->tag[j]-1; + dgrad[dgrad_row_indx+2][ncoeff+2] = 2; - ncount++; + dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; + + dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; + + dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]-1; + dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; - // upper-triangular elements of quadratic matrix - - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; - - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; - - ncount++; - } - } - - } + } } - // linear contributions + // accumulate Bi + + if (!dgradflag) { - int k; - if (dgradflag){ - k = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++){ - snap[irow][k+3] += snaptr->blist[icoeff]; - k = k+1; - } - } - else{ - k = typeoffset_global; - for (int icoeff = 0; icoeff < ncoeff; icoeff++){ + // linear contributions + + int k = typeoffset_global; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - } - } - // quadratic contributions + // quadratic contributions - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } + + } else { + int k = 3; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + numneigh_sum += ninside; } } - - numneigh_sum += ninside; - } // for (int ii = 0; ii < inum; ii++) - - // Accumulate contributions to global array - - if (dgradflag){ - - int dbiri_indx; - int irow; - for (int itype=0; itype<1; itype++){ - const int typeoffset_local = ndims_peratom*nperdim*itype; - const int typeoffset_global = nperdim*itype; - for (int icoeff = 0; icoeff < nperdim; icoeff++) { - dbiri_indx=0; - for (int i = 0; i < atom->nlocal; i++) { - - for (int jj=0; jjtag[i]-1] + 3*jj; - int snap_row_indx = 3*neighsum[atom->tag[i]-1] + 3*(atom->tag[i]-1) + 3*jj; - irow = snap_row_indx + bik_rows; - - // x-coordinate - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+0][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dgrad[dgrad_row_indx+0][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; - } - irow++; - - // y-coordinate - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+1][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dgrad[dgrad_row_indx+1][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; - } - irow++; - - // z-coordinate - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+2][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dgrad[dgrad_row_indx+2][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; - } - dbiri_indx = dbiri_indx+3; - } - - // Put dBi/dRi at end of each dBj/dRi chunk. - - irow = dbiri_indx + bik_rows; - - // x-coordinate - snap[irow][icoeff+3] += dbiri[3*i+0][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dbiri[3*i+0][ncoeff]; - snap[irow][0+1] += dbiri[3*i+0][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+0][ncoeff+2]; - } - irow++; - - // y-coordinate - snap[irow][icoeff+3] += dbiri[3*i+1][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dbiri[3*i+1][ncoeff]; - snap[irow][0+1] += dbiri[3*i+1][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+1][ncoeff+2]; - } - irow++; - - // z-coordinate - snap[irow][icoeff+3] += dbiri[3*i+2][icoeff]; - if (icoeff==(ncoeff-1)){ - snap[irow][0] += dbiri[3*i+2][ncoeff]; - snap[irow][0+1] += dbiri[3*i+2][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+2][ncoeff+2]; - } - - dbiri_indx = dbiri_indx+3; - } - } - } - } - else{ + // accumulate bispectrum force contributions to global array - // accumulate bispectrum force contributions to global array + if (!dgradflag) { for (int itype = 0; itype < atom->ntypes; itype++) { const int typeoffset_local = ndims_peratom*nperdim*itype; @@ -676,21 +600,81 @@ void ComputeSnap::compute_array() } } } + + } else { + + int irow = bik_rows; + for (int itype = 0; itype < 1; itype++) { + const int typeoffset_local = ndims_peratom * nperdim * itype; + const int typeoffset_global = nperdim * itype; + for (int i = 0; i < atom->nlocal; i++) { + + for (int jj = 0; jjtag[i]-1] + 3 * jj; + int snap_row_indx = 3 * neighsum[atom->tag[i]-1] + 3 * (atom->tag[i]-1) + 3 * jj; + // irow = snap_row_indx + bik_rows; + + // x-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+0][icoeff]; + snap[irow][0] += dgrad[dgrad_row_indx+0][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; + irow++; + + // y-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+1][icoeff]; + snap[irow][0] += dgrad[dgrad_row_indx+1][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; + irow++; + + // z-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dgrad[dgrad_row_indx+2][icoeff]; + snap[irow][0] += dgrad[dgrad_row_indx+2][ncoeff]; + snap[irow][0+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; + snap[irow][0+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; + irow++; + + } + + // Put dBi/dRi at end of each dBj/dRi chunk. + + // x-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dbiri[3*i+0][icoeff]; + snap[irow][0] += dbiri[3*i+0][ncoeff]; + snap[irow][0+1] += dbiri[3*i+0][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+0][ncoeff+2]; + irow++; + + // y-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dbiri[3*i+1][icoeff]; + snap[irow][0] += dbiri[3*i+1][ncoeff]; + snap[irow][0+1] += dbiri[3*i+1][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+1][ncoeff+2]; + irow++; + + // z-coordinate + for (int icoeff = 0; icoeff < nperdim; icoeff++) + snap[irow][icoeff+3] += dbiri[3*i+2][icoeff]; + snap[irow][0] += dbiri[3*i+2][ncoeff]; + snap[irow][0+1] += dbiri[3*i+2][ncoeff+1]; + snap[irow][0+2] += dbiri[3*i+2][ncoeff+2]; + irow++; + + } + } + } // accumulate forces to global array - if (dgradflag){ - // for dgradflag=1, put forces at first 3 columns of bik rows - for (int i=0; inlocal; i++){ - int iglobal = atom->tag[i]; - snap[iglobal-1][0+0] = atom->f[i][0]; - snap[iglobal-1][0+1] = atom->f[i][1]; - snap[iglobal-1][0+2] = atom->f[i][2]; - - } - } - else{ + if (!dgradflag) { for (int i = 0; i < atom->nlocal; i++) { int iglobal = atom->tag[i]; int irow = 3*(iglobal-1)+bik_rows; @@ -698,42 +682,50 @@ void ComputeSnap::compute_array() snap[irow++][lastcol] = atom->f[i][1]; snap[irow][lastcol] = atom->f[i][2]; } + + } else { + + // for dgradflag=1, put forces at first 3 columns of bik rows + + for (int i=0; inlocal; i++){ + int iglobal = atom->tag[i]; + snap[iglobal-1][0+0] = atom->f[i][0]; + snap[iglobal-1][0+1] = atom->f[i][1]; + snap[iglobal-1][0+2] = atom->f[i][2]; + } } // accumulate bispectrum virial contributions to global array - if (dgradflag){ - // no virial terms for dgrad yet - } - else{ - dbdotr_compute(); - } + dbdotr_compute(); // sum up over all processes + MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); + // assign energy to last column - if (dgradflag){ + + if (!dgradflag) { + for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; + int irow = 0; + double reference_energy = c_pe->compute_scalar(); + snapall[irow][lastcol] = reference_energy; + + } else { + // Assign reference energy right after the dgrad rows, first column. // Add 3N for the dBi/dRi rows. int irow = bik_rows + dgrad_rows + 3*natoms; double reference_energy = c_pe->compute_scalar(); snapall[irow][0] = reference_energy; } - else{ - for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; - int irow = 0; - double reference_energy = c_pe->compute_scalar(); - snapall[irow][lastcol] = reference_energy; - } // assign virial stress to last column // switch to Voigt notation c_virial->compute_vector(); - if (dgradflag){ + if (!dgradflag) { // no virial terms for dgrad yet - } - else{ c_virial->compute_vector(); int irow = 3*natoms+bik_rows; snapall[irow++][lastcol] = c_virial->vector[0]; @@ -743,7 +735,7 @@ void ComputeSnap::compute_array() snapall[irow++][lastcol] = c_virial->vector[4]; snapall[irow][lastcol] = c_virial->vector[3]; } - + } /* ---------------------------------------------------------------------- @@ -753,6 +745,11 @@ void ComputeSnap::compute_array() void ComputeSnap::dbdotr_compute() { + + // no virial terms for dgrad yet + + if (dgradflag) return; + double **x = atom->x; int irow0 = bik_rows+ndims_force*natoms; @@ -792,7 +789,9 @@ void ComputeSnap::get_dgrad_length() memory->destroy(snap); memory->destroy(snapall); + // invoke full neighbor list + neighbor->build_one(list); dgrad_rows = 0; const int inum = list->inum; @@ -807,19 +806,17 @@ void ComputeSnap::get_dgrad_length() memory->create(nneighs, natoms, "snap:nneighs"); memory->create(icounter, natoms, "snap:icounter"); memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); - if (atom->nlocal != natoms){ + if (atom->nlocal != natoms) error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); - } - for (int ii=0; ii<3*natoms; ii++){ - for (int icoeff=0; icoeff1e-20) { - dgrad_rows += 1; //jnum + 1; - jnum_cutoff += 1; - nneighs[i]+=1; + if (rsq < cutsq[itype][jtype] && rsq>1e-20) { + dgrad_rows++; + nneighs[i]++; } } } @@ -849,31 +844,33 @@ void ComputeSnap::get_dgrad_length() dgrad_rows *= ndims_force; - // Loop over all atoms again to calculate neighsum. - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - if (i==0){ - neighsum[i]=0; - } - else{ - for (int jj=0; jj < ii; jj++){ - const int j = ilist[jj]; - if (mask[j] & groupbit) { - neighsum[i] += nneighs[j]; - } - } - } - } - } + // loop over all atoms again to calculate neighsum - memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); - for (int i=0; icreate(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); + for (int i = 0; i < dgrad_rows; i++) + for (int j = 0; j < ncoeff+3; j++) + dgrad[i][j] = 0.0; + + // set size array rows which now depends on dgrad_rows. + size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); From 5023103dfb0dccd15b6745f32f876dbe4e8d55bf Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Fri, 24 Jun 2022 17:42:51 -0600 Subject: [PATCH 22/75] Added placeholder get_dgrad_length2() --- src/ML-SNAP/compute_snap.cpp | 131 ++++++++++++++++++++++++++--------- src/ML-SNAP/compute_snap.h | 2 +- 2 files changed, 99 insertions(+), 34 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 52fa685ac2..eb404d70a4 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -202,18 +202,18 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : ndims_force = 3; ndims_virial = 6; yoffset = nperdim; - zoffset = 2*nperdim; + zoffset = 2 * nperdim; natoms = atom->natoms; bik_rows = 1; if (bikflag) bik_rows = natoms; dgrad_rows = ndims_force*natoms; - size_array_rows = bik_rows+dgrad_rows+ndims_virial; - if (dgradflag) size_array_cols = nperdim+3; // plus 3 for tag[i], tag[j], and cartesian index - else size_array_cols = nperdim*atom->ntypes+1; + size_array_rows = bik_rows+dgrad_rows + ndims_virial; + if (dgradflag) size_array_cols = nperdim + 3; + else size_array_cols = nperdim*atom->ntypes + 1; lastcol = size_array_cols-1; ndims_peratom = ndims_force; - size_peratom = ndims_peratom*nperdim*atom->ntypes; + size_peratom = ndims_peratom * nperdim * atom->ntypes; nmax = 0; } @@ -266,19 +266,10 @@ void ComputeSnap::init() // allocate memory for global array - if (dgradflag){ - // Initially allocate natoms^2 rows, will prune with neighborlist later - memory->create(snap,natoms*natoms,ncoeff+3, - "snap:snap"); - memory->create(snapall,natoms*natoms,ncoeff+3, - "snap:snapall"); - } - else{ - memory->create(snap,size_array_rows,size_array_cols, - "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, - "snap:snapall"); - } + memory->create(snap,size_array_rows,size_array_cols, + "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, + "snap:snapall"); array = snapall; // find compute for reference energy @@ -290,6 +281,7 @@ void ComputeSnap::init() c_pe = modify->compute[ipe]; // add compute for reference virial tensor + std::string id_virial = std::string("snap_press"); std::string pcmd = id_virial + " all pressure NULL virial"; modify->add_compute(pcmd); @@ -314,9 +306,8 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { - if (dgradflag){ - get_dgrad_length(); - } + if (dgradflag) get_dgrad_length(); + // if (dgradflag) get_dgrad_length2(); int ntotal = atom->nlocal + atom->nghost; @@ -345,6 +336,7 @@ void ComputeSnap::compute_array() } // invoke full neighbor list (will copy or build if necessary) + neighbor->build_one(list); const int inum = list->inum; @@ -723,9 +715,7 @@ void ComputeSnap::compute_array() // assign virial stress to last column // switch to Voigt notation - c_virial->compute_vector(); if (!dgradflag) { - // no virial terms for dgrad yet c_virial->compute_vector(); int irow = 3*natoms+bik_rows; snapall[irow++][lastcol] = c_virial->vector[0]; @@ -844,6 +834,91 @@ void ComputeSnap::get_dgrad_length() dgrad_rows *= ndims_force; + neighsum[0] = 0; + for (int ii = 1; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) + neighsum[i] = neighsum[i-1] + nneighs[i-1]; + } + + memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); + for (int i = 0; i < dgrad_rows; i++) + for (int j = 0; j < ncoeff+3; j++) + dgrad[i][j] = 0.0; + + // set size array rows which now depends on dgrad_rows. + + size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy + + memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); + array = snapall; + +} + +/* ---------------------------------------------------------------------- + compute dgrad length +------------------------------------------------------------------------- */ + +void ComputeSnap::get_dgrad_length2() +{ + memory->destroy(snap); + memory->destroy(snapall); + + // invoke full neighbor list + + neighbor->build_one(list); + dgrad_rows = 0; + const int inum = list->inum; + const int* const ilist = list->ilist; + const int* const numneigh = list->numneigh; + int** const firstneigh = list->firstneigh; + int * const type = atom->type; + const int* const mask = atom->mask; + double** const x = atom->x; + + memory->create(neighsum, natoms, "snap:neighsum"); + memory->create(nneighs, natoms, "snap:nneighs"); + memory->create(icounter, natoms, "snap:icounter"); + memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); + if (atom->nlocal != natoms) + error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); + + for (int ii = 0; ii < 3 * natoms; ii++) + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + dbiri[ii][icoeff] = 0.0; + + for (int ii = 0; ii < inum; ii++) { + const int i = ilist[ii]; + if (mask[i] & groupbit) { + icounter[i] = 0; + nneighs[i] = 0; + const double xtmp = x[i][0]; + const double ytmp = x[i][1]; + const double ztmp = x[i][2]; + const int itype = type[i]; + const int* const jlist = firstneigh[i]; + const int jnum = numneigh[i]; + for (int jj = 0; jj < jnum; jj++) { + int j = jlist[jj]; + j &= NEIGHMASK; + + const double delx = x[j][0] - xtmp; + const double dely = x[j][1] - ytmp; + const double delz = x[j][2] - ztmp; + const double rsq = delx * delx + dely * dely + delz * delz; + int jtype = type[j]; + + if (rsq < cutsq[itype][jtype] && rsq>1e-20) { + dgrad_rows++; + nneighs[i]++; + } + } + } + } + + dgrad_rows *= ndims_force; + // loop over all atoms again to calculate neighsum // for (int ii = 0; ii < inum; ii++) { @@ -879,16 +954,6 @@ void ComputeSnap::get_dgrad_length() } -/* ---------------------------------------------------------------------- - compute array length -------------------------------------------------------------------------- */ - -double ComputeSnap::compute_scalar() -{ - if (dgradflag) get_dgrad_length(); - return size_array_rows; -} - /* ---------------------------------------------------------------------- memory usage ------------------------------------------------------------------------- */ diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index dcdf339b72..f106421635 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -31,7 +31,6 @@ class ComputeSnap : public Compute { void init() override; void init_list(int, class NeighList *) override; void compute_array() override; - double compute_scalar() override; double memory_usage() override; private: @@ -66,6 +65,7 @@ class ComputeSnap : public Compute { void dbdotr_compute(); void get_dgrad_length(); + void get_dgrad_length2(); }; } // namespace LAMMPS_NS From a8ba6db9610cfb492d4187d2ad4a672867790398 Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Mon, 27 Jun 2022 17:02:39 -0600 Subject: [PATCH 23/75] Make the threebody loop optional --- src/MANYBODY/pair_sw.cpp | 108 +++++++++++++++++++++++---------------- src/MANYBODY/pair_sw.h | 1 + 2 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 71cb1cf5bc..9188b98ae9 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -159,50 +159,51 @@ void PairSW::compute(int eflag, int vflag) if (evflag) ev_tally(i,j,nlocal,newton_pair, evdwl,0.0,fpair,delx,dely,delz); } - - jnumm1 = numshort - 1; - - for (jj = 0; jj < jnumm1; jj++) { - j = neighshort[jj]; - jtype = map[type[j]]; - ijparam = elem3param[itype][jtype][jtype]; - delr1[0] = x[j][0] - xtmp; - delr1[1] = x[j][1] - ytmp; - delr1[2] = x[j][2] - ztmp; - rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - - double fjxtmp,fjytmp,fjztmp; - fjxtmp = fjytmp = fjztmp = 0.0; - - for (kk = jj+1; kk < numshort; kk++) { - k = neighshort[kk]; - ktype = map[type[k]]; - ikparam = elem3param[itype][ktype][ktype]; - ijkparam = elem3param[itype][jtype][ktype]; - - delr2[0] = x[k][0] - xtmp; - delr2[1] = x[k][1] - ytmp; - delr2[2] = x[k][2] - ztmp; - rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - - threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], - rsq1,rsq2,delr1,delr2,fj,fk,eflag,evdwl); - - fxtmp -= fj[0] + fk[0]; - fytmp -= fj[1] + fk[1]; - fztmp -= fj[2] + fk[2]; - fjxtmp += fj[0]; - fjytmp += fj[1]; - fjztmp += fj[2]; - f[k][0] += fk[0]; - f[k][1] += fk[1]; - f[k][2] += fk[2]; - - if (evflag) ev_tally3(i,j,k,evdwl,0.0,fj,fk,delr1,delr2); + if (threebody_on) { + jnumm1 = numshort - 1; + + for (jj = 0; jj < jnumm1; jj++) { + j = neighshort[jj]; + jtype = map[type[j]]; + ijparam = elem3param[itype][jtype][jtype]; + delr1[0] = x[j][0] - xtmp; + delr1[1] = x[j][1] - ytmp; + delr1[2] = x[j][2] - ztmp; + rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; + + double fjxtmp,fjytmp,fjztmp; + fjxtmp = fjytmp = fjztmp = 0.0; + + for (kk = jj+1; kk < numshort; kk++) { + k = neighshort[kk]; + ktype = map[type[k]]; + ikparam = elem3param[itype][ktype][ktype]; + ijkparam = elem3param[itype][jtype][ktype]; + + delr2[0] = x[k][0] - xtmp; + delr2[1] = x[k][1] - ytmp; + delr2[2] = x[k][2] - ztmp; + rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; + + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], + rsq1,rsq2,delr1,delr2,fj,fk,eflag,evdwl); + + fxtmp -= fj[0] + fk[0]; + fytmp -= fj[1] + fk[1]; + fztmp -= fj[2] + fk[2]; + fjxtmp += fj[0]; + fjytmp += fj[1]; + fjztmp += fj[2]; + f[k][0] += fk[0]; + f[k][1] += fk[1]; + f[k][2] += fk[2]; + + if (evflag) ev_tally3(i,j,k,evdwl,0.0,fj,fk,delr1,delr2); + } + f[j][0] += fjxtmp; + f[j][1] += fjytmp; + f[j][2] += fjztmp; } - f[j][0] += fjxtmp; - f[j][1] += fjytmp; - f[j][2] += fjztmp; } f[i][0] += fxtmp; f[i][1] += fytmp; @@ -229,9 +230,26 @@ void PairSW::allocate() global settings ------------------------------------------------------------------------- */ -void PairSW::settings(int narg, char **/*arg*/) +void PairSW::settings(int narg, char ** arg) { - if (narg != 0) error->all(FLERR,"Illegal pair_style command"); + // Default + threebody_on = true; + + int iarg = 0; + + while (iarg < narg) { + if (strcmp(arg[iarg],"threebody") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style command"); + if (strcmp(arg[iarg+1], "on") == 0) { + threebody_on = true; + one_coeff = 1; + } else if (strcmp(arg[iarg+1], "off") == 0) { + threebody_on = false; + one_coeff = 0; // Allow for multiple pair_coeff's + } else error->all(FLERR,"Illegal pair_style command"); + iarg += 2; + } else error->all(FLERR,"Illegal pair_style command"); + } } /* ---------------------------------------------------------------------- diff --git a/src/MANYBODY/pair_sw.h b/src/MANYBODY/pair_sw.h index 8bae7e5282..34fbfa4a4a 100644 --- a/src/MANYBODY/pair_sw.h +++ b/src/MANYBODY/pair_sw.h @@ -52,6 +52,7 @@ class PairSW : public Pair { Param *params; // parameter set for an I-J-K interaction int maxshort; // size of short neighbor list array int *neighshort; // short neighbor list array + bool threebody_on; // whether to run threebody loop void settings(int, char **) override; virtual void allocate(); From 1d3f865d1b09d8740c0debb7a5142dc84ab1e714 Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 00:00:15 -0600 Subject: [PATCH 24/75] Initial changes to the doc --- doc/src/pair_sw.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index 0a6b284b96..a249856371 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -24,13 +24,17 @@ Syntax pair_style style keyword values * style = *sw* or *sw/mod* -* keyword = *maxdelcs* +* keyword = *maxdelcs* or *threebody* .. parsed-literal:: *maxdelcs* value = delta1 delta2 (optional) delta1 = The minimum thershold for the variation of cosine of three-body angle delta2 = The maximum threshold for the variation of cosine of three-body angle + *threebody* value = *on* or *off* (optional) + on (default) = Compute both the three-body and two-body terms of the potential + off = Compute both only the two-body terms of the potential + Examples """""""" @@ -44,6 +48,10 @@ Examples pair_style sw/mod maxdelcs 0.25 0.35 pair_coeff * * tmd.sw.mod Mo S S + pair_style hybrid sw sw threebody off + pair_coeff * * mW_xL.sw mW NULL + pair_coeff * 2 mW_xL.sw mW xL + Description """"""""""" @@ -111,6 +119,9 @@ This value enables the cut-off function to exclude unnecessary angles in the thr However, the angle variation is much smaller than the given threshold value for actual simulations, so the inconsistency between potential and force can be neglected in actual simulations. +The *threebody* keyword determines whether or not the three-body term of the potential is calculated. Skipping this +significantly increases the speed of the potential, but is only applicable when :math:`\lambda_{ijk} = 0` + Only a single pair_coeff command is used with the *sw* and *sw/mod* styles which specifies a Stillinger-Weber potential file with parameters for all needed elements. These are mapped to LAMMPS atom types by specifying @@ -141,6 +152,12 @@ potential is used as part of the *hybrid* pair style. The NULL values are placeholders for atom types that will be used with other potentials. +.. note:: + + When the *threebody on* keyword is used, multiple pair_coeff commands may + be used to specific the pairs of atoms which don't require three-body term. + In these cases, the first 2 arguments are not required to be \* \*. + Stillinger-Weber files in the *potentials* directory of the LAMMPS distribution have a ".sw" suffix. Lines that are not blank or comments (starting with #) define parameters for a triplet of From 4097d295ce23c30a969be3bddb27aa8acfdbcb9a Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 01:37:39 -0600 Subject: [PATCH 25/75] Skip three-body in OpenMP version --- src/OPENMP/pair_sw_omp.cpp | 87 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/OPENMP/pair_sw_omp.cpp b/src/OPENMP/pair_sw_omp.cpp index f6d615b2a1..32df823e31 100644 --- a/src/OPENMP/pair_sw_omp.cpp +++ b/src/OPENMP/pair_sw_omp.cpp @@ -156,50 +156,51 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) if (EVFLAG) ev_tally_thr(this,i,j,nlocal,/* newton_pair */ 1, evdwl,0.0,fpair,delx,dely,delz,thr); } - - jnumm1 = numshort - 1; - - for (jj = 0; jj < jnumm1; jj++) { - j = neighshort_thr[jj]; - jtype = map[type[j]]; - ijparam = elem3param[itype][jtype][jtype]; - delr1[0] = x[j].x - xtmp; - delr1[1] = x[j].y - ytmp; - delr1[2] = x[j].z - ztmp; - rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - - double fjxtmp,fjytmp,fjztmp; - fjxtmp = fjytmp = fjztmp = 0.0; - - for (kk = jj+1; kk < numshort; kk++) { - k = neighshort_thr[kk]; - ktype = map[type[k]]; - ikparam = elem3param[itype][ktype][ktype]; - ijkparam = elem3param[itype][jtype][ktype]; - - delr2[0] = x[k].x - xtmp; - delr2[1] = x[k].y - ytmp; - delr2[2] = x[k].z - ztmp; - rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - - threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], - rsq1,rsq2,delr1,delr2,fj,fk,EFLAG,evdwl); - - fxtmp -= fj[0] + fk[0]; - fytmp -= fj[1] + fk[1]; - fztmp -= fj[2] + fk[2]; - fjxtmp += fj[0]; - fjytmp += fj[1]; - fjztmp += fj[2]; - f[k].x += fk[0]; - f[k].y += fk[1]; - f[k].z += fk[2]; - - if (EVFLAG) ev_tally3_thr(this,i,j,k,evdwl,0.0,fj,fk,delr1,delr2,thr); + if (threebody_on) { + jnumm1 = numshort - 1; + + for (jj = 0; jj < jnumm1; jj++) { + j = neighshort_thr[jj]; + jtype = map[type[j]]; + ijparam = elem3param[itype][jtype][jtype]; + delr1[0] = x[j].x - xtmp; + delr1[1] = x[j].y - ytmp; + delr1[2] = x[j].z - ztmp; + rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; + + double fjxtmp,fjytmp,fjztmp; + fjxtmp = fjytmp = fjztmp = 0.0; + + for (kk = jj+1; kk < numshort; kk++) { + k = neighshort_thr[kk]; + ktype = map[type[k]]; + ikparam = elem3param[itype][ktype][ktype]; + ijkparam = elem3param[itype][jtype][ktype]; + + delr2[0] = x[k].x - xtmp; + delr2[1] = x[k].y - ytmp; + delr2[2] = x[k].z - ztmp; + rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; + + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], + rsq1,rsq2,delr1,delr2,fj,fk,EFLAG,evdwl); + + fxtmp -= fj[0] + fk[0]; + fytmp -= fj[1] + fk[1]; + fztmp -= fj[2] + fk[2]; + fjxtmp += fj[0]; + fjytmp += fj[1]; + fjztmp += fj[2]; + f[k].x += fk[0]; + f[k].y += fk[1]; + f[k].z += fk[2]; + + if (EVFLAG) ev_tally3_thr(this,i,j,k,evdwl,0.0,fj,fk,delr1,delr2,thr); + } + f[j].x += fjxtmp; + f[j].y += fjytmp; + f[j].z += fjztmp; } - f[j].x += fjxtmp; - f[j].y += fjytmp; - f[j].z += fjztmp; } f[i].x += fxtmp; f[i].y += fytmp; From 4a2182ae84a64e8c29b763f1afc75549470f326b Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:22:25 -0600 Subject: [PATCH 26/75] Rename threebody_on to threebody_flag --- src/MANYBODY/pair_sw.cpp | 8 ++++---- src/MANYBODY/pair_sw.h | 10 +++++----- src/OPENMP/pair_sw_omp.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 9188b98ae9..0564f9be2b 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -159,7 +159,7 @@ void PairSW::compute(int eflag, int vflag) if (evflag) ev_tally(i,j,nlocal,newton_pair, evdwl,0.0,fpair,delx,dely,delz); } - if (threebody_on) { + if (threebody_flag) { jnumm1 = numshort - 1; for (jj = 0; jj < jnumm1; jj++) { @@ -233,7 +233,7 @@ void PairSW::allocate() void PairSW::settings(int narg, char ** arg) { // Default - threebody_on = true; + threebody_flag = true; int iarg = 0; @@ -241,10 +241,10 @@ void PairSW::settings(int narg, char ** arg) if (strcmp(arg[iarg],"threebody") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style command"); if (strcmp(arg[iarg+1], "on") == 0) { - threebody_on = true; + threebody_flag = true; one_coeff = 1; } else if (strcmp(arg[iarg+1], "off") == 0) { - threebody_on = false; + threebody_flag = false; one_coeff = 0; // Allow for multiple pair_coeff's } else error->all(FLERR,"Illegal pair_style command"); iarg += 2; diff --git a/src/MANYBODY/pair_sw.h b/src/MANYBODY/pair_sw.h index 34fbfa4a4a..76d1bbc202 100644 --- a/src/MANYBODY/pair_sw.h +++ b/src/MANYBODY/pair_sw.h @@ -48,11 +48,11 @@ class PairSW : public Pair { }; protected: - double cutmax; // max cutoff for all elements - Param *params; // parameter set for an I-J-K interaction - int maxshort; // size of short neighbor list array - int *neighshort; // short neighbor list array - bool threebody_on; // whether to run threebody loop + double cutmax; // max cutoff for all elements + Param *params; // parameter set for an I-J-K interaction + int maxshort; // size of short neighbor list array + int *neighshort; // short neighbor list array + bool threebody_flag; // whether to run threebody loop void settings(int, char **) override; virtual void allocate(); diff --git a/src/OPENMP/pair_sw_omp.cpp b/src/OPENMP/pair_sw_omp.cpp index 32df823e31..a1c0bec34c 100644 --- a/src/OPENMP/pair_sw_omp.cpp +++ b/src/OPENMP/pair_sw_omp.cpp @@ -156,7 +156,7 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) if (EVFLAG) ev_tally_thr(this,i,j,nlocal,/* newton_pair */ 1, evdwl,0.0,fpair,delx,dely,delz,thr); } - if (threebody_on) { + if (threebody_flag) { jnumm1 = numshort - 1; for (jj = 0; jj < jnumm1; jj++) { From e8c2dcc693f1f0a46a02dd989de2e35e56cd5119 Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:25:35 -0600 Subject: [PATCH 27/75] Fix doc example and doc note. --- doc/src/pair_sw.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index a249856371..4f51be7a6a 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -49,8 +49,9 @@ Examples pair_coeff * * tmd.sw.mod Mo S S pair_style hybrid sw sw threebody off - pair_coeff * * mW_xL.sw mW NULL - pair_coeff * 2 mW_xL.sw mW xL + pair_coeff * * sw 1 mW_xL.sw mW NULL + pair_coeff 1 2 sw 2 mW_xL.sw mW xL + pair_coeff 2 2 sw 2 mW_xL.sw mW xL Description """"""""""" @@ -154,7 +155,7 @@ potentials. .. note:: - When the *threebody on* keyword is used, multiple pair_coeff commands may + When the *threebody off* keyword is used, multiple pair_coeff commands may be used to specific the pairs of atoms which don't require three-body term. In these cases, the first 2 arguments are not required to be \* \*. From 4db7f91c4805b472bde3205ea012cb78a3a36d75 Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:52:57 -0600 Subject: [PATCH 28/75] Use utils::logical() to parse arguments. --- src/MANYBODY/pair_sw.cpp | 9 ++------- src/MANYBODY/pair_sw.h | 10 +++++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 0564f9be2b..4d6080c146 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -240,13 +240,8 @@ void PairSW::settings(int narg, char ** arg) while (iarg < narg) { if (strcmp(arg[iarg],"threebody") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style command"); - if (strcmp(arg[iarg+1], "on") == 0) { - threebody_flag = true; - one_coeff = 1; - } else if (strcmp(arg[iarg+1], "off") == 0) { - threebody_flag = false; - one_coeff = 0; // Allow for multiple pair_coeff's - } else error->all(FLERR,"Illegal pair_style command"); + threebody_flag = utils::logical(FLERR,arg[iarg+1],false,lmp); + one_coeff = threebody_flag; iarg += 2; } else error->all(FLERR,"Illegal pair_style command"); } diff --git a/src/MANYBODY/pair_sw.h b/src/MANYBODY/pair_sw.h index 76d1bbc202..40d3568fb4 100644 --- a/src/MANYBODY/pair_sw.h +++ b/src/MANYBODY/pair_sw.h @@ -48,11 +48,11 @@ class PairSW : public Pair { }; protected: - double cutmax; // max cutoff for all elements - Param *params; // parameter set for an I-J-K interaction - int maxshort; // size of short neighbor list array - int *neighshort; // short neighbor list array - bool threebody_flag; // whether to run threebody loop + double cutmax; // max cutoff for all elements + Param *params; // parameter set for an I-J-K interaction + int maxshort; // size of short neighbor list array + int *neighshort; // short neighbor list array + int threebody_flag; // whether to run threebody loop void settings(int, char **) override; virtual void allocate(); From 9a05cd3e983ef33edfdfba6a625872f13472ede7 Mon Sep 17 00:00:00 2001 From: jkelowitt <58435724+jkelowitt@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:59:32 -0600 Subject: [PATCH 29/75] Fix gramatical errors --- doc/src/pair_sw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index 4f51be7a6a..5a4e8f4c38 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -33,7 +33,7 @@ Syntax delta2 = The maximum threshold for the variation of cosine of three-body angle *threebody* value = *on* or *off* (optional) on (default) = Compute both the three-body and two-body terms of the potential - off = Compute both only the two-body terms of the potential + off = Compute only the two-body term of the potential Examples From 7546955046e60072b13d098a30331a004ad2ea8d Mon Sep 17 00:00:00 2001 From: Steve Plimpton Date: Wed, 29 Jun 2022 15:10:03 -0600 Subject: [PATCH 30/75] optimizations to input class for reading very long lines --- src/input.cpp | 174 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 70 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index a28bd48296..d40bb55747 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -188,17 +188,20 @@ of the file is reached. The *infile* pointer will usually point to void Input::file() { - int m,n; + int m,n,mstart,ntriple,endfile; while (true) { // read a line from input script - // n = length of line including str terminator, 0 if end of file + // when done, n = length of line including str terminator, 0 if end of file // if line ends in continuation char '&', concatenate next line + // if triple quotes are used, read until closing triple quotes if (me == 0) { - + ntriple = 0; + endfile = 0; m = 0; + while (true) { if (infile == nullptr) { @@ -206,38 +209,58 @@ void Input::file() break; } - if (maxline-m < 2) reallocate(line,maxline,0); + mstart = m; - // end of file reached, so break - // n == 0 if nothing read, else n = line with str terminator + while (1) { + if (maxline-m < 2) reallocate(line,maxline,0); - if (fgets(&line[m],maxline-m,infile) == nullptr) { - if (m) n = strlen(line) + 1; - else n = 0; + // end of file reached, so break + // n == 0 if nothing read, else n = line with str terminator + + if (fgets(&line[m],maxline-m,infile) == nullptr) { + endfile = 1; + if (m) n = strlen(line) + 1; + else n = 0; + break; + } + + // continue if last char read was not a newline + // can happen if line is very long + + m += strlen(&line[m]); + if (line[m-1] != '\n') continue; break; } - // continue if last char read was not a newline - // could happen if line is very long + if (endfile) break; - m = strlen(line); - if (line[m-1] != '\n') continue; + // add # of triple quotes in just-read line to ntriple - // continue reading if final printable char is & char - // or if odd number of triple quotes - // else break with n = line with str terminator + ntriple += numtriple(&line[mstart]); + + // trim whitespace from end of line + // line[m] = last printable char m--; while (m >= 0 && isspace(line[m])) m--; - if (m < 0 || line[m] != '&') { - if (numtriple(line) % 2) { - m += 2; - continue; - } - line[m+1] = '\0'; - n = m+2; - break; + + // continue reading if final printable char is "&" + + if (m >= 0 && line[m] == '&') continue; + + // continue reading if odd number of triple quotes + + if (ntriple % 2) { + line[m+1] = '\n'; + m += 2; + continue; } + + // done, break with n = length of line with str terminator + + line[m+1] = '\0'; + n = m+2; + break; } } @@ -371,8 +394,9 @@ char *Input::one(const std::string &single) } /* ---------------------------------------------------------------------- - Send text to active echo file pointers + send text to active echo file pointers ------------------------------------------------------------------------- */ + void Input::write_echo(const std::string &txt) { if (me == 0) { @@ -399,34 +423,35 @@ void Input::parse() if (n > maxcopy) reallocate(copy,maxcopy,n); strcpy(copy,line); - // strip any # comment by replacing it with 0 - // do not strip from a # inside single/double/triple quotes - // quoteflag = 1,2,3 when encounter first single/double,triple quote - // quoteflag = 0 when encounter matching single/double,triple quote + // strip a # comment by replacing it with 0 + // do not treat a # inside single/double/triple quotes as a comment - int quoteflag = 0; + char *ptrmatch; char *ptr = copy; + while (*ptr) { - if (*ptr == '#' && !quoteflag) { + if (*ptr == '#') { *ptr = '\0'; break; } - if (quoteflag == 0) { + if (*ptr == '\'') { + ptrmatch = strchr(ptr+1,'\''); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched single quote in command"); + ptr = ptrmatch + 1; + } else if (*ptr == '"') { if (strstr(ptr,"\"\"\"") == ptr) { - quoteflag = 3; - ptr += 2; + ptrmatch = strstr(ptr+3,"\"\"\""); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched triple quote in command"); + ptr = ptrmatch + 3; + } else { + ptrmatch = strchr(ptr+1,'"'); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched double quote in command"); + ptr = ptrmatch + 1; } - else if (*ptr == '"') quoteflag = 2; - else if (*ptr == '\'') quoteflag = 1; - } else { - if (quoteflag == 3 && strstr(ptr,"\"\"\"") == ptr) { - quoteflag = 0; - ptr += 2; - } - else if (quoteflag == 2 && *ptr == '"') quoteflag = 0; - else if (quoteflag == 1 && *ptr == '\'') quoteflag = 0; - } - ptr++; + } else ptr++; } if (utils::has_utf8(copy)) { @@ -534,16 +559,18 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) { // use str2 as scratch space to expand str, then copy back to str // reallocate str and str2 as necessary - // do not replace $ inside single/double/triple quotes + // do not replace variables inside single/double/triple quotes // var = pts at variable name, ended by null char // if $ is followed by '{', trailing '}' becomes null char // else $x becomes x followed by null char // beyond = points to text following variable - int i,n,paren_count; + int i,n,paren_count,nchars;; char immediate[256]; char *var,*value,*beyond; int quoteflag = 0; + char *ptrmatch; + char *ptr = str; n = strlen(str) + 1; @@ -637,34 +664,41 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) if (echo_log && logfile) fprintf(logfile,"%s%s\n",str2,beyond); } - continue; - } + // check for single/double/triple quotes and skip past them - // quoteflag = 1,2,3 when encounter first single/double,triple quote - // quoteflag = 0 when encounter matching single/double,triple quote - // copy 2 extra triple quote chars into str2 - - if (quoteflag == 0) { + } else if (*ptr == '\'') { + ptrmatch = strchr(ptr+1,'\''); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched single quote in command"); + nchars = ptrmatch+1 - ptr; + strncpy(ptr2,ptr,nchars); + ptr += nchars; + ptr2 += nchars; + } else if (*ptr == '"') { if (strstr(ptr,"\"\"\"") == ptr) { - quoteflag = 3; - *ptr2++ = *ptr++; - *ptr2++ = *ptr++; + ptrmatch = strstr(ptr+3,"\"\"\""); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched triple quote in command"); + nchars = ptrmatch+3 - ptr; + strncpy(ptr2,ptr,nchars); + ptr += nchars; + ptr2 += nchars; + } else { + ptrmatch = strchr(ptr+1,'"'); + if (ptrmatch == NULL) + error->all(FLERR,"Unmatched double quote in command"); + nchars = ptrmatch+1 - ptr; + strncpy(ptr2,ptr,nchars); + ptr += nchars; + ptr2 += nchars; } - else if (*ptr == '"') quoteflag = 2; - else if (*ptr == '\'') quoteflag = 1; - } else { - if (quoteflag == 3 && strstr(ptr,"\"\"\"") == ptr) { - quoteflag = 0; - *ptr2++ = *ptr++; - *ptr2++ = *ptr++; - } - else if (quoteflag == 2 && *ptr == '"') quoteflag = 0; - else if (quoteflag == 1 && *ptr == '\'') quoteflag = 0; - } - // copy current character into str2 + // else copy current single character into str2 + + } else *ptr2++ = *ptr++; + + // terminate current str2 so variable sub can perform strlen() - *ptr2++ = *ptr++; *ptr2 = '\0'; } From d327a4c512830f6b733be25f0e7801fa70dd678e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 30 Jun 2022 05:57:52 -0400 Subject: [PATCH 31/75] whitespace --- src/input.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index d40bb55747..11e8a53952 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -226,7 +226,7 @@ void Input::file() // continue if last char read was not a newline // can happen if line is very long - + m += strlen(&line[m]); if (line[m-1] != '\n') continue; break; @@ -436,18 +436,18 @@ void Input::parse() } if (*ptr == '\'') { ptrmatch = strchr(ptr+1,'\''); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched single quote in command"); ptr = ptrmatch + 1; } else if (*ptr == '"') { if (strstr(ptr,"\"\"\"") == ptr) { ptrmatch = strstr(ptr+3,"\"\"\""); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched triple quote in command"); ptr = ptrmatch + 3; } else { ptrmatch = strchr(ptr+1,'"'); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched double quote in command"); ptr = ptrmatch + 1; } @@ -668,7 +668,7 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) } else if (*ptr == '\'') { ptrmatch = strchr(ptr+1,'\''); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched single quote in command"); nchars = ptrmatch+1 - ptr; strncpy(ptr2,ptr,nchars); @@ -677,7 +677,7 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) } else if (*ptr == '"') { if (strstr(ptr,"\"\"\"") == ptr) { ptrmatch = strstr(ptr+3,"\"\"\""); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched triple quote in command"); nchars = ptrmatch+3 - ptr; strncpy(ptr2,ptr,nchars); @@ -685,7 +685,7 @@ void Input::substitute(char *&str, char *&str2, int &max, int &max2, int flag) ptr2 += nchars; } else { ptrmatch = strchr(ptr+1,'"'); - if (ptrmatch == NULL) + if (ptrmatch == NULL) error->all(FLERR,"Unmatched double quote in command"); nchars = ptrmatch+1 - ptr; strncpy(ptr2,ptr,nchars); From d7c4a495ca56507b65817575c103c42fa68f8ccb Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 12:10:17 -0600 Subject: [PATCH 32/75] Force lambda = 0 if threebody_flag is false --- src/MANYBODY/pair_sw.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 4d6080c146..b75c30c04a 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -368,6 +368,8 @@ void PairSW::read_file(char *file) params[nparams].epsilon *= conversion_factor; } + if (!threebody_flag) params[nparams].lambda = 0; + if (params[nparams].epsilon < 0.0 || params[nparams].sigma < 0.0 || params[nparams].littlea < 0.0 || params[nparams].lambda < 0.0 || params[nparams].gamma < 0.0 || params[nparams].biga < 0.0 || From 419460f13b1e2f9fa432c37ca7e3e2444619d9f1 Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 13:30:26 -0600 Subject: [PATCH 33/75] Note limitations on threebody in doc --- doc/src/pair_sw.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index 5a4e8f4c38..b33371dce8 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -120,8 +120,11 @@ This value enables the cut-off function to exclude unnecessary angles in the thr However, the angle variation is much smaller than the given threshold value for actual simulations, so the inconsistency between potential and force can be neglected in actual simulations. -The *threebody* keyword determines whether or not the three-body term of the potential is calculated. Skipping this -significantly increases the speed of the potential, but is only applicable when :math:`\lambda_{ijk} = 0` +The *threebody* keyword determines whether or not the three-body term of the potential is calculated. +Skipping this significantly increases the speed of the calculation, with the tradeoff that :math:\lambda_{ijk} +is forcibly set to 0. If the keyword is used with the pair styles, sw/gpu, sw/intel, or sw/kokkos, +:math:\lambda_{ijk} will still be forcibly set to 0, but no additional benefits will be gained. This keyword +cannot be used for variants of the sw/mod or sw/angle/table pair styles. Only a single pair_coeff command is used with the *sw* and *sw/mod* styles which specifies a Stillinger-Weber potential file with parameters for all From 454c39ae21db4cdc49e962dd87b44fd6cd2fd5ca Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 14:21:11 -0600 Subject: [PATCH 34/75] Invert skipping flag logic. Update keyword and doc to match. --- doc/src/pair_sw.rst | 14 +++--- src/MANYBODY/pair_sw.cpp | 99 ++++++++++++++++++++-------------------- src/MANYBODY/pair_sw.h | 10 ++-- 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index b33371dce8..a83f446a07 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -24,16 +24,16 @@ Syntax pair_style style keyword values * style = *sw* or *sw/mod* -* keyword = *maxdelcs* or *threebody* +* keyword = *maxdelcs* or *skip_threebody* .. parsed-literal:: *maxdelcs* value = delta1 delta2 (optional) delta1 = The minimum thershold for the variation of cosine of three-body angle delta2 = The maximum threshold for the variation of cosine of three-body angle - *threebody* value = *on* or *off* (optional) - on (default) = Compute both the three-body and two-body terms of the potential - off = Compute only the two-body term of the potential + *skip_threebody* value = *on* or *off* (optional) + off (default) = Compute both the three-body and two-body terms of the potential + on = Compute only the two-body term of the potential Examples @@ -48,7 +48,7 @@ Examples pair_style sw/mod maxdelcs 0.25 0.35 pair_coeff * * tmd.sw.mod Mo S S - pair_style hybrid sw sw threebody off + pair_style hybrid sw sw skip_threebody on pair_coeff * * sw 1 mW_xL.sw mW NULL pair_coeff 1 2 sw 2 mW_xL.sw mW xL pair_coeff 2 2 sw 2 mW_xL.sw mW xL @@ -120,7 +120,7 @@ This value enables the cut-off function to exclude unnecessary angles in the thr However, the angle variation is much smaller than the given threshold value for actual simulations, so the inconsistency between potential and force can be neglected in actual simulations. -The *threebody* keyword determines whether or not the three-body term of the potential is calculated. +The *skip_threebody* keyword determines whether or not the three-body term of the potential is calculated. Skipping this significantly increases the speed of the calculation, with the tradeoff that :math:\lambda_{ijk} is forcibly set to 0. If the keyword is used with the pair styles, sw/gpu, sw/intel, or sw/kokkos, :math:\lambda_{ijk} will still be forcibly set to 0, but no additional benefits will be gained. This keyword @@ -158,7 +158,7 @@ potentials. .. note:: - When the *threebody off* keyword is used, multiple pair_coeff commands may + When the *skip_threebody on* keyword is used, multiple pair_coeff commands may be used to specific the pairs of atoms which don't require three-body term. In these cases, the first 2 arguments are not required to be \* \*. diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index b75c30c04a..3cc402cb5f 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -159,51 +159,52 @@ void PairSW::compute(int eflag, int vflag) if (evflag) ev_tally(i,j,nlocal,newton_pair, evdwl,0.0,fpair,delx,dely,delz); } - if (threebody_flag) { - jnumm1 = numshort - 1; - - for (jj = 0; jj < jnumm1; jj++) { - j = neighshort[jj]; - jtype = map[type[j]]; - ijparam = elem3param[itype][jtype][jtype]; - delr1[0] = x[j][0] - xtmp; - delr1[1] = x[j][1] - ytmp; - delr1[2] = x[j][2] - ztmp; - rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - - double fjxtmp,fjytmp,fjztmp; - fjxtmp = fjytmp = fjztmp = 0.0; - - for (kk = jj+1; kk < numshort; kk++) { - k = neighshort[kk]; - ktype = map[type[k]]; - ikparam = elem3param[itype][ktype][ktype]; - ijkparam = elem3param[itype][jtype][ktype]; - - delr2[0] = x[k][0] - xtmp; - delr2[1] = x[k][1] - ytmp; - delr2[2] = x[k][2] - ztmp; - rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - - threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], - rsq1,rsq2,delr1,delr2,fj,fk,eflag,evdwl); - - fxtmp -= fj[0] + fk[0]; - fytmp -= fj[1] + fk[1]; - fztmp -= fj[2] + fk[2]; - fjxtmp += fj[0]; - fjytmp += fj[1]; - fjztmp += fj[2]; - f[k][0] += fk[0]; - f[k][1] += fk[1]; - f[k][2] += fk[2]; - - if (evflag) ev_tally3(i,j,k,evdwl,0.0,fj,fk,delr1,delr2); - } - f[j][0] += fjxtmp; - f[j][1] += fjytmp; - f[j][2] += fjztmp; + if (skip_threebody_flag) { + jnumm1 = 0; + } else { + jnumm1 = numshort - 1; + } + for (jj = 0; jj < jnumm1; jj++) { + j = neighshort[jj]; + jtype = map[type[j]]; + ijparam = elem3param[itype][jtype][jtype]; + delr1[0] = x[j][0] - xtmp; + delr1[1] = x[j][1] - ytmp; + delr1[2] = x[j][2] - ztmp; + rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; + + double fjxtmp,fjytmp,fjztmp; + fjxtmp = fjytmp = fjztmp = 0.0; + + for (kk = jj+1; kk < numshort; kk++) { + k = neighshort[kk]; + ktype = map[type[k]]; + ikparam = elem3param[itype][ktype][ktype]; + ijkparam = elem3param[itype][jtype][ktype]; + + delr2[0] = x[k][0] - xtmp; + delr2[1] = x[k][1] - ytmp; + delr2[2] = x[k][2] - ztmp; + rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; + + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], + rsq1,rsq2,delr1,delr2,fj,fk,eflag,evdwl); + + fxtmp -= fj[0] + fk[0]; + fytmp -= fj[1] + fk[1]; + fztmp -= fj[2] + fk[2]; + fjxtmp += fj[0]; + fjytmp += fj[1]; + fjztmp += fj[2]; + f[k][0] += fk[0]; + f[k][1] += fk[1]; + f[k][2] += fk[2]; + + if (evflag) ev_tally3(i,j,k,evdwl,0.0,fj,fk,delr1,delr2); } + f[j][0] += fjxtmp; + f[j][1] += fjytmp; + f[j][2] += fjztmp; } f[i][0] += fxtmp; f[i][1] += fytmp; @@ -233,15 +234,15 @@ void PairSW::allocate() void PairSW::settings(int narg, char ** arg) { // Default - threebody_flag = true; + skip_threebody_flag = false; int iarg = 0; while (iarg < narg) { - if (strcmp(arg[iarg],"threebody") == 0) { + if (strcmp(arg[iarg],"skip_threebody") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style command"); - threebody_flag = utils::logical(FLERR,arg[iarg+1],false,lmp); - one_coeff = threebody_flag; + skip_threebody_flag = utils::logical(FLERR,arg[iarg+1],false,lmp); + one_coeff = !skip_threebody_flag; iarg += 2; } else error->all(FLERR,"Illegal pair_style command"); } @@ -368,7 +369,7 @@ void PairSW::read_file(char *file) params[nparams].epsilon *= conversion_factor; } - if (!threebody_flag) params[nparams].lambda = 0; + if (skip_threebody_flag) params[nparams].lambda = 0; if (params[nparams].epsilon < 0.0 || params[nparams].sigma < 0.0 || params[nparams].littlea < 0.0 || params[nparams].lambda < 0.0 || diff --git a/src/MANYBODY/pair_sw.h b/src/MANYBODY/pair_sw.h index 40d3568fb4..aa0aef228e 100644 --- a/src/MANYBODY/pair_sw.h +++ b/src/MANYBODY/pair_sw.h @@ -48,11 +48,11 @@ class PairSW : public Pair { }; protected: - double cutmax; // max cutoff for all elements - Param *params; // parameter set for an I-J-K interaction - int maxshort; // size of short neighbor list array - int *neighshort; // short neighbor list array - int threebody_flag; // whether to run threebody loop + double cutmax; // max cutoff for all elements + Param *params; // parameter set for an I-J-K interaction + int maxshort; // size of short neighbor list array + int *neighshort; // short neighbor list array + int skip_threebody_flag; // whether to run threebody loop void settings(int, char **) override; virtual void allocate(); From 0cd15ab927a2954ab7b0dbf2402dd53353b159ed Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 15:24:33 -0600 Subject: [PATCH 35/75] Settings function for sw/angle/table --- src/MANYBODY/pair_sw_angle_table.cpp | 10 ++++++++++ src/MANYBODY/pair_sw_angle_table.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/MANYBODY/pair_sw_angle_table.cpp b/src/MANYBODY/pair_sw_angle_table.cpp index d66a959d52..6eb7cd8a90 100644 --- a/src/MANYBODY/pair_sw_angle_table.cpp +++ b/src/MANYBODY/pair_sw_angle_table.cpp @@ -751,3 +751,13 @@ void PairSWAngleTable::uf_lookup(ParamTable *pm, double x, double &u, double &f) pm->angtable->deltasq6; } } + + +/* ---------------------------------------------------------------------- + global settings +------------------------------------------------------------------------- */ + +void PairSW::settings(int narg, char **/*arg*/) +{ + if (narg != 0) error->all(FLERR,"Illegal pair_style command"); +} \ No newline at end of file diff --git a/src/MANYBODY/pair_sw_angle_table.h b/src/MANYBODY/pair_sw_angle_table.h index 6257d18283..e8ff8f82cd 100644 --- a/src/MANYBODY/pair_sw_angle_table.h +++ b/src/MANYBODY/pair_sw_angle_table.h @@ -69,6 +69,7 @@ class PairSWAngleTable : public PairSW { void spline(double *, double *, int, double, double, double *); double splint(double *, double *, double *, int, double); void uf_lookup(ParamTable *, double, double &, double &); + void settings(int, char **) override; }; } // namespace LAMMPS_NS From 3d939923b582222e3648ebb6c869a9ccc20337ed Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 15:32:08 -0600 Subject: [PATCH 36/75] Fix flag name --- src/OPENMP/pair_sw_omp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OPENMP/pair_sw_omp.cpp b/src/OPENMP/pair_sw_omp.cpp index a1c0bec34c..4056c5d0ed 100644 --- a/src/OPENMP/pair_sw_omp.cpp +++ b/src/OPENMP/pair_sw_omp.cpp @@ -156,7 +156,7 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) if (EVFLAG) ev_tally_thr(this,i,j,nlocal,/* newton_pair */ 1, evdwl,0.0,fpair,delx,dely,delz,thr); } - if (threebody_flag) { + if (skip_threebody_flag) { jnumm1 = numshort - 1; for (jj = 0; jj < jnumm1; jj++) { From 79468f6d4fe9dac39a94c7d3ed1cc115634d2eb4 Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 15:39:02 -0600 Subject: [PATCH 37/75] Fix inverted logic and minimize footprint. --- src/OPENMP/pair_sw_omp.cpp | 73 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/src/OPENMP/pair_sw_omp.cpp b/src/OPENMP/pair_sw_omp.cpp index 4056c5d0ed..a59922b8c4 100644 --- a/src/OPENMP/pair_sw_omp.cpp +++ b/src/OPENMP/pair_sw_omp.cpp @@ -157,50 +157,51 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) evdwl,0.0,fpair,delx,dely,delz,thr); } if (skip_threebody_flag) { + jnumm1 = 0; + } else { jnumm1 = numshort - 1; + } + for (jj = 0; jj < jnumm1; jj++) { + j = neighshort_thr[jj]; + jtype = map[type[j]]; + ijparam = elem3param[itype][jtype][jtype]; + delr1[0] = x[j].x - xtmp; + delr1[1] = x[j].y - ytmp; + delr1[2] = x[j].z - ztmp; + rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - for (jj = 0; jj < jnumm1; jj++) { - j = neighshort_thr[jj]; - jtype = map[type[j]]; - ijparam = elem3param[itype][jtype][jtype]; - delr1[0] = x[j].x - xtmp; - delr1[1] = x[j].y - ytmp; - delr1[2] = x[j].z - ztmp; - rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; + double fjxtmp,fjytmp,fjztmp; + fjxtmp = fjytmp = fjztmp = 0.0; - double fjxtmp,fjytmp,fjztmp; - fjxtmp = fjytmp = fjztmp = 0.0; + for (kk = jj+1; kk < numshort; kk++) { + k = neighshort_thr[kk]; + ktype = map[type[k]]; + ikparam = elem3param[itype][ktype][ktype]; + ijkparam = elem3param[itype][jtype][ktype]; - for (kk = jj+1; kk < numshort; kk++) { - k = neighshort_thr[kk]; - ktype = map[type[k]]; - ikparam = elem3param[itype][ktype][ktype]; - ijkparam = elem3param[itype][jtype][ktype]; + delr2[0] = x[k].x - xtmp; + delr2[1] = x[k].y - ytmp; + delr2[2] = x[k].z - ztmp; + rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - delr2[0] = x[k].x - xtmp; - delr2[1] = x[k].y - ytmp; - delr2[2] = x[k].z - ztmp; - rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], + rsq1,rsq2,delr1,delr2,fj,fk,EFLAG,evdwl); - threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], - rsq1,rsq2,delr1,delr2,fj,fk,EFLAG,evdwl); + fxtmp -= fj[0] + fk[0]; + fytmp -= fj[1] + fk[1]; + fztmp -= fj[2] + fk[2]; + fjxtmp += fj[0]; + fjytmp += fj[1]; + fjztmp += fj[2]; + f[k].x += fk[0]; + f[k].y += fk[1]; + f[k].z += fk[2]; - fxtmp -= fj[0] + fk[0]; - fytmp -= fj[1] + fk[1]; - fztmp -= fj[2] + fk[2]; - fjxtmp += fj[0]; - fjytmp += fj[1]; - fjztmp += fj[2]; - f[k].x += fk[0]; - f[k].y += fk[1]; - f[k].z += fk[2]; - - if (EVFLAG) ev_tally3_thr(this,i,j,k,evdwl,0.0,fj,fk,delr1,delr2,thr); - } - f[j].x += fjxtmp; - f[j].y += fjytmp; - f[j].z += fjztmp; + if (EVFLAG) ev_tally3_thr(this,i,j,k,evdwl,0.0,fj,fk,delr1,delr2,thr); } + f[j].x += fjxtmp; + f[j].y += fjytmp; + f[j].z += fjztmp; } f[i].x += fxtmp; f[i].y += fytmp; From 36aead3877781a0d6311201137ea12b1bd2ee98d Mon Sep 17 00:00:00 2001 From: Jackson Date: Thu, 30 Jun 2022 16:06:52 -0600 Subject: [PATCH 38/75] Fix class name --- src/MANYBODY/pair_sw_angle_table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MANYBODY/pair_sw_angle_table.cpp b/src/MANYBODY/pair_sw_angle_table.cpp index 6eb7cd8a90..7667f77491 100644 --- a/src/MANYBODY/pair_sw_angle_table.cpp +++ b/src/MANYBODY/pair_sw_angle_table.cpp @@ -757,7 +757,7 @@ void PairSWAngleTable::uf_lookup(ParamTable *pm, double x, double &u, double &f) global settings ------------------------------------------------------------------------- */ -void PairSW::settings(int narg, char **/*arg*/) +void PairSWAngleTable::settings(int narg, char **/*arg*/) { if (narg != 0) error->all(FLERR,"Illegal pair_style command"); } \ No newline at end of file From 2bfbd6fba1c3a23995c1ffb21928a00df8a43cd9 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Fri, 1 Jul 2022 13:17:50 -0600 Subject: [PATCH 39/75] Update Kokkos library in LAMMPS to v3.6.1 --- lib/kokkos/CHANGELOG.md | 16 ++++ lib/kokkos/CMakeLists.txt | 2 +- lib/kokkos/Makefile.kokkos | 2 +- lib/kokkos/algorithms/src/Kokkos_Sort.hpp | 79 ++++++++----------- lib/kokkos/algorithms/unit_tests/TestSort.hpp | 58 ++++++++++++++ .../containers/src/Kokkos_ScatterView.hpp | 40 +--------- lib/kokkos/containers/src/Kokkos_Vector.hpp | 18 ++--- .../containers/unit_tests/TestVector.hpp | 17 ++++ lib/kokkos/core/src/CMakeLists.txt | 1 + .../core/src/Cuda/Kokkos_Cuda_Instance.cpp | 9 +++ lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp | 2 +- .../core/src/HIP/Kokkos_HIP_Instance.cpp | 4 +- .../core/src/HIP/Kokkos_HIP_KernelLaunch.hpp | 2 + lib/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp | 3 +- lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp | 9 +++ lib/kokkos/core/src/HPX/Kokkos_HPX.cpp | 9 +++ lib/kokkos/core/src/Kokkos_Cuda.hpp | 1 + lib/kokkos/core/src/Kokkos_HIP_Space.hpp | 3 + lib/kokkos/core/src/Kokkos_HPX.hpp | 1 + lib/kokkos/core/src/Kokkos_OpenMP.hpp | 1 + lib/kokkos/core/src/Kokkos_OpenMPTarget.hpp | 3 + lib/kokkos/core/src/Kokkos_SYCL.hpp | 3 + lib/kokkos/core/src/Kokkos_Serial.hpp | 1 + lib/kokkos/core/src/Kokkos_Threads.hpp | 1 + .../core/src/OpenMP/Kokkos_OpenMP_Exec.cpp | 14 +++- .../core/src/OpenMP/Kokkos_OpenMP_Exec.hpp | 10 ++- .../core/src/SYCL/Kokkos_SYCL_Instance.hpp | 2 +- .../core/src/Threads/Kokkos_ThreadsExec.cpp | 74 ++++++++++++++--- .../core/src/Threads/Kokkos_ThreadsExec.hpp | 5 +- .../src/impl/Kokkos_Profiling_Interface.hpp | 7 +- lib/kokkos/core/src/impl/Kokkos_Serial.cpp | 9 +++ .../core/src/impl/Kokkos_ViewMapping.hpp | 59 +++++++------- .../core/src/impl/Kokkos_ViewTracker.hpp | 2 + lib/kokkos/core/unit_test/TestViewAPI.hpp | 14 ++++ lib/kokkos/core/unit_test/TestViewAPI_e.hpp | 29 +++++++ .../unit_test/tools/TestEventCorrectness.hpp | 15 ++-- lib/kokkos/master_history.txt | 1 + 37 files changed, 363 insertions(+), 163 deletions(-) diff --git a/lib/kokkos/CHANGELOG.md b/lib/kokkos/CHANGELOG.md index dfbe22edde..a908507704 100644 --- a/lib/kokkos/CHANGELOG.md +++ b/lib/kokkos/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## [3.6.01](https://github.com/kokkos/kokkos/tree/3.6.01) (2022-05-23) +[Full Changelog](https://github.com/kokkos/kokkos/compare/3.6.00...3.6.01) + +### Bug Fixes: +- Fix Threads: Fix serial resizing scratch space (3.6.01 cherry-pick) [\#5109](https://github.com/kokkos/kokkos/pull/5109) +- Fix ScatterMin/ScatterMax to use proper atomics (3.6.01 cherry-pick) [\#5046](https://github.com/kokkos/kokkos/pull/5046) +- Fix allocating large Views [\#4907](https://github.com/kokkos/kokkos/pull/4907) +- Fix bounds errors with Kokkos::sort [\#4980](https://github.com/kokkos/kokkos/pull/4980) +- Fix HIP version when printing the configuration [\#4872](https://github.com/kokkos/kokkos/pull/4872) +- Fixed `_CUDA_ARCH__` to `__CUDA_ARCH__` for CUDA LDG [\#4893](https://github.com/kokkos/kokkos/pull/4893) +- Fixed an incorrect struct initialization [\#5028](https://github.com/kokkos/kokkos/pull/5028) +- Fix racing condition in `HIPParallelLaunch` [\#5008](https://github.com/kokkos/kokkos/pull/5008) +- Avoid deprecation warnings with `OpenMPExec::validate_partition` [\#4982](https://github.com/kokkos/kokkos/pull/4982) +- Make View self-assignment not produce double-free [\#5024](https://github.com/kokkos/kokkos/pull/5024) + + ## [3.6.00](https://github.com/kokkos/kokkos/tree/3.6.00) (2022-02-18) [Full Changelog](https://github.com/kokkos/kokkos/compare/3.5.00...3.6.00) diff --git a/lib/kokkos/CMakeLists.txt b/lib/kokkos/CMakeLists.txt index e1c6893725..b0a54118a0 100644 --- a/lib/kokkos/CMakeLists.txt +++ b/lib/kokkos/CMakeLists.txt @@ -136,7 +136,7 @@ ENDIF() set(Kokkos_VERSION_MAJOR 3) set(Kokkos_VERSION_MINOR 6) -set(Kokkos_VERSION_PATCH 00) +set(Kokkos_VERSION_PATCH 01) set(Kokkos_VERSION "${Kokkos_VERSION_MAJOR}.${Kokkos_VERSION_MINOR}.${Kokkos_VERSION_PATCH}") math(EXPR KOKKOS_VERSION "${Kokkos_VERSION_MAJOR} * 10000 + ${Kokkos_VERSION_MINOR} * 100 + ${Kokkos_VERSION_PATCH}") diff --git a/lib/kokkos/Makefile.kokkos b/lib/kokkos/Makefile.kokkos index aa5f7c98f8..755831452b 100644 --- a/lib/kokkos/Makefile.kokkos +++ b/lib/kokkos/Makefile.kokkos @@ -12,7 +12,7 @@ endif KOKKOS_VERSION_MAJOR = 3 KOKKOS_VERSION_MINOR = 6 -KOKKOS_VERSION_PATCH = 00 +KOKKOS_VERSION_PATCH = 01 KOKKOS_VERSION = $(shell echo $(KOKKOS_VERSION_MAJOR)*10000+$(KOKKOS_VERSION_MINOR)*100+$(KOKKOS_VERSION_PATCH) | bc) # Options: Cuda,HIP,SYCL,OpenMPTarget,OpenMP,Threads,Serial diff --git a/lib/kokkos/algorithms/src/Kokkos_Sort.hpp b/lib/kokkos/algorithms/src/Kokkos_Sort.hpp index cde5e6857e..ce97de9b7d 100644 --- a/lib/kokkos/algorithms/src/Kokkos_Sort.hpp +++ b/lib/kokkos/algorithms/src/Kokkos_Sort.hpp @@ -422,54 +422,34 @@ class BinSort { template struct BinOp1D { - int max_bins_; - double mul_; - typename KeyViewType::const_value_type range_; - typename KeyViewType::const_value_type min_; + int max_bins_ = {}; + double mul_ = {}; + double min_ = {}; - BinOp1D() - : max_bins_(0), - mul_(0.0), - range_(typename KeyViewType::const_value_type()), - min_(typename KeyViewType::const_value_type()) {} + BinOp1D() = default; // Construct BinOp with number of bins, minimum value and maxuimum value BinOp1D(int max_bins__, typename KeyViewType::const_value_type min, typename KeyViewType::const_value_type max) : max_bins_(max_bins__ + 1), - // Cast to int64_t to avoid possible overflow when using integer - mul_(std::is_integral::value - ? 1.0 * max_bins__ / (int64_t(max) - int64_t(min)) - : 1.0 * max_bins__ / (max - min)), - range_(max - min), - min_(min) { + // Cast to double to avoid possible overflow when using integer + mul_(static_cast(max_bins__) / + (static_cast(max) - static_cast(min))), + min_(static_cast(min)) { // For integral types the number of bins may be larger than the range // in which case we can exactly have one unique value per bin // and then don't need to sort bins. if (std::is_integral::value && - static_cast(range_) <= static_cast(max_bins__)) { + (static_cast(max) - static_cast(min)) <= + static_cast(max_bins__)) { mul_ = 1.; } } // Determine bin index from key value - template < - class ViewType, - std::enable_if_t::value, - bool> = true> + template KOKKOS_INLINE_FUNCTION int bin(ViewType& keys, const int& i) const { - return int(mul_ * (keys(i) - min_)); - } - - // Determine bin index from key value - template < - class ViewType, - std::enable_if_t::value, - bool> = true> - KOKKOS_INLINE_FUNCTION int bin(ViewType& keys, const int& i) const { - // The cast to int64_t is necessary because otherwise HIP returns the wrong - // result. - return int(mul_ * (int64_t(keys(i)) - int64_t(min_))); + return static_cast(mul_ * (static_cast(keys(i)) - min_)); } // Return maximum bin index + 1 @@ -486,10 +466,9 @@ struct BinOp1D { template struct BinOp3D { - int max_bins_[3]; - double mul_[3]; - typename KeyViewType::non_const_value_type range_[3]; - typename KeyViewType::non_const_value_type min_[3]; + int max_bins_[3] = {}; + double mul_[3] = {}; + double min_[3] = {}; BinOp3D() = default; @@ -498,15 +477,15 @@ struct BinOp3D { max_bins_[0] = max_bins__[0]; max_bins_[1] = max_bins__[1]; max_bins_[2] = max_bins__[2]; - mul_[0] = 1.0 * max_bins__[0] / (max[0] - min[0]); - mul_[1] = 1.0 * max_bins__[1] / (max[1] - min[1]); - mul_[2] = 1.0 * max_bins__[2] / (max[2] - min[2]); - range_[0] = max[0] - min[0]; - range_[1] = max[1] - min[1]; - range_[2] = max[2] - min[2]; - min_[0] = min[0]; - min_[1] = min[1]; - min_[2] = min[2]; + mul_[0] = static_cast(max_bins__[0]) / + (static_cast(max[0]) - static_cast(min[0])); + mul_[1] = static_cast(max_bins__[1]) / + (static_cast(max[1]) - static_cast(min[1])); + mul_[2] = static_cast(max_bins__[2]) / + (static_cast(max[2]) - static_cast(min[2])); + min_[0] = static_cast(min[0]); + min_[1] = static_cast(min[1]); + min_[2] = static_cast(min[2]); } template @@ -596,9 +575,9 @@ std::enable_if_t::value> sort( // TODO: figure out better max_bins then this ... int64_t max_bins = view.extent(0) / 2; if (std::is_integral::value) { - // Cast to int64_t to avoid possible overflow when using integer - int64_t const max_val = result.max_val; - int64_t const min_val = result.min_val; + // Cast to double to avoid possible overflow when using integer + auto const max_val = static_cast(result.max_val); + auto const min_val = static_cast(result.min_val); // using 10M as the cutoff for special behavior (roughly 40MB for the count // array) if ((max_val - min_val) < 10000000) { @@ -606,6 +585,10 @@ std::enable_if_t::value> sort( sort_in_bins = false; } } + if (std::is_floating_point::value) { + KOKKOS_ASSERT(std::isfinite(static_cast(result.max_val) - + static_cast(result.min_val))); + } BinSort bin_sort( view, CompType(max_bins, result.min_val, result.max_val), sort_in_bins); diff --git a/lib/kokkos/algorithms/unit_tests/TestSort.hpp b/lib/kokkos/algorithms/unit_tests/TestSort.hpp index a03847f2b2..9108731c15 100644 --- a/lib/kokkos/algorithms/unit_tests/TestSort.hpp +++ b/lib/kokkos/algorithms/unit_tests/TestSort.hpp @@ -353,6 +353,55 @@ void test_issue_1160_impl() { } } +template +void test_issue_4978_impl() { + Kokkos::View element_("element", 9); + + auto h_element = Kokkos::create_mirror_view(element_); + + h_element(0) = LLONG_MIN; + h_element(1) = 0; + h_element(2) = 3; + h_element(3) = 2; + h_element(4) = 1; + h_element(5) = 3; + h_element(6) = 6; + h_element(7) = 4; + h_element(8) = 3; + + ExecutionSpace exec; + Kokkos::deep_copy(exec, element_, h_element); + + Kokkos::sort(exec, element_); + + Kokkos::deep_copy(exec, h_element, element_); + exec.fence(); + + ASSERT_EQ(h_element(0), LLONG_MIN); + ASSERT_EQ(h_element(1), 0); + ASSERT_EQ(h_element(2), 1); + ASSERT_EQ(h_element(3), 2); + ASSERT_EQ(h_element(4), 3); + ASSERT_EQ(h_element(5), 3); + ASSERT_EQ(h_element(6), 3); + ASSERT_EQ(h_element(7), 4); + ASSERT_EQ(h_element(8), 6); +} + +template +void test_sort_integer_overflow() { + // array with two extrema in reverse order to expose integer overflow bug in + // bin calculation + T a[2] = {Kokkos::Experimental::finite_max::value, + Kokkos::Experimental::finite_min::value}; + auto vd = Kokkos::create_mirror_view_and_copy( + ExecutionSpace(), Kokkos::View(a)); + Kokkos::sort(vd, /*force using Kokkos bin sort*/ true); + auto vh = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), vd); + EXPECT_TRUE(std::is_sorted(vh.data(), vh.data() + 2)) + << "view (" << vh[0] << ", " << vh[1] << ") is not sorted"; +} + //---------------------------------------------------------------------------- template @@ -376,6 +425,11 @@ void test_issue_1160_sort() { test_issue_1160_impl(); } +template +void test_issue_4978_sort() { + test_issue_4978_impl(); +} + template void test_sort(unsigned int N) { test_1D_sort(N); @@ -385,6 +439,10 @@ void test_sort(unsigned int N) { test_dynamic_view_sort(N); #endif test_issue_1160_sort(); + test_issue_4978_sort(); + test_sort_integer_overflow(); + test_sort_integer_overflow(); + test_sort_integer_overflow(); } } // namespace Impl } // namespace Test diff --git a/lib/kokkos/containers/src/Kokkos_ScatterView.hpp b/lib/kokkos/containers/src/Kokkos_ScatterView.hpp index 024b4618a4..e4dd9531fc 100644 --- a/lib/kokkos/containers/src/Kokkos_ScatterView.hpp +++ b/lib/kokkos/containers/src/Kokkos_ScatterView.hpp @@ -369,18 +369,6 @@ struct ScatterValue(&dest, dest_old, dest_new); - success = ((dest_new - dest_old) / dest_old <= 1e-15); - } - } - KOKKOS_INLINE_FUNCTION void join(ValueType& dest, const ValueType& src) const { atomic_prod(&dest, src); @@ -440,21 +428,9 @@ struct ScatterValue src) ? src : dest_old; - dest_new = - Kokkos::atomic_compare_exchange(&dest, dest_old, dest_new); - success = ((dest_new - dest_old) / dest_old <= 1e-15); - } - } - KOKKOS_INLINE_FUNCTION void join(ValueType& dest, const ValueType& src) const { - atomic_min(dest, src); + atomic_min(&dest, src); } KOKKOS_INLINE_FUNCTION @@ -511,21 +487,9 @@ struct ScatterValue(&dest, dest_old, dest_new); - success = ((dest_new - dest_old) / dest_old <= 1e-15); - } - } - KOKKOS_INLINE_FUNCTION void join(ValueType& dest, const ValueType& src) const { - atomic_max(dest, src); + atomic_max(&dest, src); } KOKKOS_INLINE_FUNCTION diff --git a/lib/kokkos/containers/src/Kokkos_Vector.hpp b/lib/kokkos/containers/src/Kokkos_Vector.hpp index 88721bd89e..eddb878003 100644 --- a/lib/kokkos/containers/src/Kokkos_Vector.hpp +++ b/lib/kokkos/containers/src/Kokkos_Vector.hpp @@ -162,7 +162,7 @@ class vector : public DualView { } DV::sync_host(); DV::modify_host(); - if (it < begin() || it > end()) + if (std::less<>()(it, begin()) || std::less<>()(end(), it)) Kokkos::abort("Kokkos::vector::insert : invalid insert iterator"); if (count == 0) return it; ptrdiff_t start = std::distance(begin(), it); @@ -189,27 +189,21 @@ class vector : public DualView { iterator>::type insert(iterator it, InputIterator b, InputIterator e) { ptrdiff_t count = std::distance(b, e); - if (count == 0) return it; DV::sync_host(); DV::modify_host(); - if (it < begin() || it > end()) + if (std::less<>()(it, begin()) || std::less<>()(end(), it)) Kokkos::abort("Kokkos::vector::insert : invalid insert iterator"); - bool resized = false; - if ((size() == 0) && (it == begin())) { - resize(count); - it = begin(); - resized = true; - } ptrdiff_t start = std::distance(begin(), it); auto org_size = size(); - if (!resized) resize(size() + count); - it = begin() + start; + + // Note: resize(...) invalidates it; use begin() + start instead + resize(size() + count); std::copy_backward(begin() + start, begin() + org_size, begin() + org_size + count); - std::copy(b, e, it); + std::copy(b, e, begin() + start); return begin() + start; } diff --git a/lib/kokkos/containers/unit_tests/TestVector.hpp b/lib/kokkos/containers/unit_tests/TestVector.hpp index 57b92c38f8..c093c7b0c9 100644 --- a/lib/kokkos/containers/unit_tests/TestVector.hpp +++ b/lib/kokkos/containers/unit_tests/TestVector.hpp @@ -172,6 +172,23 @@ struct test_vector_insert { run_test(a); check_test(a, size); } + { test_vector_insert_into_empty(size); } + } + + void test_vector_insert_into_empty(const size_t size) { + using Vector = Kokkos::vector; + { + Vector a; + Vector b(size); + a.insert(a.begin(), b.begin(), b.end()); + ASSERT_EQ(a.size(), size); + } + + { + Vector c; + c.insert(c.begin(), size, Scalar{}); + ASSERT_EQ(c.size(), size); + } } }; diff --git a/lib/kokkos/core/src/CMakeLists.txt b/lib/kokkos/core/src/CMakeLists.txt index 88cca93f3c..793e07a841 100644 --- a/lib/kokkos/core/src/CMakeLists.txt +++ b/lib/kokkos/core/src/CMakeLists.txt @@ -8,6 +8,7 @@ KOKKOS_INCLUDE_DIRECTORIES( INSTALL (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION ${KOKKOS_HEADER_DIR} + FILES_MATCHING PATTERN desul/src EXCLUDE PATTERN "*.inc" PATTERN "*.inc_*" diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp index 294be2774b..aaa9ea8ad4 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp @@ -1007,6 +1007,15 @@ void CudaSpaceInitializer::print_configuration(std::ostream &msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } // namespace Kokkos #else diff --git a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp index 61563a0100..dec6ef15e1 100644 --- a/lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp +++ b/lib/kokkos/core/src/Cuda/Kokkos_Cuda_View.hpp @@ -139,7 +139,7 @@ struct CudaLDGFetch { template KOKKOS_INLINE_FUNCTION ValueType operator[](const iType& i) const { -#if defined(__CUDA_ARCH__) && (350 <= _CUDA_ARCH__) +#if defined(__CUDA_ARCH__) && (350 <= __CUDA_ARCH__) AliasType v = __ldg(reinterpret_cast(&m_ptr[i])); return *(reinterpret_cast(&v)); #else diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp index 4a6a3ba99e..a8a0496afe 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp @@ -132,7 +132,8 @@ void HIPInternal::print_configuration(std::ostream &s) const { s << "macro KOKKOS_ENABLE_HIP : defined" << '\n'; #if defined(HIP_VERSION) s << "macro HIP_VERSION = " << HIP_VERSION << " = version " - << HIP_VERSION / 100 << "." << HIP_VERSION % 100 << '\n'; + << HIP_VERSION_MAJOR << '.' << HIP_VERSION_MINOR << '.' << HIP_VERSION_PATCH + << '\n'; #endif for (int i = 0; i < dev_info.m_hipDevCount; ++i) { @@ -467,7 +468,6 @@ void HIPInternal::finalize() { } char *HIPInternal::get_next_driver(size_t driverTypeSize) const { - std::lock_guard const lock(m_mutexWorkArray); if (d_driverWorkArray == nullptr) { KOKKOS_IMPL_HIP_SAFE_CALL( hipHostMalloc(&d_driverWorkArray, diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp index 384b7ffd67..70b979e00a 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp @@ -490,6 +490,8 @@ struct HIPParallelLaunch< KOKKOS_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE(); + std::lock_guard const lock(hip_instance->m_mutexWorkArray); + // Invoke the driver function on the device DriverType *d_driver = reinterpret_cast( hip_instance->get_next_driver(sizeof(DriverType))); diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp index f334d93412..e9cfbf99f7 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp @@ -56,8 +56,7 @@ namespace Kokkos { #ifdef KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE namespace Impl { -__device__ __constant__ HIPLockArrays g_device_hip_lock_arrays = {nullptr, - nullptr, 0}; +__device__ __constant__ HIPLockArrays g_device_hip_lock_arrays = {nullptr, 0}; } #endif diff --git a/lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp b/lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp index 6ade677fa8..776b7c6abe 100644 --- a/lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp +++ b/lib/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp @@ -464,6 +464,15 @@ void HIPSpaceInitializer::print_configuration(std::ostream& msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } // namespace Kokkos //============================================================================== diff --git a/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp b/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp index acf2224f02..623c7da025 100644 --- a/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp +++ b/lib/kokkos/core/src/HPX/Kokkos_HPX.cpp @@ -199,6 +199,15 @@ void HPXSpaceInitializer::print_configuration(std::ostream &msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } // namespace Kokkos #else diff --git a/lib/kokkos/core/src/Kokkos_Cuda.hpp b/lib/kokkos/core/src/Kokkos_Cuda.hpp index 6305a1fa5d..0063b1cd1e 100644 --- a/lib/kokkos/core/src/Kokkos_Cuda.hpp +++ b/lib/kokkos/core/src/Kokkos_Cuda.hpp @@ -260,6 +260,7 @@ template <> struct DeviceTypeTraits { /// \brief An ID to differentiate (for example) Serial from OpenMP in Tooling static constexpr DeviceType id = DeviceType::Cuda; + static int device_id(const Cuda& exec) { return exec.cuda_device(); } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_HIP_Space.hpp b/lib/kokkos/core/src/Kokkos_HIP_Space.hpp index 1371d21d38..68869a6074 100644 --- a/lib/kokkos/core/src/Kokkos_HIP_Space.hpp +++ b/lib/kokkos/core/src/Kokkos_HIP_Space.hpp @@ -571,6 +571,9 @@ namespace Experimental { template <> struct DeviceTypeTraits { static constexpr DeviceType id = DeviceType::HIP; + static int device_id(const Kokkos::Experimental::HIP& exec) { + return exec.hip_device(); + } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_HPX.hpp b/lib/kokkos/core/src/Kokkos_HPX.hpp index d2ae9c0ec2..9238ca30a7 100644 --- a/lib/kokkos/core/src/Kokkos_HPX.hpp +++ b/lib/kokkos/core/src/Kokkos_HPX.hpp @@ -500,6 +500,7 @@ namespace Experimental { template <> struct DeviceTypeTraits { static constexpr DeviceType id = DeviceType::HPX; + static int device_id(const Kokkos::Experimental::HPX &) { return 0; } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_OpenMP.hpp b/lib/kokkos/core/src/Kokkos_OpenMP.hpp index 5d76e689f2..767e5b9324 100644 --- a/lib/kokkos/core/src/Kokkos_OpenMP.hpp +++ b/lib/kokkos/core/src/Kokkos_OpenMP.hpp @@ -179,6 +179,7 @@ namespace Experimental { template <> struct DeviceTypeTraits { static constexpr DeviceType id = DeviceType::OpenMP; + static int device_id(const OpenMP&) { return 0; } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_OpenMPTarget.hpp b/lib/kokkos/core/src/Kokkos_OpenMPTarget.hpp index f394f32408..373dc3d9c7 100644 --- a/lib/kokkos/core/src/Kokkos_OpenMPTarget.hpp +++ b/lib/kokkos/core/src/Kokkos_OpenMPTarget.hpp @@ -130,6 +130,9 @@ template <> struct DeviceTypeTraits<::Kokkos::Experimental::OpenMPTarget> { static constexpr DeviceType id = ::Kokkos::Profiling::Experimental::DeviceType::OpenMPTarget; + static int device_id(const Kokkos::Experimental::OpenMPTarget&) { + return omp_get_default_device(); + } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_SYCL.hpp b/lib/kokkos/core/src/Kokkos_SYCL.hpp index 02095ff7b3..e29093db32 100644 --- a/lib/kokkos/core/src/Kokkos_SYCL.hpp +++ b/lib/kokkos/core/src/Kokkos_SYCL.hpp @@ -182,6 +182,9 @@ template <> struct DeviceTypeTraits { /// \brief An ID to differentiate (for example) Serial from OpenMP in Tooling static constexpr DeviceType id = DeviceType::SYCL; + static int device_id(const Kokkos::Experimental::SYCL& exec) { + return exec.sycl_device(); + } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_Serial.hpp b/lib/kokkos/core/src/Kokkos_Serial.hpp index 9aada48bf6..b2e524c374 100644 --- a/lib/kokkos/core/src/Kokkos_Serial.hpp +++ b/lib/kokkos/core/src/Kokkos_Serial.hpp @@ -226,6 +226,7 @@ namespace Experimental { template <> struct DeviceTypeTraits { static constexpr DeviceType id = DeviceType::Serial; + static int device_id(const Serial&) { return 0; } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/Kokkos_Threads.hpp b/lib/kokkos/core/src/Kokkos_Threads.hpp index 45a2d0e326..5879209f12 100644 --- a/lib/kokkos/core/src/Kokkos_Threads.hpp +++ b/lib/kokkos/core/src/Kokkos_Threads.hpp @@ -175,6 +175,7 @@ namespace Experimental { template <> struct DeviceTypeTraits { static constexpr DeviceType id = DeviceType::Threads; + static int device_id(const Threads&) { return 0; } }; } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp index d2283d456f..66dbbacce9 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp @@ -67,8 +67,9 @@ __thread int t_openmp_hardware_id = 0; __thread Impl::OpenMPExec *t_openmp_instance = nullptr; #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3 -void OpenMPExec::validate_partition(const int nthreads, int &num_partitions, - int &partition_size) { +void OpenMPExec::validate_partition_impl(const int nthreads, + int &num_partitions, + int &partition_size) { if (nthreads == 1) { num_partitions = 1; partition_size = 1; @@ -506,6 +507,15 @@ void OpenMPSpaceInitializer::print_configuration(std::ostream &msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } // namespace Kokkos #else diff --git a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp index 2f647af77e..ede24d1094 100644 --- a/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp +++ b/lib/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp @@ -93,7 +93,11 @@ class OpenMPExec { #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3 KOKKOS_DEPRECATED static void validate_partition(const int nthreads, int& num_partitions, - int& partition_size); + int& partition_size) { + validate_partition_impl(nthreads, num_partitions, partition_size); + } + static void validate_partition_impl(const int nthreads, int& num_partitions, + int& partition_size); #endif private: @@ -179,8 +183,8 @@ KOKKOS_DEPRECATED void OpenMP::partition_master(F const& f, int num_partitions, Exec* prev_instance = Impl::t_openmp_instance; - Exec::validate_partition(prev_instance->m_pool_size, num_partitions, - partition_size); + Exec::validate_partition_impl(prev_instance->m_pool_size, num_partitions, + partition_size); OpenMP::memory_space space; diff --git a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp index 907e4e9efe..45aacd7258 100644 --- a/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp +++ b/lib/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp @@ -72,7 +72,7 @@ class SYCLInternal { bool force_shrink = false); uint32_t impl_get_instance_id() const; - int m_syclDev = -1; + int m_syclDev = 0; size_t m_maxWorkgroupSize = 0; uint32_t m_maxConcurrency = 0; diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp index 8a7c49871b..9682564ee0 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp @@ -399,27 +399,68 @@ bool ThreadsExec::wake() { //---------------------------------------------------------------------------- +void ThreadsExec::execute_resize_scratch_in_serial() { + const unsigned begin = s_threads_process.m_pool_base ? 1 : 0; + + auto deallocate_scratch_memory = [](ThreadsExec &exec) { + if (exec.m_scratch) { + using Record = + Kokkos::Impl::SharedAllocationRecord; + Record *const r = Record::get_record(exec.m_scratch); + exec.m_scratch = nullptr; + Record::decrement(r); + } + }; + if (s_threads_process.m_pool_base) { + for (unsigned i = s_thread_pool_size[0]; begin < i;) { + deallocate_scratch_memory(*s_threads_exec[--i]); + } + } + + s_current_function = &first_touch_allocate_thread_private_scratch; + s_current_function_arg = &s_threads_process; + + // Make sure function and arguments are written before activating threads. + memory_fence(); + + for (unsigned i = s_thread_pool_size[0]; begin < i;) { + ThreadsExec &th = *s_threads_exec[--i]; + + th.m_pool_state = ThreadsExec::Active; + + wait_yield(th.m_pool_state, ThreadsExec::Active); + } + + if (s_threads_process.m_pool_base) { + deallocate_scratch_memory(s_threads_process); + s_threads_process.m_pool_state = ThreadsExec::Active; + first_touch_allocate_thread_private_scratch(s_threads_process, nullptr); + s_threads_process.m_pool_state = ThreadsExec::Inactive; + } + + s_current_function_arg = nullptr; + s_current_function = nullptr; + + // Make sure function and arguments are cleared before proceeding. + memory_fence(); +} + +//---------------------------------------------------------------------------- + void *ThreadsExec::root_reduce_scratch() { return s_threads_process.reduce_memory(); } -void ThreadsExec::execute_resize_scratch(ThreadsExec &exec, const void *) { - using Record = Kokkos::Impl::SharedAllocationRecord; - - if (exec.m_scratch) { - Record *const r = Record::get_record(exec.m_scratch); - - exec.m_scratch = nullptr; - - Record::decrement(r); - } - +void ThreadsExec::first_touch_allocate_thread_private_scratch(ThreadsExec &exec, + const void *) { exec.m_scratch_reduce_end = s_threads_process.m_scratch_reduce_end; exec.m_scratch_thread_end = s_threads_process.m_scratch_thread_end; if (s_threads_process.m_scratch_thread_end) { // Allocate tracked memory: { + using Record = + Kokkos::Impl::SharedAllocationRecord; Record *const r = Record::allocate(Kokkos::HostSpace(), "Kokkos::thread_scratch", s_threads_process.m_scratch_thread_end); @@ -461,7 +502,7 @@ void *ThreadsExec::resize_scratch(size_t reduce_size, size_t thread_size) { s_threads_process.m_scratch_reduce_end = reduce_size; s_threads_process.m_scratch_thread_end = reduce_size + thread_size; - execute_resize_scratch(s_threads_process, nullptr); + execute_resize_scratch_in_serial(); s_threads_process.m_scratch = s_threads_exec[0]->m_scratch; } @@ -845,6 +886,15 @@ void ThreadsSpaceInitializer::print_configuration(std::ostream &msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } /* namespace Kokkos */ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp index 561b1ce292..d17f417bbc 100644 --- a/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp +++ b/lib/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp @@ -123,12 +123,15 @@ class ThreadsExec { static void global_unlock(); static void spawn(); - static void execute_resize_scratch(ThreadsExec &, const void *); + static void first_touch_allocate_thread_private_scratch(ThreadsExec &, + const void *); static void execute_sleep(ThreadsExec &, const void *); ThreadsExec(const ThreadsExec &); ThreadsExec &operator=(const ThreadsExec &); + static void execute_resize_scratch_in_serial(); + public: KOKKOS_INLINE_FUNCTION int pool_size() const { return m_pool_size; } KOKKOS_INLINE_FUNCTION int pool_rank() const { return m_pool_rank; } diff --git a/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp b/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp index 4e0e81405f..d526682056 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp @@ -118,11 +118,14 @@ template constexpr uint32_t device_id_root() { constexpr auto device_id = static_cast(DeviceTypeTraits::id); - return (device_id << num_instance_bits); + return (device_id << (num_instance_bits + num_device_bits)); } template inline uint32_t device_id(ExecutionSpace const& space) noexcept { - return device_id_root() + space.impl_instance_id(); + return device_id_root() + + (DeviceTypeTraits::device_id(space) + << num_instance_bits) + + space.impl_instance_id(); } } // namespace Experimental } // namespace Tools diff --git a/lib/kokkos/core/src/impl/Kokkos_Serial.cpp b/lib/kokkos/core/src/impl/Kokkos_Serial.cpp index c49e838d8f..e5917eb59d 100644 --- a/lib/kokkos/core/src/impl/Kokkos_Serial.cpp +++ b/lib/kokkos/core/src/impl/Kokkos_Serial.cpp @@ -233,6 +233,15 @@ void SerialSpaceInitializer::print_configuration(std::ostream& msg, } } // namespace Impl + +#ifdef KOKKOS_ENABLE_CXX14 +namespace Tools { +namespace Experimental { +constexpr DeviceType DeviceTypeTraits::id; +} +} // namespace Tools +#endif + } // namespace Kokkos #else diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp index 09f7af0918..f606a39839 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewMapping.hpp @@ -1005,15 +1005,15 @@ struct ViewOffset< /* Cardinality of the domain index space */ KOKKOS_INLINE_FUNCTION constexpr size_type size() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } /* Span of the range space */ KOKKOS_INLINE_FUNCTION constexpr size_type span() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { @@ -1026,23 +1026,24 @@ struct ViewOffset< return m_dim.N0; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_2() const { - return m_dim.N0 * m_dim.N1; + return size_type(m_dim.N0) * m_dim.N1; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_3() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_4() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_5() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_6() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5; } KOKKOS_INLINE_FUNCTION constexpr size_type stride_7() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6; } // Stride with [ rank ] value is the total length @@ -1288,8 +1289,8 @@ struct ViewOffset< /* Cardinality of the domain index space */ KOKKOS_INLINE_FUNCTION constexpr size_type size() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } /* Span of the range space */ @@ -1633,15 +1634,15 @@ struct ViewOffset< /* Cardinality of the domain index space */ KOKKOS_INLINE_FUNCTION constexpr size_type size() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } /* Span of the range space */ KOKKOS_INLINE_FUNCTION constexpr size_type span() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { @@ -1916,14 +1917,14 @@ struct ViewOffset< /* Cardinality of the domain index space */ KOKKOS_INLINE_FUNCTION constexpr size_type size() const { - return m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * m_dim.N5 * - m_dim.N6 * m_dim.N7; + return size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * + m_dim.N5 * m_dim.N6 * m_dim.N7; } /* Span of the range space */ KOKKOS_INLINE_FUNCTION constexpr size_type span() const { - return size() > 0 ? m_dim.N0 * m_stride : 0; + return size() > 0 ? size_type(m_dim.N0) * m_stride : 0; } KOKKOS_INLINE_FUNCTION constexpr bool span_is_contiguous() const { @@ -2066,27 +2067,29 @@ struct ViewOffset< stride(/* 2 <= rank */ m_dim.N1 * (dimension_type::rank == 2 - ? 1 + ? size_t(1) : m_dim.N2 * (dimension_type::rank == 3 - ? 1 + ? size_t(1) : m_dim.N3 * (dimension_type::rank == 4 - ? 1 + ? size_t(1) : m_dim.N4 * (dimension_type::rank == 5 - ? 1 + ? size_t(1) : m_dim.N5 * (dimension_type:: rank == 6 - ? 1 + ? size_t( + 1) : m_dim.N6 * (dimension_type:: rank == 7 - ? 1 + ? size_t( + 1) : m_dim .N7)))))))) { } @@ -2447,8 +2450,8 @@ struct ViewOffset { constexpr size_type size() const { return dimension_type::rank == 0 ? 1 - : m_dim.N0 * m_dim.N1 * m_dim.N2 * m_dim.N3 * m_dim.N4 * - m_dim.N5 * m_dim.N6 * m_dim.N7; + : size_type(m_dim.N0) * m_dim.N1 * m_dim.N2 * m_dim.N3 * + m_dim.N4 * m_dim.N5 * m_dim.N6 * m_dim.N7; } private: diff --git a/lib/kokkos/core/src/impl/Kokkos_ViewTracker.hpp b/lib/kokkos/core/src/impl/Kokkos_ViewTracker.hpp index fe3651886b..972b1b6d9a 100644 --- a/lib/kokkos/core/src/impl/Kokkos_ViewTracker.hpp +++ b/lib/kokkos/core/src/impl/Kokkos_ViewTracker.hpp @@ -91,6 +91,7 @@ struct ViewTracker { template KOKKOS_INLINE_FUNCTION void assign(const View& vt) noexcept { + if (this == reinterpret_cast(&vt.m_track)) return; KOKKOS_IF_ON_HOST(( if (view_traits::is_managed && Kokkos::Impl::SharedAllocationRecord< void, void>::tracking_enabled()) { @@ -102,6 +103,7 @@ struct ViewTracker { KOKKOS_INLINE_FUNCTION ViewTracker& operator=( const ViewTracker& rhs) noexcept { + if (this == &rhs) return *this; KOKKOS_IF_ON_HOST(( if (view_traits::is_managed && Kokkos::Impl::SharedAllocationRecord< void, void>::tracking_enabled()) { diff --git a/lib/kokkos/core/unit_test/TestViewAPI.hpp b/lib/kokkos/core/unit_test/TestViewAPI.hpp index 21602be086..83efae6170 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI.hpp @@ -1087,6 +1087,20 @@ class TestViewAPI { dView4_unmanaged unmanaged_dx = dx; ASSERT_EQ(dx.use_count(), 1); + // Test self assignment +#if defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wself-assign-overloaded" +#endif + dx = dx; // copy-assignment operator +#if defined(__clang__) +#pragma GCC diagnostic pop +#endif + ASSERT_EQ(dx.use_count(), 1); + dx = reinterpret_cast( + dx); // conversion assignment operator + ASSERT_EQ(dx.use_count(), 1); + dView4_unmanaged unmanaged_from_ptr_dx = dView4_unmanaged( dx.data(), dx.extent(0), dx.extent(1), dx.extent(2), dx.extent(3)); diff --git a/lib/kokkos/core/unit_test/TestViewAPI_e.hpp b/lib/kokkos/core/unit_test/TestViewAPI_e.hpp index d4f484a530..d1d38022a7 100644 --- a/lib/kokkos/core/unit_test/TestViewAPI_e.hpp +++ b/lib/kokkos/core/unit_test/TestViewAPI_e.hpp @@ -240,6 +240,35 @@ struct TestViewOverloadResolution { TEST(TEST_CATEGORY, view_overload_resolution) { TestViewOverloadResolution::test_function_overload(); } + +template +struct TestViewAllocationLargeRank { + using ViewType = Kokkos::View; + + KOKKOS_FUNCTION void operator()(int) const { + size_t idx = v.extent(0) - 1; + auto& lhs = v(idx, idx, idx, idx, idx, idx, idx, idx); + lhs = 42; // This is where it segfaulted + } + + ViewType v; +}; + +TEST(TEST_CATEGORY, view_allocation_large_rank) { + using ExecutionSpace = typename TEST_EXECSPACE::execution_space; + using MemorySpace = typename TEST_EXECSPACE::memory_space; + constexpr int dim = 16; + using FunctorType = TestViewAllocationLargeRank; + typename FunctorType::ViewType v("v", dim, dim, dim, dim, dim, dim, dim, dim); + + Kokkos::parallel_for(Kokkos::RangePolicy(0, 1), + FunctorType{v}); + typename FunctorType::ViewType v_single(v.data() + v.size() - 1, 1, 1, 1, 1, + 1, 1, 1, 1); + auto result = + Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, v_single); + ASSERT_EQ(result(0, 0, 0, 0, 0, 0, 0, 0), 42); +} } // namespace Test #include diff --git a/lib/kokkos/core/unit_test/tools/TestEventCorrectness.hpp b/lib/kokkos/core/unit_test/tools/TestEventCorrectness.hpp index 08863232ed..bb1d3156f5 100644 --- a/lib/kokkos/core/unit_test/tools/TestEventCorrectness.hpp +++ b/lib/kokkos/core/unit_test/tools/TestEventCorrectness.hpp @@ -238,13 +238,10 @@ TEST(kokkosp, test_id_gen) { using Kokkos::Tools::Experimental::DeviceTypeTraits; test_wrapper([&]() { Kokkos::DefaultExecutionSpace ex; - auto id = device_id(ex); - auto id_ref = identifier_from_devid(id); - auto success = (id_ref.instance_id == ex.impl_instance_id()) && - (id_ref.device_id == - static_cast( - DeviceTypeTraits::id)); - ASSERT_TRUE(success); + auto id = device_id(ex); + auto id_ref = identifier_from_devid(id); + ASSERT_EQ(DeviceTypeTraits::id, id_ref.type); + ASSERT_EQ(id_ref.instance_id, ex.impl_instance_id()); }); } @@ -253,6 +250,7 @@ TEST(kokkosp, test_id_gen) { */ TEST(kokkosp, test_kernel_sequence) { test_wrapper([&]() { + Kokkos::DefaultExecutionSpace ex; auto root = Kokkos::Tools::Experimental::device_id_root< Kokkos::DefaultExecutionSpace>(); std::vector expected{ @@ -260,11 +258,10 @@ TEST(kokkosp, test_kernel_sequence) { {"named_instance", FencePayload::distinguishable_devices::no, root + num_instances}, {"test_kernel", FencePayload::distinguishable_devices::no, - root + num_instances} + Kokkos::Tools::Experimental::device_id(ex)} }; expect_fence_events(expected, [=]() { - Kokkos::DefaultExecutionSpace ex; TestFunctor tf; ex.fence("named_instance"); Kokkos::parallel_for( diff --git a/lib/kokkos/master_history.txt b/lib/kokkos/master_history.txt index e174b47f67..41c755a8a8 100644 --- a/lib/kokkos/master_history.txt +++ b/lib/kokkos/master_history.txt @@ -27,3 +27,4 @@ tag: 3.4.00 date: 04:26:2021 master: 1fb0c284 release: 5d7738d6 tag: 3.4.01 date: 05:20:2021 master: 4b97a22f release: 410b15c8 tag: 3.5.00 date: 11:19:2021 master: c28a8b03 release: 21b879e4 tag: 3.6.00 date: 04:14:2022 master: 2834f94a release: 6ea708ff +tag: 3.6.01 date: 06:16:2022 master: b52f8c83 release: afe9b404 From cf49042fe6ce0d6a6dd3fac65c89c50be4c87e34 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Fri, 1 Jul 2022 14:13:08 -0600 Subject: [PATCH 40/75] Update Kokkos version in CMake --- cmake/Modules/Packages/KOKKOS.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Packages/KOKKOS.cmake b/cmake/Modules/Packages/KOKKOS.cmake index cf3d19c720..7f23a6f777 100644 --- a/cmake/Modules/Packages/KOKKOS.cmake +++ b/cmake/Modules/Packages/KOKKOS.cmake @@ -47,8 +47,8 @@ if(DOWNLOAD_KOKKOS) list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}") list(APPEND KOKKOS_LIB_BUILD_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") include(ExternalProject) - set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/3.6.00.tar.gz" CACHE STRING "URL for KOKKOS tarball") - set(KOKKOS_MD5 "b5c44ea961031795f434002cd7b31c20" CACHE STRING "MD5 checksum of KOKKOS tarball") + set(KOKKOS_URL "https://github.com/kokkos/kokkos/archive/3.6.01.tar.gz" CACHE STRING "URL for KOKKOS tarball") + set(KOKKOS_MD5 "0ec97fc0c356dd65bd2487defe81a7bf" CACHE STRING "MD5 checksum of KOKKOS tarball") mark_as_advanced(KOKKOS_URL) mark_as_advanced(KOKKOS_MD5) ExternalProject_Add(kokkos_build @@ -72,7 +72,7 @@ if(DOWNLOAD_KOKKOS) add_dependencies(LAMMPS::KOKKOSCORE kokkos_build) add_dependencies(LAMMPS::KOKKOSCONTAINERS kokkos_build) elseif(EXTERNAL_KOKKOS) - find_package(Kokkos 3.6.00 REQUIRED CONFIG) + find_package(Kokkos 3.6.01 REQUIRED CONFIG) target_link_libraries(lammps PRIVATE Kokkos::kokkos) target_link_libraries(lmp PRIVATE Kokkos::kokkos) else() From dc1b7ba0a4195082f1540249679778346d9da644 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 1 Jul 2022 15:37:00 -0600 Subject: [PATCH 41/75] Parallelization and ij pair collapse. --- examples/snap/compute_snap_dgrad.py | 21 ++- src/ML-SNAP/compute_snap.cpp | 198 ++++++++++++++-------------- src/ML-SNAP/compute_snap.h | 1 + 3 files changed, 118 insertions(+), 102 deletions(-) diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index 180734cb0c..95f2e1fa55 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -27,7 +27,7 @@ lmp = lammps(cmdargs = cmds) def run_lammps(dgradflag): # simulation settings - + lmp.command("clear") lmp.command("units metal") lmp.command("boundary p p p") @@ -40,7 +40,7 @@ def run_lammps(dgradflag): lmp.command("displace_atoms all random 0.01 0.01 0.01 123456") # potential settings - + snap_options = f'{rcutfac} {rfac0} {twojmax} {radelem1} {radelem2} {wj1} {wj2} rmin0 {rmin0} quadraticflag {quadratic} bzeroflag {bzero} switchflag {switch} bikflag {bikflag} dgradflag {dgradflag}' lmp.command(f"pair_style zero {rcutfac}") lmp.command(f"pair_coeff * *") @@ -111,6 +111,12 @@ run_lammps(dgradflag) lmp_snap = lmp.numpy.extract_compute("snap", LMP_STYLE_GLOBAL, LMP_TYPE_ARRAY) +# print snap array to observe +#if (me==0): +# np.savetxt("test_snap.dat", lmp_snap, fmt="%d %d %d %f %f %f %f %f") + +# take out rows with zero column + # extract dBj/dRi (includes dBi/dRi) natoms = lmp.get_natoms() @@ -120,6 +126,14 @@ dbdr_length = np.shape(lmp_snap)[0]-(natoms) - 1 dBdR = lmp_snap[natoms:(natoms+dbdr_length),3:(nd+3)] force_indices = lmp_snap[natoms:(natoms+dbdr_length),0:3].astype(np.int32) +# strip rows with all zero descriptor gradients to demonstrate how to save memory + +nonzero_rows = lmp_snap[natoms:(natoms+dbdr_length),3:(nd+3)] != 0.0 +nonzero_rows = np.any(nonzero_rows, axis=1) +dBdR = dBdR[nonzero_rows, :] +force_indices = force_indices[nonzero_rows,:] +dbdr_length = np.shape(dBdR)[0] + # sum over atoms i that j is a neighbor of, like dgradflag = 0 does. array1 = np.zeros((3*natoms,nd)) @@ -128,6 +142,7 @@ for k in range(0,nd): i = force_indices[l,0] j = force_indices[l,1] a = force_indices[l,2] + #print(f"{i} {j} {a}") array1[3 * j + a, k] += dBdR[l,k] # run lammps with dgradflag off @@ -135,7 +150,7 @@ for k in range(0,nd): if me == 0: print("Running with dgradflag off") -dgradflag = 0 +dgradflag = 0 run_lammps(dgradflag) # get global snap array diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index eb404d70a4..52c1b32fbc 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -190,7 +190,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (dgradflag && quadraticflag) error->all(FLERR,"Illegal compute snap command: dgradflag=1 not implemented for quadratic SNAP"); - + snaptr = new SNA(lmp, rfac0, twojmax, rmin0, switchflag, bzeroflag, chemflag, bnormflag, wselfallflag, @@ -372,6 +372,31 @@ void ComputeSnap::compute_array() const int typeoffset_local = ndims_peratom*nperdim*(itype-1); const int typeoffset_global = nperdim*(itype-1); + if (dgradflag){ + // dBi/dRi tags + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + // dBi/dRj tags + for (int j=0; jtag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + } + } + // insure rij, inside, and typej are of size jnum snaptr->grow_rij(jnum); @@ -459,13 +484,13 @@ void ComputeSnap::compute_array() double bix = snaptr->dblist[icoeff][0]; double biy = snaptr->dblist[icoeff][1]; double biz = snaptr->dblist[icoeff][2]; - + // diagonal elements of quadratic matrix - + double dbxtmp = bi*bix; double dbytmp = bi*biy; double dbztmp = bi*biz; - + snadi[ncount] += dbxtmp; snadi[ncount+yoffset] += dbytmp; snadi[ncount+zoffset] += dbztmp; @@ -476,7 +501,7 @@ void ComputeSnap::compute_array() ncount++; // upper-triangular elements of quadratic matrix - + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { double dbxtmp = bi*snaptr->dblist[jcoeff][0] + bix*snaptr->blist[jcoeff]; @@ -484,27 +509,27 @@ void ComputeSnap::compute_array() + biy*snaptr->blist[jcoeff]; double dbztmp = bi*snaptr->dblist[jcoeff][2] + biz*snaptr->blist[jcoeff]; - + snadi[ncount] += dbxtmp; snadi[ncount+yoffset] += dbytmp; snadi[ncount+zoffset] += dbztmp; snadj[ncount] -= dbxtmp; snadj[ncount+yoffset] -= dbytmp; snadj[ncount+zoffset] -= dbztmp; - + ncount++; } - + } } - + } else { for (int icoeff = 0; icoeff < ncoeff; icoeff++) { // sign convention same as compute snad - + /* dgrad[dgrad_row_indx+0][icoeff] = -snaptr->dblist[icoeff][0]; dgrad[dgrad_row_indx+1][icoeff] = -snaptr->dblist[icoeff][1]; dgrad[dgrad_row_indx+2][icoeff] = -snaptr->dblist[icoeff][2]; @@ -514,9 +539,20 @@ void ComputeSnap::compute_array() dbiri[3*(atom->tag[i]-1)+0][icoeff] += snaptr->dblist[icoeff][0]; dbiri[3*(atom->tag[i]-1)+1][icoeff] += snaptr->dblist[icoeff][1]; dbiri[3*(atom->tag[i]-1)+2][icoeff] += snaptr->dblist[icoeff][2]; - + */ + + // add to snap array for this proc + // dBi/dRj + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; + // dBi/dRi + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; } + /* dgrad[dgrad_row_indx+0][ncoeff] = atom->tag[i]-1; dgrad[dgrad_row_indx+0][ncoeff+1] = atom->tag[j]-1; dgrad[dgrad_row_indx+0][ncoeff+2] = 0; @@ -530,20 +566,48 @@ void ComputeSnap::compute_array() dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; - + dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; - + dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]-1; dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; + */ + + // add to snap array for this proc + // dBi/dRj tags + /* + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[j]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[j]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[j]-1; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + */ + + // dBi/dRi tags + /* + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + */ } - } +} // loop over jj inside // accumulate Bi - + if (!dgradflag) { // linear contributions @@ -595,73 +659,6 @@ void ComputeSnap::compute_array() } else { - int irow = bik_rows; - for (int itype = 0; itype < 1; itype++) { - const int typeoffset_local = ndims_peratom * nperdim * itype; - const int typeoffset_global = nperdim * itype; - for (int i = 0; i < atom->nlocal; i++) { - - for (int jj = 0; jjtag[i]-1] + 3 * jj; - int snap_row_indx = 3 * neighsum[atom->tag[i]-1] + 3 * (atom->tag[i]-1) + 3 * jj; - // irow = snap_row_indx + bik_rows; - - // x-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+0][icoeff]; - snap[irow][0] += dgrad[dgrad_row_indx+0][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+0][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+0][ncoeff+2]; - irow++; - - // y-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+1][icoeff]; - snap[irow][0] += dgrad[dgrad_row_indx+1][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+1][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+1][ncoeff+2]; - irow++; - - // z-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dgrad[dgrad_row_indx+2][icoeff]; - snap[irow][0] += dgrad[dgrad_row_indx+2][ncoeff]; - snap[irow][0+1] += dgrad[dgrad_row_indx+2][ncoeff+1]; - snap[irow][0+2] += dgrad[dgrad_row_indx+2][ncoeff+2]; - irow++; - - } - - // Put dBi/dRi at end of each dBj/dRi chunk. - - // x-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dbiri[3*i+0][icoeff]; - snap[irow][0] += dbiri[3*i+0][ncoeff]; - snap[irow][0+1] += dbiri[3*i+0][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+0][ncoeff+2]; - irow++; - - // y-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dbiri[3*i+1][icoeff]; - snap[irow][0] += dbiri[3*i+1][ncoeff]; - snap[irow][0+1] += dbiri[3*i+1][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+1][ncoeff+2]; - irow++; - - // z-coordinate - for (int icoeff = 0; icoeff < nperdim; icoeff++) - snap[irow][icoeff+3] += dbiri[3*i+2][icoeff]; - snap[irow][0] += dbiri[3*i+2][ncoeff]; - snap[irow][0+1] += dbiri[3*i+2][ncoeff+1]; - snap[irow][0+2] += dbiri[3*i+2][ncoeff+2]; - irow++; - - } - } - } // accumulate forces to global array @@ -674,11 +671,11 @@ void ComputeSnap::compute_array() snap[irow++][lastcol] = atom->f[i][1]; snap[irow][lastcol] = atom->f[i][2]; } - + } else { - + // for dgradflag=1, put forces at first 3 columns of bik rows - + for (int i=0; inlocal; i++){ int iglobal = atom->tag[i]; snap[iglobal-1][0+0] = atom->f[i][0]; @@ -692,7 +689,7 @@ void ComputeSnap::compute_array() dbdotr_compute(); // sum up over all processes - + MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); // assign energy to last column @@ -707,7 +704,8 @@ void ComputeSnap::compute_array() // Assign reference energy right after the dgrad rows, first column. // Add 3N for the dBi/dRi rows. - int irow = bik_rows + dgrad_rows + 3*natoms; + //int irow = bik_rows + dgrad_rows + 3*natoms; + int irow = bik_rows + 3*natoms*natoms; double reference_energy = c_pe->compute_scalar(); snapall[irow][0] = reference_energy; } @@ -725,7 +723,7 @@ void ComputeSnap::compute_array() snapall[irow++][lastcol] = c_virial->vector[4]; snapall[irow][lastcol] = c_virial->vector[3]; } - + } /* ---------------------------------------------------------------------- @@ -739,7 +737,7 @@ void ComputeSnap::dbdotr_compute() // no virial terms for dgrad yet if (dgradflag) return; - + double **x = atom->x; int irow0 = bik_rows+ndims_force*natoms; @@ -775,7 +773,7 @@ void ComputeSnap::dbdotr_compute() void ComputeSnap::get_dgrad_length() { - int rank = universe->me; // for MPI debugging + rank = universe->me; // for MPI debugging memory->destroy(snap); memory->destroy(snapall); @@ -796,8 +794,8 @@ void ComputeSnap::get_dgrad_length() memory->create(nneighs, natoms, "snap:nneighs"); memory->create(icounter, natoms, "snap:icounter"); memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); - if (atom->nlocal != natoms) - error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); + //if (atom->nlocal != natoms) + // error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); for (int ii = 0; ii < 3 * natoms; ii++) for (int icoeff = 0; icoeff < ncoeff; icoeff++) @@ -834,21 +832,23 @@ void ComputeSnap::get_dgrad_length() dgrad_rows *= ndims_force; - neighsum[0] = 0; + neighsum[0] = 0; for (int ii = 1; ii < inum; ii++) { const int i = ilist[ii]; if (mask[i] & groupbit) neighsum[i] = neighsum[i-1] + nneighs[i-1]; } - + memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); for (int i = 0; i < dgrad_rows; i++) for (int j = 0; j < ncoeff+3; j++) dgrad[i][j] = 0.0; - + // set size array rows which now depends on dgrad_rows. - size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy + //size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy + size_array_rows = bik_rows + 3*natoms*natoms + 1; + //printf("----- dgrad_rows, 3*natoms*natoms: %d %d\n", dgrad_rows, 3*natoms*natoms); memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); @@ -932,18 +932,18 @@ void ComputeSnap::get_dgrad_length2() // } // } - neighsum[0] = 0; + neighsum[0] = 0; for (int ii = 1; ii < inum; ii++) { const int i = ilist[ii]; if (mask[i] & groupbit) neighsum[i] = neighsum[i-1] + nneighs[i-1]; } - + memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); for (int i = 0; i < dgrad_rows; i++) for (int j = 0; j < ncoeff+3; j++) dgrad[i][j] = 0.0; - + // set size array rows which now depends on dgrad_rows. size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index f106421635..e1fb7cee49 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -59,6 +59,7 @@ class ComputeSnap : public Compute { int *nneighs; // number of neighs inside the snap cutoff. int *neighsum; int *icounter; // counting atoms i for each j. + int rank; Compute *c_pe; Compute *c_virial; From e98af882422074770d696c0fa14e389da3ce6fa4 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 1 Jul 2022 20:56:11 -0600 Subject: [PATCH 42/75] Clean up unnecessary code. --- examples/snap/compute_snap_dgrad.py | 2 +- src/ML-SNAP/compute_snap.cpp | 447 ++++++---------------------- src/ML-SNAP/compute_snap.h | 9 +- 3 files changed, 95 insertions(+), 363 deletions(-) diff --git a/examples/snap/compute_snap_dgrad.py b/examples/snap/compute_snap_dgrad.py index 95f2e1fa55..0919af8598 100644 --- a/examples/snap/compute_snap_dgrad.py +++ b/examples/snap/compute_snap_dgrad.py @@ -6,7 +6,7 @@ Purpose: Demonstrate extraction of descriptor gradient (dB/dR) array from comput Serial syntax: python compute_snap_dgrad.py Parallel syntax: - mpirun -np 2 python compute_snap_dgrad.py + mpirun -np 4 python compute_snap_dgrad.py """ from __future__ import print_function diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 52c1b32fbc..25c5c488fa 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -208,7 +208,11 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (bikflag) bik_rows = natoms; dgrad_rows = ndims_force*natoms; size_array_rows = bik_rows+dgrad_rows + ndims_virial; - if (dgradflag) size_array_cols = nperdim + 3; + if (dgradflag){ + size_array_rows = bik_rows + 3*natoms*natoms + 1; + size_array_cols = nperdim + 3; + error->warning(FLERR,"dgradflag=1 creates a N^2 array, beware of large systems."); + } else size_array_cols = nperdim*atom->ntypes + 1; lastcol = size_array_cols-1; @@ -236,13 +240,7 @@ ComputeSnap::~ComputeSnap() memory->destroy(sinnerelem); memory->destroy(dinnerelem); } - if (dgradflag){ - memory->destroy(dgrad); - memory->destroy(nneighs); - memory->destroy(neighsum); - memory->destroy(icounter); - memory->destroy(dbiri); - } + } /* ---------------------------------------------------------------------- */ @@ -306,9 +304,6 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { - if (dgradflag) get_dgrad_length(); - // if (dgradflag) get_dgrad_length2(); - int ntotal = atom->nlocal + atom->nghost; invoked_array = update->ntimestep; @@ -448,195 +443,130 @@ void ComputeSnap::compute_array() for (int jj = 0; jj < ninside; jj++) { const int j = snaptr->inside[jj]; - if (dgradflag){ - dgrad_row_indx = 3*neighsum[atom->tag[j]-1] + 3*icounter[atom->tag[j]-1] ; - icounter[atom->tag[j]-1] += 1; - } - snaptr->compute_duidrj(jj); snaptr->compute_dbidrj(); // accumulate dBi/dRi, -dBi/dRj - if (!dgradflag) { + if (!dgradflag) { - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - } + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; - // diagonal elements of quadratic matrix + // diagonal elements of quadratic matrix - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; + ncount++; - // upper-triangular elements of quadratic matrix + // upper-triangular elements of quadratic matrix - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; - } + ncount++; + } + } + } + } else { - } + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - } + // add to snap array for this proc - } else { + // dBi/dRj - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - // sign convention same as compute snad - /* - dgrad[dgrad_row_indx+0][icoeff] = -snaptr->dblist[icoeff][0]; - dgrad[dgrad_row_indx+1][icoeff] = -snaptr->dblist[icoeff][1]; - dgrad[dgrad_row_indx+2][icoeff] = -snaptr->dblist[icoeff][2]; + // dBi/dRi - // accumulate dBi/dRi = sum (-dBi/dRj) for neighbors j of if i + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + } - dbiri[3*(atom->tag[i]-1)+0][icoeff] += snaptr->dblist[icoeff][0]; - dbiri[3*(atom->tag[i]-1)+1][icoeff] += snaptr->dblist[icoeff][1]; - dbiri[3*(atom->tag[i]-1)+2][icoeff] += snaptr->dblist[icoeff][2]; - */ + } - // add to snap array for this proc - // dBi/dRj - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - // dBi/dRi - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; - } - - /* - dgrad[dgrad_row_indx+0][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+0][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+0][ncoeff+2] = 0; - dgrad[dgrad_row_indx+1][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+1][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+1][ncoeff+2] = 1; - dgrad[dgrad_row_indx+2][ncoeff] = atom->tag[i]-1; - dgrad[dgrad_row_indx+2][ncoeff+1] = atom->tag[j]-1; - dgrad[dgrad_row_indx+2][ncoeff+2] = 2; - - dbiri[3*(atom->tag[i]-1)+0][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+0][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+0][ncoeff+2] = 0; - - dbiri[3*(atom->tag[i]-1)+1][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+1][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+1][ncoeff+2] = 1; - - dbiri[3*(atom->tag[i]-1)+2][ncoeff] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+2][ncoeff+1] = atom->tag[i]-1; - dbiri[3*(atom->tag[i]-1)+2][ncoeff+2] = 2; - */ - - // add to snap array for this proc - // dBi/dRj tags - /* - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[j]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[j]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[j]-1; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; - */ - - // dBi/dRi tags - /* - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; - */ - - } -} // loop over jj inside + } // loop over jj inside // accumulate Bi if (!dgradflag) { - // linear contributions + // linear contributions int k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - // quadratic contributions + // quadratic contributions - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } - } + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } } else { - int k = 3; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; - } - } - } + int k = 3; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + numneigh_sum += ninside; + } + + } // if (mask[i] & groupbit) + + } // for (int ii = 0; ii < inum; ii++) { // accumulate bispectrum force contributions to global array @@ -657,8 +587,6 @@ void ComputeSnap::compute_array() } } - } else { - } // accumulate forces to global array @@ -702,9 +630,8 @@ void ComputeSnap::compute_array() } else { - // Assign reference energy right after the dgrad rows, first column. - // Add 3N for the dBi/dRi rows. - //int irow = bik_rows + dgrad_rows + 3*natoms; + // assign reference energy right after the dgrad rows, first column + int irow = bik_rows + 3*natoms*natoms; double reference_energy = c_pe->compute_scalar(); snapall[irow][0] = reference_energy; @@ -766,194 +693,6 @@ void ComputeSnap::dbdotr_compute() } } -/* ---------------------------------------------------------------------- - compute dgrad length -------------------------------------------------------------------------- */ - -void ComputeSnap::get_dgrad_length() -{ - - rank = universe->me; // for MPI debugging - - memory->destroy(snap); - memory->destroy(snapall); - - // invoke full neighbor list - - neighbor->build_one(list); - dgrad_rows = 0; - const int inum = list->inum; - const int* const ilist = list->ilist; - const int* const numneigh = list->numneigh; - int** const firstneigh = list->firstneigh; - int * const type = atom->type; - const int* const mask = atom->mask; - double** const x = atom->x; - - memory->create(neighsum, natoms, "snap:neighsum"); - memory->create(nneighs, natoms, "snap:nneighs"); - memory->create(icounter, natoms, "snap:icounter"); - memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); - //if (atom->nlocal != natoms) - // error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); - - for (int ii = 0; ii < 3 * natoms; ii++) - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - dbiri[ii][icoeff] = 0.0; - - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - icounter[i] = 0; - nneighs[i] = 0; - const double xtmp = x[i][0]; - const double ytmp = x[i][1]; - const double ztmp = x[i][2]; - const int itype = type[i]; - const int* const jlist = firstneigh[i]; - const int jnum = numneigh[i]; - for (int jj = 0; jj < jnum; jj++) { - int j = jlist[jj]; - j &= NEIGHMASK; - - const double delx = x[j][0] - xtmp; - const double dely = x[j][1] - ytmp; - const double delz = x[j][2] - ztmp; - const double rsq = delx * delx + dely * dely + delz * delz; - int jtype = type[j]; - - if (rsq < cutsq[itype][jtype] && rsq>1e-20) { - dgrad_rows++; - nneighs[i]++; - } - } - } - } - - dgrad_rows *= ndims_force; - - neighsum[0] = 0; - for (int ii = 1; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) - neighsum[i] = neighsum[i-1] + nneighs[i-1]; - } - - memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); - for (int i = 0; i < dgrad_rows; i++) - for (int j = 0; j < ncoeff+3; j++) - dgrad[i][j] = 0.0; - - // set size array rows which now depends on dgrad_rows. - - //size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy - size_array_rows = bik_rows + 3*natoms*natoms + 1; - //printf("----- dgrad_rows, 3*natoms*natoms: %d %d\n", dgrad_rows, 3*natoms*natoms); - - memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); - array = snapall; - -} - -/* ---------------------------------------------------------------------- - compute dgrad length -------------------------------------------------------------------------- */ - -void ComputeSnap::get_dgrad_length2() -{ - memory->destroy(snap); - memory->destroy(snapall); - - // invoke full neighbor list - - neighbor->build_one(list); - dgrad_rows = 0; - const int inum = list->inum; - const int* const ilist = list->ilist; - const int* const numneigh = list->numneigh; - int** const firstneigh = list->firstneigh; - int * const type = atom->type; - const int* const mask = atom->mask; - double** const x = atom->x; - - memory->create(neighsum, natoms, "snap:neighsum"); - memory->create(nneighs, natoms, "snap:nneighs"); - memory->create(icounter, natoms, "snap:icounter"); - memory->create(dbiri, 3*natoms,ncoeff+3, "snap:dbiri"); - if (atom->nlocal != natoms) - error->all(FLERR,"Compute snap dgradflag=1 does not support parallelism yet."); - - for (int ii = 0; ii < 3 * natoms; ii++) - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - dbiri[ii][icoeff] = 0.0; - - for (int ii = 0; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) { - icounter[i] = 0; - nneighs[i] = 0; - const double xtmp = x[i][0]; - const double ytmp = x[i][1]; - const double ztmp = x[i][2]; - const int itype = type[i]; - const int* const jlist = firstneigh[i]; - const int jnum = numneigh[i]; - for (int jj = 0; jj < jnum; jj++) { - int j = jlist[jj]; - j &= NEIGHMASK; - - const double delx = x[j][0] - xtmp; - const double dely = x[j][1] - ytmp; - const double delz = x[j][2] - ztmp; - const double rsq = delx * delx + dely * dely + delz * delz; - int jtype = type[j]; - - if (rsq < cutsq[itype][jtype] && rsq>1e-20) { - dgrad_rows++; - nneighs[i]++; - } - } - } - } - - dgrad_rows *= ndims_force; - - // loop over all atoms again to calculate neighsum - - // for (int ii = 0; ii < inum; ii++) { - // const int i = ilist[ii]; - // if (mask[i] & groupbit) { - // for (int jj = 0; jj < ii; jj++) { - // const int j = ilist[jj]; - // if (mask[j] & groupbit) - // neighsum[i] += nneighs[j]; - // } - // } - // } - - neighsum[0] = 0; - for (int ii = 1; ii < inum; ii++) { - const int i = ilist[ii]; - if (mask[i] & groupbit) - neighsum[i] = neighsum[i-1] + nneighs[i-1]; - } - - memory->create(dgrad, dgrad_rows, ncoeff+3, "snap:dgrad"); - for (int i = 0; i < dgrad_rows; i++) - for (int j = 0; j < ncoeff+3; j++) - dgrad[i][j] = 0.0; - - // set size array rows which now depends on dgrad_rows. - - size_array_rows = bik_rows + dgrad_rows + 3*atom->nlocal + 1; // Add 3*N for dBi/dRi. and add 1 for reference energy - - memory->create(snap,size_array_rows,size_array_cols, "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, "snap:snapall"); - array = snapall; - -} - /* ---------------------------------------------------------------------- memory usage ------------------------------------------------------------------------- */ diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index e1fb7cee49..a89a870fe8 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -53,20 +53,13 @@ class ComputeSnap : public Compute { double cutmax; int quadraticflag; int bikflag, bik_rows, dgradflag, dgrad_rows; - double **dgrad; // First ncoeff columns are descriptor derivatives. - // Last 3 columns are indices i,j,a - double **dbiri; // dBi/dRi = sum(-dBi/dRj) over neighbors j - int *nneighs; // number of neighs inside the snap cutoff. - int *neighsum; - int *icounter; // counting atoms i for each j. int rank; Compute *c_pe; Compute *c_virial; void dbdotr_compute(); - void get_dgrad_length(); - void get_dgrad_length2(); + }; } // namespace LAMMPS_NS From 581503888f776895356dd08026ab6f0eb1550aa7 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 1 Jul 2022 21:07:19 -0600 Subject: [PATCH 43/75] Updated docs. --- doc/src/compute_sna_atom.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index e2655585f3..37705ff005 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -464,15 +464,8 @@ gradient components where :math:`a` is the Cartesian direction for the gradient. The rows are organized in chunks, where each chunk corresponds to an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to -atoms :math:`i` that have :math:`j` as a neighbor. The number of rows in the -atom :math:`j` chunk is therefore equal to the number of neighbors -:math:`N_{neighs}[j]` within the SNAP potential cutoff radius of atom :math:`j` -, times 3 for each Cartesian direction. The total number of rows for these -descriptor gradients is therefore - -.. math:: - - 3 \sum_j^{N} N_{neighs}[j]. +atoms :math:`i` in the system of :math:`N` atoms. The total number of rows for +these descriptor gradients is therefore :math:`3N^2`. For *dgradflag=1*, the number of columns is equal to the number of bispectrum components, plus 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a` which @@ -483,8 +476,9 @@ For the descriptor gradient rows, the first 3 columns contain the indices of different descriptors indexed by :math:`k`. The first 3 columns of the first :math:`N` rows belong to the reference potential force components. The first column of the last row, after the first -:math:`N + 3 \sum_j^{N} N_{neighs}[j]` rows, contains the reference potential -energy. The virial components are not used with this option. +:math:`N + 3N^2` rows, contains the reference potential +energy. The virial components are not used with this option. The total number of +rows is therefore :math:`N + 3N^2 + 1` and the number of columns is :math:`K + 3`. These values can be accessed by any command that uses per-atom values from a compute as input. See the :doc:`Howto output ` doc From 39b01a901f24e289ad08ab9dfe3867048ace3518 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 11:23:59 -0400 Subject: [PATCH 44/75] print warning when using I/O redirection with parallel runs --- src/lammps.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lammps.cpp b/src/lammps.cpp index c74983be43..4ddbe42db3 100644 --- a/src/lammps.cpp +++ b/src/lammps.cpp @@ -446,8 +446,7 @@ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) : // sum of procs in all worlds must equal total # of procs if (!universe->consistent()) - error->universe_all(FLERR,"Processor partitions do not match " - "number of allocated processors"); + error->universe_all(FLERR,"Processor partitions do not match number of allocated processors"); // universe cannot use stdin for input file @@ -516,10 +515,15 @@ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) : else infile = fopen(arg[inflag],"r"); if (infile == nullptr) error->one(FLERR,"Cannot open input script {}: {}", arg[inflag], utils::getsyserror()); + if (!helpflag) + utils::logmesg(this,fmt::format("LAMMPS ({}{})\n",version,UPDATE_STRING)); + // warn against using I/O redirection in parallel runs + if ((inflag == 0) && (universe->nprocs > 1)) + error->warning(FLERR, "Using I/O redirection is unreliable with parallel runs. " + "Better use -in switch to read input file."); + utils::flush_buffers(this); } - if ((universe->me == 0) && !helpflag) - utils::logmesg(this,fmt::format("LAMMPS ({}{})\n",version,UPDATE_STRING)); // universe is one or more worlds, as setup by partition switch // split universe communicator into separate world communicators From f38a417a32d658c228ef50d23db68307c6e4d80e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 17:08:22 -0400 Subject: [PATCH 45/75] restore user facing keyword back to "threebody" defaulting to "on" also some minor updates: - streamline description in the documentation, add links/references - print message when disabling threebody terms - improve error messages, simplify argument processing --- doc/src/pair_sw.rst | 91 ++++++++++++++++------------ doc/src/pair_sw_angle_table.rst | 3 +- src/MANYBODY/pair_sw.cpp | 29 ++++----- src/MANYBODY/pair_sw_angle_table.cpp | 4 +- src/MANYBODY/pair_sw_mod.cpp | 11 ++-- 5 files changed, 75 insertions(+), 63 deletions(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index a83f446a07..b2305dab5e 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -24,17 +24,16 @@ Syntax pair_style style keyword values * style = *sw* or *sw/mod* -* keyword = *maxdelcs* or *skip_threebody* +* keyword = *maxdelcs* or *threebody* .. parsed-literal:: - *maxdelcs* value = delta1 delta2 (optional) + *maxdelcs* value = delta1 delta2 (optional, sw/mod only) delta1 = The minimum thershold for the variation of cosine of three-body angle delta2 = The maximum threshold for the variation of cosine of three-body angle - *skip_threebody* value = *on* or *off* (optional) - off (default) = Compute both the three-body and two-body terms of the potential - on = Compute only the two-body term of the potential - + *threebody* value = *on* or *off* (optional, sw only) + on (default) = Compute both the three-body and two-body terms of the potential + off = Compute only the two-body term of the potential Examples """""""" @@ -48,7 +47,7 @@ Examples pair_style sw/mod maxdelcs 0.25 0.35 pair_coeff * * tmd.sw.mod Mo S S - pair_style hybrid sw sw skip_threebody on + pair_style hybrid sw threebody on sw threebody off pair_coeff * * sw 1 mW_xL.sw mW NULL pair_coeff 1 2 sw 2 mW_xL.sw mW xL pair_coeff 2 2 sw 2 mW_xL.sw mW xL @@ -77,22 +76,25 @@ three-body term. The summations in the formula are over all neighbors J and K of atom I within a cutoff distance :math:`a `\sigma`. The *sw/mod* style is designed for simulations of materials when -distinguishing three-body angles are necessary, such as borophene -and transition metal dichalcogenides, which cannot be described -by the original code for the Stillinger-Weber potential. -For instance, there are several types of angles around each Mo atom in `MoS_2`, -and some unnecessary angle types should be excluded in the three-body interaction. -Such exclusion may be realized by selecting proper angle types directly. -The exclusion of unnecessary angles is achieved here by the cut-off function (`f_C(\delta)`), -which induces only minimum modifications for LAMMPS. +distinguishing three-body angles are necessary, such as borophene and +transition metal dichalcogenides, which cannot be described by the +original code for the Stillinger-Weber potential. For instance, there +are several types of angles around each Mo atom in `MoS_2`, and some +unnecessary angle types should be excluded in the three-body +interaction. Such exclusion may be realized by selecting proper angle +types directly. The exclusion of unnecessary angles is achieved here by +the cut-off function (`f_C(\delta)`), which induces only minimum +modifications for LAMMPS. Validation, benchmark tests, and applications of the *sw/mod* style can be found in :ref:`(Jiang2) ` and :ref:`(Jiang3) `. -The *sw/mod* style computes the energy E of a system of atoms, whose potential -function is mostly the same as the Stillinger-Weber potential. The only modification -is in the three-body term, where the value of :math:`\delta = \cos \theta_{ijk} - \cos \theta_{0ijk}` -used in the original energy and force expression is scaled by a switching factor :math:`f_C(\delta)`: +The *sw/mod* style computes the energy E of a system of atoms, whose +potential function is mostly the same as the Stillinger-Weber +potential. The only modification is in the three-body term, where the +value of :math:`\delta = \cos \theta_{ijk} - \cos \theta_{0ijk}` used in +the original energy and force expression is scaled by a switching factor +:math:`f_C(\delta)`: .. math:: @@ -103,28 +105,38 @@ used in the original energy and force expression is scaled by a switching factor 0 & \left| \delta \right| > \delta_2 \end{array} \right. \\ -This cut-off function decreases smoothly from 1 to 0 over the range :math:`[\delta_1, \delta_2]`. -This smoothly turns off the energy and force contributions for :math:`\left| \delta \right| > \delta_2`. -It is suggested that :math:`\delta 1` and :math:`\delta_2` to be the value around -:math:`0.5 \left| \cos \theta_1 - \cos \theta_2 \right|`, with -:math:`\theta_1` and :math:`\theta_2` as the different types of angles around an atom. -For borophene and transition metal dichalcogenides, :math:`\delta_1 = 0.25` and :math:`\delta_2 = 0.35`. -This value enables the cut-off function to exclude unnecessary angles in the three-body SW terms. +This cut-off function decreases smoothly from 1 to 0 over the range +:math:`[\delta_1, \delta_2]`. This smoothly turns off the energy and +force contributions for :math:`\left| \delta \right| > \delta_2`. It is +suggested that :math:`\delta 1` and :math:`\delta_2` to be the value +around :math:`0.5 \left| \cos \theta_1 - \cos \theta_2 \right|`, with +:math:`\theta_1` and :math:`\theta_2` as the different types of angles +around an atom. For borophene and transition metal dichalcogenides, +:math:`\delta_1 = 0.25` and :math:`\delta_2 = 0.35`. This value enables +the cut-off function to exclude unnecessary angles in the three-body SW +terms. .. note:: - The cut-off function is just to be used as a technique to exclude some unnecessary angles, - and it has no physical meaning. It should be noted that the force and potential are inconsistent - with each other in the decaying range of the cut-off function, as the angle dependence for the - cut-off function is not implemented in the force (first derivation of potential). - However, the angle variation is much smaller than the given threshold value for actual simulations, - so the inconsistency between potential and force can be neglected in actual simulations. + The cut-off function is just to be used as a technique to exclude + some unnecessary angles, and it has no physical meaning. It should be + noted that the force and potential are inconsistent with each other + in the decaying range of the cut-off function, as the angle + dependence for the cut-off function is not implemented in the force + (first derivation of potential). However, the angle variation is + much smaller than the given threshold value for actual simulations, + so the inconsistency between potential and force can be neglected in + actual simulations. -The *skip_threebody* keyword determines whether or not the three-body term of the potential is calculated. -Skipping this significantly increases the speed of the calculation, with the tradeoff that :math:\lambda_{ijk} -is forcibly set to 0. If the keyword is used with the pair styles, sw/gpu, sw/intel, or sw/kokkos, -:math:\lambda_{ijk} will still be forcibly set to 0, but no additional benefits will be gained. This keyword -cannot be used for variants of the sw/mod or sw/angle/table pair styles. +The *threebody* keyword is optional and determines whether or not the +three-body term of the potential is calculated. The default value is +"on" and it is only available for the plain *sw* pair style variants, +but not available for the *sw/mod* and :doc:`sw/angle/table +` pair style variants. To turn off the threebody +contributions all :math:`\lambda_{ijk}` parameters from the potential +file are forcibly set to 0. In addition the pair style implementations +may employ code optimizations for the *threebody off* setting that can +result in significant speedups versus the default. Only a single pair_coeff command is used with the *sw* and *sw/mod* styles which specifies a Stillinger-Weber potential file with parameters for all @@ -297,8 +309,9 @@ Related commands Default """"""" -The default values for the *maxdelcs* setting of the *sw/mod* pair -style are *delta1* = 0.25 and *delta2* = 0.35`. +The default value for the *threebody* setting of the "sw" pair style is +"on", the default values for the "*maxdelcs* setting of the *sw/mod* +pair style are *delta1* = 0.25 and *delta2* = 0.35`. ---------- diff --git a/doc/src/pair_sw_angle_table.rst b/doc/src/pair_sw_angle_table.rst index ff88711d7a..65e73d4445 100644 --- a/doc/src/pair_sw_angle_table.rst +++ b/doc/src/pair_sw_angle_table.rst @@ -296,7 +296,8 @@ for pair interactions. Related commands """""""""""""""" -:doc:`pair_coeff `, :doc:`pair_style sw `, :doc:`pair_style threebody/table ` +:doc:`pair_coeff `, :doc:`pair_style sw `, +:doc:`pair_style threebody/table ` ---------- diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 3cc402cb5f..b615480754 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -44,6 +44,7 @@ PairSW::PairSW(LAMMPS *lmp) : Pair(lmp) manybody_flag = 1; centroidstressflag = CENTROID_NOTAVAIL; unit_convert_flag = utils::get_supported_conversions(utils::ENERGY); + skip_threebody_flag = false; params = nullptr; @@ -172,24 +173,24 @@ void PairSW::compute(int eflag, int vflag) delr1[1] = x[j][1] - ytmp; delr1[2] = x[j][2] - ztmp; rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - + double fjxtmp,fjytmp,fjztmp; fjxtmp = fjytmp = fjztmp = 0.0; - + for (kk = jj+1; kk < numshort; kk++) { k = neighshort[kk]; ktype = map[type[k]]; ikparam = elem3param[itype][ktype][ktype]; ijkparam = elem3param[itype][jtype][ktype]; - + delr2[0] = x[k][0] - xtmp; delr2[1] = x[k][1] - ytmp; delr2[2] = x[k][2] - ztmp; rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], rsq1,rsq2,delr1,delr2,fj,fk,eflag,evdwl); - + fxtmp -= fj[0] + fk[0]; fytmp -= fj[1] + fk[1]; fztmp -= fj[2] + fk[2]; @@ -199,7 +200,7 @@ void PairSW::compute(int eflag, int vflag) f[k][0] += fk[0]; f[k][1] += fk[1]; f[k][2] += fk[2]; - + if (evflag) ev_tally3(i,j,k,evdwl,0.0,fj,fk,delr1,delr2); } f[j][0] += fjxtmp; @@ -233,18 +234,15 @@ void PairSW::allocate() void PairSW::settings(int narg, char ** arg) { - // Default - skip_threebody_flag = false; - + // process optional keywords int iarg = 0; - while (iarg < narg) { - if (strcmp(arg[iarg],"skip_threebody") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal pair_style command"); - skip_threebody_flag = utils::logical(FLERR,arg[iarg+1],false,lmp); + if (strcmp(arg[iarg],"threebody") == 0) { + if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "pair_style sw", error); + skip_threebody_flag = !utils::logical(FLERR,arg[iarg+1],false,lmp); one_coeff = !skip_threebody_flag; iarg += 2; - } else error->all(FLERR,"Illegal pair_style command"); + } else error->all(FLERR, "Illegal pair_style sw keyword: {}", arg[iarg]); } } @@ -305,6 +303,8 @@ void PairSW::read_file(char *file) PotentialFileReader reader(lmp, file, "sw", unit_convert_flag); char *line; + if (skip_threebody_flag) utils::logmesg(lmp, " disabling sw potential three-body terms\n"); + // transparently convert units for supported conversions int unit_convert = reader.get_unit_convert(); @@ -369,6 +369,7 @@ void PairSW::read_file(char *file) params[nparams].epsilon *= conversion_factor; } + // turn off three-body term if (skip_threebody_flag) params[nparams].lambda = 0; if (params[nparams].epsilon < 0.0 || params[nparams].sigma < 0.0 || diff --git a/src/MANYBODY/pair_sw_angle_table.cpp b/src/MANYBODY/pair_sw_angle_table.cpp index 7667f77491..5b9339780f 100644 --- a/src/MANYBODY/pair_sw_angle_table.cpp +++ b/src/MANYBODY/pair_sw_angle_table.cpp @@ -759,5 +759,5 @@ void PairSWAngleTable::uf_lookup(ParamTable *pm, double x, double &u, double &f) void PairSWAngleTable::settings(int narg, char **/*arg*/) { - if (narg != 0) error->all(FLERR,"Illegal pair_style command"); -} \ No newline at end of file + if (narg != 0) error->all(FLERR,"Illegal pair_style sw/angle/table command"); +} diff --git a/src/MANYBODY/pair_sw_mod.cpp b/src/MANYBODY/pair_sw_mod.cpp index ce24952fc7..e6d17b0733 100644 --- a/src/MANYBODY/pair_sw_mod.cpp +++ b/src/MANYBODY/pair_sw_mod.cpp @@ -41,21 +41,18 @@ PairSWMOD::PairSWMOD(LAMMPS *lmp) : PairSW(lmp) void PairSWMOD::settings(int narg, char **arg) { - // process optional keywords - + // process optional keywords (and not (yet) optional keywords from parent class). int iarg = 0; - while (iarg < narg) { if (strcmp(arg[iarg],"maxdelcs") == 0) { - if (iarg+3 > narg) error->all(FLERR,"Illegal pair_style command"); + if (iarg+3 > narg) utils::missing_cmd_args(FLERR, "pair_style sw/mod", error); delta1 = utils::numeric(FLERR,arg[iarg+1],false,lmp); delta2 = utils::numeric(FLERR,arg[iarg+2],false,lmp); iarg += 3; if ((delta1 < 0.0) || (delta1 > 1.0) || (delta2 < 0.0) || (delta2 > 1.0) || (delta1 > delta2)) - error->all(FLERR,"Illegal values for maxdelcs keyword"); - } else error->all(FLERR,"Illegal pair_style command"); + error->all(FLERR, "Out of range value(s) for pair style sw/mod maxdelcs keyword"); + } else error->all(FLERR, "Illegal pair_style sw/mod keyword: {}", arg[iarg]); } - PairSW::settings(narg-iarg,arg+iarg); } /* ---------------------------------------------------------------------- */ From 2fa5f7c97e66e1a48ffbb0d53a05542f03a56b8b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 17:39:01 -0400 Subject: [PATCH 46/75] use a half neighbor list for skip_threebody case for further speedup --- src/INTEL/pair_sw_intel.cpp | 4 ++++ src/KOKKOS/pair_sw_kokkos.cpp | 4 ++++ src/MANYBODY/pair_sw.cpp | 28 ++++++++++++++++++---------- src/OPENMP/pair_sw_omp.cpp | 30 ++++++++++++++++-------------- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/INTEL/pair_sw_intel.cpp b/src/INTEL/pair_sw_intel.cpp index a882f8bfba..37fe19260a 100644 --- a/src/INTEL/pair_sw_intel.cpp +++ b/src/INTEL/pair_sw_intel.cpp @@ -1101,7 +1101,11 @@ void PairSWIntel::allocate() void PairSWIntel::init_style() { + // there is no support for skipping threebody loops (yet) + bool tmp_threebody = skip_threebody_flag; + skip_threebody_flag = false; PairSW::init_style(); + skip_threebody_flag = tmp_threebody; map[0] = map[1]; diff --git a/src/KOKKOS/pair_sw_kokkos.cpp b/src/KOKKOS/pair_sw_kokkos.cpp index cae0aea0e8..a3d8d11380 100644 --- a/src/KOKKOS/pair_sw_kokkos.cpp +++ b/src/KOKKOS/pair_sw_kokkos.cpp @@ -392,7 +392,11 @@ void PairSWKokkos::coeff(int narg, char **arg) template void PairSWKokkos::init_style() { + // there is no support for skipping threebody loops (yet) + bool tmp_threebody = skip_threebody_flag; + skip_threebody_flag = false; PairSW::init_style(); + skip_threebody_flag = tmp_threebody; // adjust neighbor list request for KOKKOS diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index b615480754..034dcfaf10 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -14,6 +14,7 @@ /* ---------------------------------------------------------------------- Contributing author: Aidan Thompson (SNL) + Optimizations for two-body only: Jackson Elowitt (Univ. of Utah) ------------------------------------------------------------------------- */ #include "pair_sw.h" @@ -138,14 +139,18 @@ void PairSW::compute(int eflag, int vflag) } jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j][2] < ztmp) continue; - if (x[j][2] == ztmp && x[j][1] < ytmp) continue; - if (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp) continue; + + // only need to skip if we have a full neighbor list + if (!skip_threebody_flag) { + if (itag > jtag) { + if ((itag+jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j][2] < ztmp) continue; + if (x[j][2] == ztmp && x[j][1] < ytmp) continue; + if (x[j][2] == ztmp && x[j][1] == ytmp && x[j][0] < xtmp) continue; + } } twobody(¶ms[ijparam],rsq,fpair,eflag,evdwl); @@ -273,9 +278,12 @@ void PairSW::init_style() if (force->newton_pair == 0) error->all(FLERR,"Pair style Stillinger-Weber requires newton pair on"); - // need a full neighbor list + // need a full neighbor list for full threebody calculation - neighbor->add_request(this, NeighConst::REQ_FULL); + if (skip_threebody_flag) + neighbor->add_request(this); + else + neighbor->add_request(this, NeighConst::REQ_FULL); } /* ---------------------------------------------------------------------- diff --git a/src/OPENMP/pair_sw_omp.cpp b/src/OPENMP/pair_sw_omp.cpp index a59922b8c4..4f579a9e90 100644 --- a/src/OPENMP/pair_sw_omp.cpp +++ b/src/OPENMP/pair_sw_omp.cpp @@ -134,14 +134,16 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) } jtag = tag[j]; - if (itag > jtag) { - if ((itag+jtag) % 2 == 0) continue; - } else if (itag < jtag) { - if ((itag+jtag) % 2 == 1) continue; - } else { - if (x[j].z < ztmp) continue; - if (x[j].z == ztmp && x[j].y < ytmp) continue; - if (x[j].z == ztmp && x[j].y == ytmp && x[j].x < xtmp) continue; + if (!skip_threebody_flag) { + if (itag > jtag) { + if ((itag+jtag) % 2 == 0) continue; + } else if (itag < jtag) { + if ((itag+jtag) % 2 == 1) continue; + } else { + if (x[j].z < ztmp) continue; + if (x[j].z == ztmp && x[j].y < ytmp) continue; + if (x[j].z == ztmp && x[j].y == ytmp && x[j].x < xtmp) continue; + } } twobody(¶ms[ijparam],rsq,fpair,EFLAG,evdwl); @@ -169,24 +171,24 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) delr1[1] = x[j].y - ytmp; delr1[2] = x[j].z - ztmp; rsq1 = delr1[0]*delr1[0] + delr1[1]*delr1[1] + delr1[2]*delr1[2]; - + double fjxtmp,fjytmp,fjztmp; fjxtmp = fjytmp = fjztmp = 0.0; - + for (kk = jj+1; kk < numshort; kk++) { k = neighshort_thr[kk]; ktype = map[type[k]]; ikparam = elem3param[itype][ktype][ktype]; ijkparam = elem3param[itype][jtype][ktype]; - + delr2[0] = x[k].x - xtmp; delr2[1] = x[k].y - ytmp; delr2[2] = x[k].z - ztmp; rsq2 = delr2[0]*delr2[0] + delr2[1]*delr2[1] + delr2[2]*delr2[2]; - + threebody(¶ms[ijparam],¶ms[ikparam],¶ms[ijkparam], rsq1,rsq2,delr1,delr2,fj,fk,EFLAG,evdwl); - + fxtmp -= fj[0] + fk[0]; fytmp -= fj[1] + fk[1]; fztmp -= fj[2] + fk[2]; @@ -196,7 +198,7 @@ void PairSWOMP::eval(int iifrom, int iito, ThrData * const thr) f[k].x += fk[0]; f[k].y += fk[1]; f[k].z += fk[2]; - + if (EVFLAG) ev_tally3_thr(this,i,j,k,evdwl,0.0,fj,fk,delr1,delr2,thr); } f[j].x += fjxtmp; From 67a3a7a6c1efd594a4700436bb9b48393f78e8c8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 18:02:15 -0400 Subject: [PATCH 47/75] add unit test for twobody off and implement and test single() function --- src/MANYBODY/pair_sw.cpp | 18 +- src/MANYBODY/pair_sw.h | 1 + .../tests/manybody-pair-sw_twobody.yaml | 157 ++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 unittest/force-styles/tests/manybody-pair-sw_twobody.yaml diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 034dcfaf10..de2a7ac8d6 100644 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -245,7 +245,10 @@ void PairSW::settings(int narg, char ** arg) if (strcmp(arg[iarg],"threebody") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "pair_style sw", error); skip_threebody_flag = !utils::logical(FLERR,arg[iarg+1],false,lmp); - one_coeff = !skip_threebody_flag; + // without the threebody terms we don't need to enforce + // pair_coeff * * and can enable the single function. + one_coeff = skip_threebody_flag ? 0 : 1; + single_enable = skip_threebody_flag ? 1 : 0; iarg += 2; } else error->all(FLERR, "Illegal pair_style sw keyword: {}", arg[iarg]); } @@ -299,6 +302,19 @@ double PairSW::init_one(int i, int j) /* ---------------------------------------------------------------------- */ +double PairSW::single(int /*i*/, int /*j*/, int itype, int jtype, double rsq, + double /*factor_coul*/, double /*factor_lj*/, double &fforce) +{ + int ijparam = elem3param[map[itype]][map[jtype]][map[jtype]]; + double phisw = 0.0; + fforce = 0.0; + + if (rsq < params[ijparam].cutsq) twobody(¶ms[ijparam],rsq,fforce,1,phisw); + return phisw; +} + +/* ---------------------------------------------------------------------- */ + void PairSW::read_file(char *file) { memory->sfree(params); diff --git a/src/MANYBODY/pair_sw.h b/src/MANYBODY/pair_sw.h index aa0aef228e..bcc7227554 100644 --- a/src/MANYBODY/pair_sw.h +++ b/src/MANYBODY/pair_sw.h @@ -32,6 +32,7 @@ class PairSW : public Pair { void coeff(int, char **) override; double init_one(int, int) override; void init_style() override; + double single(int, int, int, int, double, double, double, double &) override; static constexpr int NPARAMS_PER_LINE = 14; diff --git a/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml b/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml new file mode 100644 index 0000000000..42d3cebf81 --- /dev/null +++ b/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml @@ -0,0 +1,157 @@ +--- +lammps_version: 23 Jun 2022 +tags: generated +date_generated: Sat Jul 2 17:42:21 2022 +epsilon: 1e-10 +skip_tests: +prerequisites: ! | + pair sw +pre_commands: ! | + variable newton_pair delete + if "$(is_active(package,gpu)) > 0.0" then "variable newton_pair index off" else "variable newton_pair index on" +post_commands: ! "" +input_file: in.manybody +pair_style: sw threebody off +pair_coeff: ! | + * * Si.sw Si Si Si Si Si Si Si Si +extract: ! "" +natoms: 64 +init_vdwl: -258.5086400674769 +init_coul: 0 +init_stress: ! |2- + 6.4784557236140188e+00 8.9666141338671075e+00 1.6564010213468620e+01 -4.9679217055624925e+00 3.4388959220521961e+01 7.5389343797929154e-01 +init_forces: ! |2 + 1 -7.5117950925001264e-01 3.0512035938320858e+00 1.7548369060319577e+00 + 2 -2.5839943840157944e+00 -6.2407030855132184e-01 -2.0776063043681416e+00 + 3 6.4874626526429646e-01 -1.9191097130296589e-01 -2.8953907507008203e-01 + 4 -1.8780621641443120e+00 2.9563493639001268e+00 6.0045000947756955e-01 + 5 -1.3527836952861758e+00 -7.5563316408513048e-01 -4.0171785277050770e-01 + 6 3.0261027539317009e-01 3.3010495375946016e+00 1.0578432546004755e+00 + 7 -1.0220792530722718e+00 -8.9704171544868350e-01 2.2679286915120240e+00 + 8 7.3786854090375081e-02 6.8184832716858157e-01 -9.9742395408331985e-01 + 9 1.5404185888118715e-01 -2.4234656391168983e+00 -2.5547421021459220e+00 + 10 2.9152006680754144e-01 -1.3832499077045937e+00 -2.3112314100238849e+00 + 11 3.0397941651131566e+00 -3.2696818086952382e+00 2.0402858500342536e+00 + 12 -5.2427750818963892e+00 -2.9266181850189747e+00 -2.3274218711162238e+00 + 13 -3.4764983756087076e-01 5.1759636691465873e+00 -9.5777981490571462e-01 + 14 -1.3994974411693033e+00 3.6723581318644940e+00 4.9022891744156372e-01 + 15 -3.6451950102391031e+00 4.1804831124641382e+00 -2.3094319497556559e+00 + 16 1.7762576801244847e+00 -1.7769734947711013e-01 5.6118226682874788e+00 + 17 1.1626276987569719e+00 2.5434318242406255e+00 -4.1298909446437833e+00 + 18 2.6370967308167081e-01 4.8174395544262438e-01 3.2879300848711184e+00 + 19 -1.2180668170296169e+00 -2.0218982136836656e+00 -3.8444674827692227e-01 + 20 -6.1734234441618661e+00 -8.7047552939678141e-02 7.4088977580926274e-01 + 21 2.2527604735738231e+00 9.2512650808085084e-01 -2.7596546519202407e+00 + 22 -5.0794028512678571e+00 3.2609137644938517e+00 -3.9745643191135742e+00 + 23 1.8924999787954402e+00 3.3526647080652703e+00 1.2248568956854238e+00 + 24 1.5743771798508372e+00 1.3293691279058384e+00 2.6631027667815861e+00 + 25 8.3110919566309338e-01 -1.1460141214066610e-01 1.4368370341871295e+00 + 26 -4.7991049590854340e-01 -6.7362488036409351e-01 1.2327946774343894e+00 + 27 1.9900573442863836e+00 -5.0620688348406084e-01 1.5762361423313080e+00 + 28 5.7701286079414915e-01 1.3187742370100088e+00 4.1298244042734957e+00 + 29 -3.0416482921788530e+00 9.1958118398971411e-01 -1.1418981151511567e+00 + 30 -1.5986340571260498e+00 1.2172058599377520e+00 -8.9839567436920520e-01 + 31 -1.6221367664137194e+00 1.4053388522714974e+00 -4.1030835758060596e-01 + 32 -3.3999213065003993e+00 5.4646623792746152e-01 -4.9970596852221305e-01 + 33 4.3374523080961502e+00 -2.0526192390847231e+00 2.7863621822774527e+00 + 34 3.6899966300377274e-01 -9.7647298273011718e-02 3.7849094767735275e-01 + 35 1.2474956459695217e+00 -1.6786310914928160e-01 2.5255688468039841e+00 + 36 1.9423002050777016e-02 -2.5811032787101440e+00 -5.3464409483959238e-02 + 37 -1.7991755427583054e+00 1.7326288527074167e+00 -2.3172591544605829e+00 + 38 -2.6635345675769728e-01 -4.5619472453099841e-01 2.9146619578940203e-01 + 39 2.2040425723147274e+00 -1.2990665525340543e+00 -4.1031233229148558e+00 + 40 3.8636879669801210e+00 -1.9428932057790562e+00 -1.2090029697953577e+00 + 41 6.9184862237524347e-02 -5.5563025877752470e-01 -1.0104421056432380e+00 + 42 -4.1077204842477482e+00 -1.9092260092568061e+00 -2.7778884577312546e-01 + 43 6.9234276916198878e-01 -5.1959961009882676e+00 -3.8252772226000875e-01 + 44 3.1974368019332173e+00 -3.7333126721070280e+00 1.1927602690851384e+00 + 45 -1.0979421463666958e+00 1.4281871410588294e+00 -2.7844688870636198e+00 + 46 -2.3123927283410217e-01 -1.6246499267294727e+00 4.6068188624710249e+00 + 47 2.4575638270171467e+00 1.3529111936752543e+00 8.5779610605164602e-01 + 48 1.3053149069961443e+00 5.5515699432490484e-01 -3.4671208564970402e-01 + 49 -1.6274632987918105e+00 -4.7057286351454088e+00 -2.4249279782812732e+00 + 50 5.4224455439310093e-03 2.9897586979430217e+00 -1.3950914387971693e+00 + 51 -1.2459066473548792e+00 2.9456460712366876e+00 3.7288916669729959e+00 + 52 4.2616245425615205e+00 -4.4802874559504291e+00 4.4417404910061506e+00 + 53 2.7936251596841610e+00 2.8362368635929394e+00 1.5493162393308044e+00 + 54 2.4623429186012755e+00 -2.8582750315396499e+00 -1.7184511417663617e+00 + 55 8.8879734150460621e-01 -2.9956850724190431e+00 -3.5690867805221691e+00 + 56 -4.2180992335342177e-01 2.9462851508525678e-01 -2.1026878944189669e+00 + 57 1.9534125277666281e-01 -1.1222033415899244e+00 -3.0127694733977195e-01 + 58 2.7751167493305626e+00 -9.6251009716841951e-01 1.2847140239740573e+00 + 59 2.7332664305562209e+00 1.6874001147167499e+00 -1.8630365579739607e+00 + 60 -1.9920742303178285e+00 -5.1976595137487056e+00 -2.5715712256855774e+00 + 61 -3.9186643718089481e-01 1.8941052818415300e+00 -7.2852310082079819e-01 + 62 -2.6017296078018819e+00 2.4477411514482621e+00 -2.1740532348808825e+00 + 63 1.6751774499211520e+00 -2.9786131173037549e+00 -4.4746311293685642e-02 + 64 2.2350712682686380e+00 2.4856397598318205e+00 6.0442073184431777e+00 +run_vdwl: -258.499584619321 +run_coul: 0 +run_stress: ! |2- + 6.4308150527103614e+00 8.9800084577004764e+00 1.6644352826777482e+01 -5.0072836166618568e+00 3.4219822049720790e+01 1.0492755818511172e+00 +run_forces: ! |2 + 1 -7.5937225945191122e-01 3.0453338820624811e+00 1.7588997466292056e+00 + 2 -2.5842358982706677e+00 -6.4961465564128662e-01 -2.0836136472876197e+00 + 3 6.1692053064565644e-01 -1.7923404289185974e-01 -2.5776384969836241e-01 + 4 -1.8642434201915430e+00 2.9689111966333672e+00 5.7860631771456472e-01 + 5 -1.3707214670175785e+00 -7.6882987771621347e-01 -3.8201327706453814e-01 + 6 3.5155029154762341e-01 3.3059375310476078e+00 1.0816937673465203e+00 + 7 -1.0031791908316097e+00 -8.6956258363515204e-01 2.2425245701191070e+00 + 8 5.9471905500604189e-02 6.7712638453434015e-01 -9.9455163342941122e-01 + 9 1.3241459971784919e-01 -2.4451165559600572e+00 -2.5688790543810329e+00 + 10 2.7987760793693212e-01 -1.3869445644157263e+00 -2.2737317068259308e+00 + 11 3.0093494238363268e+00 -3.2460329093478379e+00 2.0365059309648439e+00 + 12 -5.2407947220384763e+00 -2.9126835314212682e+00 -2.3181888620032565e+00 + 13 -2.9313171694052698e-01 5.1682815539279954e+00 -9.3178794848643598e-01 + 14 -1.4205609885169097e+00 3.6728355747283463e+00 4.7973399101037340e-01 + 15 -3.6438539671591643e+00 4.1738585973197502e+00 -2.2995365264114347e+00 + 16 1.7665430094094714e+00 -1.6683625143013525e-01 5.5980371284228605e+00 + 17 1.1609229005729407e+00 2.5445052813433287e+00 -4.1101936902212186e+00 + 18 2.3250564926704054e-01 5.0119516573696155e-01 3.2999642189792304e+00 + 19 -1.2455755116140121e+00 -2.0483645027853807e+00 -3.9915953456529252e-01 + 20 -6.1533476323819203e+00 -1.0336110065476412e-01 7.2207408198501155e-01 + 21 2.2580812268964414e+00 8.8665782585823316e-01 -2.7867801730661323e+00 + 22 -5.0715437260287493e+00 3.2720805913066657e+00 -3.9870515109961557e+00 + 23 1.9067384153660658e+00 3.3666024181351721e+00 1.2360966484469924e+00 + 24 1.6076366668181861e+00 1.3129049141466476e+00 2.6481286770383776e+00 + 25 8.2849608759830151e-01 -1.0946573631083545e-01 1.4137379099564085e+00 + 26 -4.9245162995242880e-01 -6.5633188499443484e-01 1.2397189435323861e+00 + 27 2.0002068209516661e+00 -5.2635863568453822e-01 1.5812202387080725e+00 + 28 5.6469856769551852e-01 1.3419655823448202e+00 4.1390158656307525e+00 + 29 -3.0414702840012948e+00 9.2717141813720239e-01 -1.1446412926158587e+00 + 30 -1.5780449202280797e+00 1.1972994610432468e+00 -9.0937832538133612e-01 + 31 -1.6135468940121711e+00 1.4097747951848163e+00 -4.1013831792153099e-01 + 32 -3.3839861094060888e+00 5.3294244523688450e-01 -5.1150418397488095e-01 + 33 4.3179202422054406e+00 -2.0530236039826524e+00 2.7909047875298811e+00 + 34 3.6044665483098232e-01 -9.6636898593039144e-02 3.8719889716901629e-01 + 35 1.2574806392912055e+00 -1.5474474915601943e-01 2.5284696614854756e+00 + 36 -9.2507293281644375e-03 -2.5688826605251358e+00 -8.8917022692741904e-02 + 37 -1.7874734690581056e+00 1.7150271178647891e+00 -2.3271057624958509e+00 + 38 -2.4661620498076103e-01 -4.4857619626472056e-01 3.0090275233366270e-01 + 39 2.1876463817083560e+00 -1.2953665685718856e+00 -4.1070251548878520e+00 + 40 3.8954619284502536e+00 -1.9483835119233155e+00 -1.2227531289486768e+00 + 41 9.7565025130135041e-02 -5.2646053136013127e-01 -9.8280124384883183e-01 + 42 -4.1063684952413535e+00 -1.9251952021816745e+00 -2.9901720065849435e-01 + 43 6.7939692714052213e-01 -5.1874569217280806e+00 -3.9562141807204243e-01 + 44 3.2038887558507829e+00 -3.7312345150786044e+00 1.1791192854596575e+00 + 45 -1.1219574440264417e+00 1.4017870123386482e+00 -2.7737869798443779e+00 + 46 -2.3209294883861276e-01 -1.6182897275443398e+00 4.6296975809710883e+00 + 47 2.4602350208971560e+00 1.3452019899041738e+00 8.6358301884597988e-01 + 48 1.3093376440414450e+00 5.7371015819685922e-01 -3.5578408564713881e-01 + 49 -1.6696474765985014e+00 -4.7474039477977401e+00 -2.4607694981777248e+00 + 50 1.2989593641085595e-02 2.9812087098985032e+00 -1.4123464138675532e+00 + 51 -1.2853374902017087e+00 2.9768731587433548e+00 3.7403754374802158e+00 + 52 4.2768627438095859e+00 -4.4665207635055904e+00 4.4526942886426610e+00 + 53 2.8263576162367916e+00 2.8711821643474851e+00 1.6198032393140254e+00 + 54 2.4557393026757914e+00 -2.8576870371250496e+00 -1.7106753514157151e+00 + 55 8.7016650440839127e-01 -2.9993943594233912e+00 -3.5406400607258477e+00 + 56 -4.2720269240408387e-01 2.6284724466018883e-01 -2.0965871396618168e+00 + 57 1.8938909101993642e-01 -1.1500277319075947e+00 -3.0413618657652775e-01 + 58 2.7772395410738167e+00 -9.4351277181421578e-01 1.2588196612829914e+00 + 59 2.7573808165218825e+00 1.7074121472099479e+00 -1.8649938272758655e+00 + 60 -2.0326029067319982e+00 -5.2224816026297454e+00 -2.6126651787431214e+00 + 61 -3.8509493120844207e-01 1.9073567822120387e+00 -7.1625738088539748e-01 + 62 -2.6004175741382847e+00 2.4418292628003133e+00 -2.1615478367945307e+00 + 63 1.6782508781720011e+00 -3.0101538006831734e+00 -7.1986308169233931e-02 + 64 2.2749536899334073e+00 2.5303495677814198e+00 6.0668040667204064e+00 +... From 1f75740de7045d2b355691d3f2eb8d7386d1699d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 18:32:23 -0400 Subject: [PATCH 48/75] add unit test input for hybrid twobody/threebody sw style --- .../tests/manybody-pair-sw_twobody.yaml | 1 - .../tests/manybody-pair-sw_twothree.yaml | 157 ++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 unittest/force-styles/tests/manybody-pair-sw_twothree.yaml diff --git a/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml b/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml index 42d3cebf81..0c48d47137 100644 --- a/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml +++ b/unittest/force-styles/tests/manybody-pair-sw_twobody.yaml @@ -1,6 +1,5 @@ --- lammps_version: 23 Jun 2022 -tags: generated date_generated: Sat Jul 2 17:42:21 2022 epsilon: 1e-10 skip_tests: diff --git a/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml b/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml new file mode 100644 index 0000000000..68faad2b8b --- /dev/null +++ b/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml @@ -0,0 +1,157 @@ +--- +lammps_version: 23 Jun 2022 +date_generated: Sat Jul 2 18:29:21 2022 +epsilon: 1e-10 +skip_tests: +prerequisites: ! | + pair sw +pre_commands: ! | + variable newton_pair delete + variable newton_pair index on +post_commands: ! "" +input_file: in.manybody +pair_style: hybrid sw threebody on sw threebody off +pair_coeff: ! | + * * sw 1 Si.sw Si Si Si Si NULL NULL NULL NULL + * 5*8 sw 2 Si.sw Si Si Si Si Si Si Si Si +extract: ! "" +natoms: 64 +init_vdwl: -258.5086399744112 +init_coul: 0 +init_stress: ! |2- + 6.4784606418926112e+00 8.9666260111962721e+00 1.6564019181847929e+01 -4.9679178424853108e+00 3.4388958502311766e+01 7.5390085880741020e-01 +init_forces: ! |2 + 1 -7.5117950925001176e-01 3.0512035938320858e+00 1.7548369060319566e+00 + 2 -2.5839943840157309e+00 -6.2407030855217227e-01 -2.0776063043672743e+00 + 3 6.4874626526431012e-01 -1.9191097130277632e-01 -2.8953907507029547e-01 + 4 -1.8780621642391011e+00 2.9563493639933633e+00 6.0045000947943949e-01 + 5 -1.3527836952861756e+00 -7.5563316408513048e-01 -4.0171785277050776e-01 + 6 3.0261027539317009e-01 3.3010495375946016e+00 1.0578432546004755e+00 + 7 -1.0220792530722718e+00 -8.9704171544868350e-01 2.2679286915120240e+00 + 8 7.3786854090375081e-02 6.8184832716858157e-01 -9.9742395408331985e-01 + 9 1.5404185888294175e-01 -2.4234656392100611e+00 -2.5547421022333259e+00 + 10 2.9152004537455556e-01 -1.3832499266817839e+00 -2.3112314079357352e+00 + 11 3.0397941652061906e+00 -3.2696818086953119e+00 2.0402858501197878e+00 + 12 -5.2427766862019523e+00 -2.9266230951551324e+00 -2.3274249447095405e+00 + 13 -3.4764983756087076e-01 5.1759636691465873e+00 -9.5777981490571462e-01 + 14 -1.3994974411693033e+00 3.6723581318644940e+00 4.9022891744156372e-01 + 15 -3.6451950102391031e+00 4.1804831124641391e+00 -2.3094319497556559e+00 + 16 1.7762576801244845e+00 -1.7769734947711072e-01 5.6118226682874788e+00 + 17 1.1626295956924018e+00 2.5434335885879951e+00 -4.1298909753889967e+00 + 18 2.6370967308167081e-01 4.8174395544262394e-01 3.2879300848711188e+00 + 19 -1.2180668170296169e+00 -2.0218982136836656e+00 -3.8444674827692210e-01 + 20 -6.1734234441618661e+00 -8.7047552939678141e-02 7.4088977580926274e-01 + 21 2.2527604735738231e+00 9.2512650808085084e-01 -2.7596546519202407e+00 + 22 -5.0794028512678571e+00 3.2609137644938517e+00 -3.9745643191135742e+00 + 23 1.8924999787954402e+00 3.3526647080652703e+00 1.2248568956854238e+00 + 24 1.5743771798508370e+00 1.3293691279058382e+00 2.6631027667815861e+00 + 25 8.3110919554353102e-01 -1.1460141213930673e-01 1.4368370342964809e+00 + 26 -4.7991049590854340e-01 -6.7362488036409351e-01 1.2327946774343894e+00 + 27 1.9900570741156840e+00 -5.0620387120655386e-01 1.5762394195123659e+00 + 28 5.7701286079972869e-01 1.3187742380597833e+00 4.1298244053155067e+00 + 29 -3.0416482921788530e+00 9.1958118398971411e-01 -1.1418981151511567e+00 + 30 -1.5986340571260498e+00 1.2172058599377520e+00 -8.9839567436920520e-01 + 31 -1.6221367664137194e+00 1.4053388522714974e+00 -4.1030835758060596e-01 + 32 -3.3999213065003993e+00 5.4646623792746152e-01 -4.9970596852221305e-01 + 33 4.3374523080961529e+00 -2.0526192390847209e+00 2.7863621822774500e+00 + 34 3.6899967841270293e-01 -9.7647299317203395e-02 3.7849093017581542e-01 + 35 1.2474956459695270e+00 -1.6786310914897268e-01 2.5255688468043052e+00 + 36 1.9423002050769304e-02 -2.5811032787101440e+00 -5.3464409483951571e-02 + 37 -1.7991755427583054e+00 1.7326288527074167e+00 -2.3172591544605829e+00 + 38 -2.6635345675769728e-01 -4.5619472453099841e-01 2.9146619578940203e-01 + 39 2.2040425723147274e+00 -1.2990665525340543e+00 -4.1031233229148558e+00 + 40 3.8636879669801210e+00 -1.9428932057790562e+00 -1.2090029697953577e+00 + 41 6.9184862237524403e-02 -5.5563025877752559e-01 -1.0104421056432389e+00 + 42 -4.1077204863643084e+00 -1.9092260113246491e+00 -2.7778884595397102e-01 + 43 6.9234276922332216e-01 -5.1959961010562941e+00 -3.8252772225624965e-01 + 44 3.1974368019347907e+00 -3.7333126721157721e+00 1.1927602690968555e+00 + 45 -1.0979421463666958e+00 1.4281871410588294e+00 -2.7844688870636198e+00 + 46 -2.3123927283410217e-01 -1.6246499267294727e+00 4.6068188624710249e+00 + 47 2.4575638270171472e+00 1.3529111936752540e+00 8.5779610605164602e-01 + 48 1.3053149069961458e+00 5.5515699432490606e-01 -3.4671208564970524e-01 + 49 -1.6274632987918134e+00 -4.7057286351454106e+00 -2.4249279782812714e+00 + 50 5.4224455439262353e-03 2.9897586979430182e+00 -1.3950914387971656e+00 + 51 -1.2459066473548797e+00 2.9456460712366872e+00 3.7288916669729959e+00 + 52 4.2616245425615027e+00 -4.4802874559509265e+00 4.4417404910060432e+00 + 53 2.7936251596841610e+00 2.8362368635929394e+00 1.5493162393308042e+00 + 54 2.4623429186012760e+00 -2.8582750315396499e+00 -1.7184511417663617e+00 + 55 8.8879734150460621e-01 -2.9956850724190431e+00 -3.5690867805221691e+00 + 56 -4.2180992335342188e-01 2.9462851508525667e-01 -2.1026878944189669e+00 + 57 1.9534125277666264e-01 -1.1222033415899244e+00 -3.0127694733977195e-01 + 58 2.7751167493305635e+00 -9.6251009716841907e-01 1.2847140239740573e+00 + 59 2.7332664162886871e+00 1.6874002693437411e+00 -1.8630367163899653e+00 + 60 -1.9920742303178280e+00 -5.1976595137487056e+00 -2.5715712256855774e+00 + 61 -3.9186643718089481e-01 1.8941052818415300e+00 -7.2852310082079819e-01 + 62 -2.6017296078018801e+00 2.4477411514482652e+00 -2.1740532348808843e+00 + 63 1.6751774499211569e+00 -2.9786131173037518e+00 -4.4746311293689334e-02 + 64 2.2350712682686358e+00 2.4856397598318183e+00 6.0442073184431804e+00 +run_vdwl: -258.49958453100083 +run_coul: 0 +run_stress: ! |2- + 6.4308197607512207e+00 8.9800199663285003e+00 1.6644361563236384e+01 -5.0072799407290933e+00 3.4219821356106827e+01 1.0492826840671752e+00 +run_forces: ! |2 + 1 -7.5937225945190234e-01 3.0453338820624740e+00 1.7588997466292104e+00 + 2 -2.5842358982706335e+00 -6.4961465564215615e-01 -2.0836136472867430e+00 + 3 6.1692053064567098e-01 -1.7923404289169326e-01 -2.5776384969855004e-01 + 4 -1.8642434203060627e+00 2.9689111967459501e+00 5.7860631771685955e-01 + 5 -1.3707214670175798e+00 -7.6882987771621170e-01 -3.8201327706454102e-01 + 6 3.5155029154762341e-01 3.3059375310476078e+00 1.0816937673465203e+00 + 7 -1.0031791908375163e+00 -8.6956258364196903e-01 2.2425245701241256e+00 + 8 5.9471905500534855e-02 6.7712638453480301e-01 -9.9455163342971664e-01 + 9 1.3241459971867986e-01 -2.4451165560724939e+00 -2.5688790544877764e+00 + 10 2.7987758741645180e-01 -1.3869445825984155e+00 -2.2737317048352144e+00 + 11 3.0093494239500198e+00 -3.2460329093479796e+00 2.0365059310692528e+00 + 12 -5.2407962483722299e+00 -2.9126882828373892e+00 -2.3181917992781393e+00 + 13 -2.9313171711099439e-01 5.1682815536825712e+00 -9.3178794843042834e-01 + 14 -1.4205609886004000e+00 3.6728355746142340e+00 4.7973399106847658e-01 + 15 -3.6438539671591443e+00 4.1738585973197599e+00 -2.2995365264114183e+00 + 16 1.7665430073015751e+00 -1.6683625231758512e-01 5.5980371264386779e+00 + 17 1.1609247141613361e+00 2.5445069687633297e+00 -4.1101937186844282e+00 + 18 2.3250564926703476e-01 5.0119516573698042e-01 3.2999642189792335e+00 + 19 -1.2455755116140015e+00 -2.0483645027853901e+00 -3.9915953456528291e-01 + 20 -6.1533476323819176e+00 -1.0336110065476101e-01 7.2207408198501200e-01 + 21 2.2580812272923181e+00 8.8665782626079470e-01 -2.7867801726837302e+00 + 22 -5.0715437260287493e+00 3.2720805913066657e+00 -3.9870515109961557e+00 + 23 1.9067384153660658e+00 3.3666024181351721e+00 1.2360966484469924e+00 + 24 1.6076366668181858e+00 1.3129049141466480e+00 2.6481286770383776e+00 + 25 8.2849608748715486e-01 -1.0946573630945597e-01 1.4137379100581711e+00 + 26 -4.9245162995242892e-01 -6.5633188499443507e-01 1.2397189435323859e+00 + 27 2.0002065581614117e+00 -5.2635572637485362e-01 1.5812234025897816e+00 + 28 5.6469856770107940e-01 1.3419655834852620e+00 4.1390158667617349e+00 + 29 -3.0414702840545953e+00 9.2717141811819714e-01 -1.1446412927789829e+00 + 30 -1.5780449202280824e+00 1.1972994610432426e+00 -9.0937832538133057e-01 + 31 -1.6135468935693842e+00 1.4097747955859368e+00 -4.1013831752390606e-01 + 32 -3.3839861094059622e+00 5.3294244523702139e-01 -5.1150418397474551e-01 + 33 4.3179202422054441e+00 -2.0530236039826488e+00 2.7909047875298785e+00 + 34 3.6044667063424402e-01 -9.6636899664461151e-02 3.8719887922396817e-01 + 35 1.2574806392912077e+00 -1.5474474915574327e-01 2.5284696614857665e+00 + 36 -9.2507293281699435e-03 -2.5688826605251358e+00 -8.8917022692736422e-02 + 37 -1.7874734690551621e+00 1.7150271178623466e+00 -2.3271057624984728e+00 + 38 -2.4661620491429920e-01 -4.4857619628292789e-01 3.0090275228821284e-01 + 39 2.1876463817081504e+00 -1.2953665685720137e+00 -4.1070251548877099e+00 + 40 3.8954619284501790e+00 -1.9483835119233022e+00 -1.2227531289487130e+00 + 41 9.7565025130134986e-02 -5.2646053136013171e-01 -9.8280124384883216e-01 + 42 -4.1063684977228627e+00 -1.9251952046358491e+00 -2.9901720087297745e-01 + 43 6.7939692719606781e-01 -5.1874569217896553e+00 -3.9562141806867668e-01 + 44 3.2038887558519624e+00 -3.7312345150848865e+00 1.1791192854681274e+00 + 45 -1.1219574440264011e+00 1.4017870123386453e+00 -2.7737869798443744e+00 + 46 -2.3209294883240861e-01 -1.6182897275509909e+00 4.6296975808382292e+00 + 47 2.4602350215523230e+00 1.3452019889417968e+00 8.6358301807951909e-01 + 48 1.3093376440442195e+00 5.7371015819454974e-01 -3.5578408565068542e-01 + 49 -1.6696474765984912e+00 -4.7474039477977534e+00 -2.4607694981777111e+00 + 50 1.2989593641085762e-02 2.9812087098985032e+00 -1.4123464138675532e+00 + 51 -1.2853374902017072e+00 2.9768731587433566e+00 3.7403754374802136e+00 + 52 4.2768627438095717e+00 -4.4665207635060362e+00 4.4526942886425624e+00 + 53 2.8263576162367916e+00 2.8711821643474851e+00 1.6198032393140251e+00 + 54 2.4557393026757937e+00 -2.8576870371250473e+00 -1.7106753514157176e+00 + 55 8.7016650440838128e-01 -2.9993943594233770e+00 -3.5406400607258579e+00 + 56 -4.2720269240408387e-01 2.6284724466018883e-01 -2.0965871396618163e+00 + 57 1.8938909101993043e-01 -1.1500277319075998e+00 -3.0413618657653302e-01 + 58 2.7772395410738175e+00 -9.4351277181421511e-01 1.2588196612829905e+00 + 59 2.7573808001442890e+00 1.7074123239008328e+00 -1.8649940082533623e+00 + 60 -2.0326029067319991e+00 -5.2224816026297418e+00 -2.6126651787431188e+00 + 61 -3.8509493124293104e-01 1.9073567822530930e+00 -7.1625738092917579e-01 + 62 -2.6004175741383238e+00 2.4418292628003044e+00 -2.1615478367946075e+00 + 63 1.6782508782162848e+00 -3.0101538006328594e+00 -7.1986308168993651e-02 + 64 2.2749536899334060e+00 2.5303495677814132e+00 6.0668040667204073e+00 +... From 6f3f1dba6e3f2b05c3d68ad94ff81a73177d270f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 19:05:58 -0400 Subject: [PATCH 49/75] more documentation tweaks --- doc/src/pair_sw.rst | 55 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/doc/src/pair_sw.rst b/doc/src/pair_sw.rst index b2305dab5e..c456b2f422 100644 --- a/doc/src/pair_sw.rst +++ b/doc/src/pair_sw.rst @@ -134,15 +134,17 @@ three-body term of the potential is calculated. The default value is but not available for the *sw/mod* and :doc:`sw/angle/table ` pair style variants. To turn off the threebody contributions all :math:`\lambda_{ijk}` parameters from the potential -file are forcibly set to 0. In addition the pair style implementations +file are forcibly set to 0. In addition the pair style implementation may employ code optimizations for the *threebody off* setting that can -result in significant speedups versus the default. +result in significant speedups versus the default. These code optimizations +are currently only available for the MANYBODY and OPENMP packages. -Only a single pair_coeff command is used with the *sw* and *sw/mod* styles -which specifies a Stillinger-Weber potential file with parameters for all -needed elements. These are mapped to LAMMPS atom types by specifying -N additional arguments after the filename in the pair_coeff command, -where N is the number of LAMMPS atom types: +Only a single pair_coeff command is used with the *sw* and *sw/mod* +styles which specifies a Stillinger-Weber potential file with parameters +for all needed elements, except for when the *threebody off* setting is +used (see note below). These are mapped to LAMMPS atom types by +specifying N additional arguments after the filename in the pair_coeff +command, where N is the number of LAMMPS atom types: * filename * N element names = mapping of SW elements to atom types @@ -157,20 +159,20 @@ pair_coeff command: .. code-block:: LAMMPS + pair_style sw pair_coeff * * SiC.sw Si Si Si C The first 2 arguments must be \* \* so as to span all LAMMPS atom types. -The first three Si arguments map LAMMPS atom types 1,2,3 to the Si -element in the SW file. The final C argument maps LAMMPS atom type 4 -to the C element in the SW file. If a mapping value is specified as -NULL, the mapping is not performed. This can be used when a *sw* +The first three Si arguments map LAMMPS atom types 1, 2, and 3 to the Si +element in the SW file. The final C argument maps LAMMPS atom type 4 to +the C element in the SW file. If an argument value is specified as +NULL, the mapping is not performed. This can be used when an *sw* potential is used as part of the *hybrid* pair style. The NULL values -are placeholders for atom types that will be used with other -potentials. +are placeholders for atom types that will be used with other potentials. .. note:: - When the *skip_threebody on* keyword is used, multiple pair_coeff commands may + When the *threebody off* keyword is used, multiple pair_coeff commands may be used to specific the pairs of atoms which don't require three-body term. In these cases, the first 2 arguments are not required to be \* \*. @@ -276,30 +278,39 @@ described above from values in the potential file. This pair style does not support the :doc:`pair_modify ` shift, table, and tail options. -This pair style does not write its information to :doc:`binary restart files `, since it is stored in potential files. Thus, you -need to re-specify the pair_style and pair_coeff commands in an input -script that reads a restart file. +This pair style does not write its information to :doc:`binary restart +files `, since it is stored in potential files. Thus, you need +to re-specify the pair_style and pair_coeff commands in an input script +that reads a restart file. This pair style can only be used via the *pair* keyword of the :doc:`run_style respa ` command. It does not support the *inner*, *middle*, *outer* keywords. +The single() function of the *sw* pair style is only enabled and +supported for the case of the *threebody off* setting. + ---------- Restrictions """""""""""" -This pair style is part of the MANYBODY package. It is only enabled -if LAMMPS was built with that package. See the :doc:`Build package ` page for more info. +This pair style is part of the MANYBODY package. It is only enabled if +LAMMPS was built with that package. See the :doc:`Build package +` page for more info. This pair style requires the :doc:`newton ` setting to be "on" for pair interactions. The Stillinger-Weber potential files provided with LAMMPS (see the potentials directory) are parameterized for metal :doc:`units `. -You can use the SW potential with any LAMMPS units, but you would need -to create your own SW potential file with coefficients listed in the -appropriate units if your simulation does not use "metal" units. +You can use the sw or sw/mod pair styles with any LAMMPS units, but you +would need to create your own SW potential file with coefficients listed +in the appropriate units if your simulation does not use "metal" units. +If the potential file contains a 'UNITS:' metadata tag in the first line +of the potential file, then LAMMPS can convert it transparently between +"metal" and "real" units. + Related commands """""""""""""""" From ea48b031a48fad37537cf9f247618f48d9b3ae08 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 2 Jul 2022 21:24:25 -0400 Subject: [PATCH 50/75] must not run hybrid styles with GPU package that use the same pair style twice --- unittest/force-styles/tests/manybody-pair-sw_twothree.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml b/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml index 68faad2b8b..0a7c60a892 100644 --- a/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml +++ b/unittest/force-styles/tests/manybody-pair-sw_twothree.yaml @@ -2,7 +2,7 @@ lammps_version: 23 Jun 2022 date_generated: Sat Jul 2 18:29:21 2022 epsilon: 1e-10 -skip_tests: +skip_tests: gpu prerequisites: ! | pair sw pre_commands: ! | From 1599afc623bf6083e9d5bac6c7be10de21b07879 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Wed, 6 Jul 2022 00:45:36 -0400 Subject: [PATCH 51/75] clarify how reaxff/species decides when to reneighbor --- doc/src/fix_reaxff_species.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/doc/src/fix_reaxff_species.rst b/doc/src/fix_reaxff_species.rst index 208ee31009..11f0e7b7e7 100644 --- a/doc/src/fix_reaxff_species.rst +++ b/doc/src/fix_reaxff_species.rst @@ -68,13 +68,18 @@ the first line. .. warning:: In order to compute averaged data, it is required that there are no - neighbor list rebuilds between the *Nfreq* steps. For that reason, fix - *reaxff/species* may change your neighbor list settings. There will - be a warning message showing the new settings. Having an *Nfreq* - setting that is larger than what is required for correct computation - of the ReaxFF force field interactions can thus lead to incorrect - results. For typical ReaxFF calculations a value of 100 is already - quite large. + neighbor list rebuilds for at least Nrepeat\*Nevery steps preceding + each *Nfreq* step. For that reason, fix *reaxff/species* may + change your neighbor list settings. Reneighboring will occur no + more frequently than every Nrepeat\*Nevery timesteps, and will + occur less frequently if *Nfreq* is not a multiple of + Nrepeat\*Nevery. There will be a warning message showing the new + settings. Having a *Nfreq* setting that is larger than what is + required for correct computation of the ReaxFF force field + interactions, in combination with certain *Nrepeat* and *Nevery* + settings, can thus lead to incorrect results. For typical ReaxFF + calculations, reneighboring only every 100 steps is already quite a + low frequency. If the filename ends with ".gz", the output file is written in gzipped format. A gzipped dump file will be about 3x smaller than the text version, From 14299b36ba3b5a1bc390c913b9c24277cb9718b8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 7 Jul 2022 08:59:08 -0400 Subject: [PATCH 52/75] correct force and energy for excluded pairs --- src/EXTRA-PAIR/pair_coul_slater_long.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-PAIR/pair_coul_slater_long.cpp b/src/EXTRA-PAIR/pair_coul_slater_long.cpp index 65619f0f65..7351c2a493 100644 --- a/src/EXTRA-PAIR/pair_coul_slater_long.cpp +++ b/src/EXTRA-PAIR/pair_coul_slater_long.cpp @@ -124,7 +124,7 @@ void PairCoulSlaterLong::compute(int eflag, int vflag) slater_term = exp(-2*r/lamda)*(1 + (2*r/lamda*(1+r/lamda))); prefactor = qqrd2e * scale[itype][jtype] * qtmp*q[j]/r; forcecoul = prefactor * (erfc + EWALD_F*grij*expm2 - slater_term); - if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor*(1-slater_term); fpair = forcecoul * r2inv; @@ -139,7 +139,7 @@ void PairCoulSlaterLong::compute(int eflag, int vflag) if (eflag) { ecoul = prefactor*(erfc - (1 + r/lamda)*exp(-2*r/lamda)); - if (factor_coul < 1.0) ecoul -= (1.0-factor_coul)*prefactor; + if (factor_coul < 1.0) ecoul -= (1.0-factor_coul)*prefactor*(1.0-(1 + r/lamda)*exp(-2*r/lamda)); } if (evflag) ev_tally(i,j,nlocal,newton_pair, From fc508bc7b1b244689d72108808663491d00e6353 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 7 Jul 2022 08:59:49 -0400 Subject: [PATCH 53/75] apply clang-format --- src/EXTRA-PAIR/pair_coul_slater_long.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/EXTRA-PAIR/pair_coul_slater_long.cpp b/src/EXTRA-PAIR/pair_coul_slater_long.cpp index 7351c2a493..6662ae3721 100644 --- a/src/EXTRA-PAIR/pair_coul_slater_long.cpp +++ b/src/EXTRA-PAIR/pair_coul_slater_long.cpp @@ -116,14 +116,14 @@ void PairCoulSlaterLong::compute(int eflag, int vflag) if (rsq < cut_coulsq) { r2inv = 1.0/rsq; - r = sqrt(rsq); - grij = g_ewald * r; - expm2 = exp(-grij*grij); - t = 1.0 / (1.0 + EWALD_P*grij); - erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; - slater_term = exp(-2*r/lamda)*(1 + (2*r/lamda*(1+r/lamda))); - prefactor = qqrd2e * scale[itype][jtype] * qtmp*q[j]/r; - forcecoul = prefactor * (erfc + EWALD_F*grij*expm2 - slater_term); + r = sqrt(rsq); + grij = g_ewald * r; + expm2 = exp(-grij*grij); + t = 1.0 / (1.0 + EWALD_P*grij); + erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; + slater_term = exp(-2*r/lamda)*(1 + (2*r/lamda*(1+r/lamda))); + prefactor = qqrd2e * scale[itype][jtype] * qtmp*q[j]/r; + forcecoul = prefactor * (erfc + EWALD_F*grij*expm2 - slater_term); if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor*(1-slater_term); fpair = forcecoul * r2inv; @@ -138,7 +138,7 @@ void PairCoulSlaterLong::compute(int eflag, int vflag) } if (eflag) { - ecoul = prefactor*(erfc - (1 + r/lamda)*exp(-2*r/lamda)); + ecoul = prefactor*(erfc - (1 + r/lamda)*exp(-2*r/lamda)); if (factor_coul < 1.0) ecoul -= (1.0-factor_coul)*prefactor*(1.0-(1 + r/lamda)*exp(-2*r/lamda)); } From 66c1d8bbca60809c452714dc13d5726cd1c33118 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 7 Jul 2022 09:01:16 -0400 Subject: [PATCH 54/75] update unit test --- .../tests/mol-pair-coul_slater_long.yaml | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/unittest/force-styles/tests/mol-pair-coul_slater_long.yaml b/unittest/force-styles/tests/mol-pair-coul_slater_long.yaml index becaaddacb..52efdaa52d 100644 --- a/unittest/force-styles/tests/mol-pair-coul_slater_long.yaml +++ b/unittest/force-styles/tests/mol-pair-coul_slater_long.yaml @@ -1,6 +1,6 @@ --- -lammps_version: 17 Feb 2022 -date_generated: Fri Mar 18 22:17:29 2022 +lammps_version: 23 Jun 2022 +date_generated: Thu Jul 7 09:00:39 2022 epsilon: 2e-13 skip_tests: prerequisites: ! | @@ -21,71 +21,71 @@ extract: ! | cut_coul 0 natoms: 29 init_vdwl: 0 -init_coul: 531.6959978948178 +init_coul: 254.5592842067175 init_stress: ! |2- - 2.5972486027624825e+02 1.6017698793801901e+02 2.4137753464923463e+02 1.1278136362375312e+02 7.5965726957592040e+01 4.3719482306226765e-01 + 1.1708139667614163e-01 -2.1626456898855512e+01 -1.2229635861086752e+01 1.1761975361937868e+01 -9.4490379133316071e-01 7.0303045871968024e+00 init_forces: ! |2 - 1 -1.4505459775863072e+01 -1.3044138972612792e+01 2.1164399702389403e+01 - 2 1.7223357637414050e+01 1.1196601670721074e+01 -2.1963333681835362e+01 - 3 1.9690750992824077e-01 8.4027350365370912e-01 3.2676598741321244e-01 - 4 -4.1823205578686712e-01 -4.8034612329946474e-01 -6.2465767147804363e-01 - 5 -8.1067146840659532e-01 -7.0419963313519196e-01 1.0662874339675656e-01 - 6 -1.5369327833513578e+01 1.5524058497083976e+01 2.0805079862911210e+01 - 7 2.5724194203247794e+00 -6.5060840392199450e+00 -2.5818209750744270e+01 - 8 5.4989014023099685e+00 -1.3233615378795388e+01 -1.9749310147567481e+01 - 9 6.7810825735410649e+00 2.9735670644350400e+00 2.9299979326310858e+01 - 10 1.5690354534134865e+00 -3.8317369465722124e+00 -4.6200627486826261e-02 - 11 -3.3949224827476737e-01 6.3351363413160511e-01 4.2844876717729496e-01 - 12 3.5635170964422114e-01 -1.4508136789019890e+00 3.4423369459456787e+00 - 13 3.1047049296276570e+00 -1.3699917700047379e+00 -1.9294387191193760e-01 - 14 -1.8808718796901713e+00 4.5935276633752392e-01 -3.4319894559470154e+00 - 15 5.0373263520628020e-01 3.2293151135885791e+00 1.4501551118828643e-01 - 16 9.3153064058538995e+00 -7.2444035690649828e+00 -2.8031955215690214e+01 - 17 -1.2854116750038774e+01 1.2711581809604329e+01 2.2641980278104416e+01 - 18 -4.3096955761758995e+00 -1.9223912022853014e+01 7.9000623254438963e+01 - 19 -3.6557250488749723e+01 -2.4008797712603496e+01 -4.6906379653253587e+01 - 20 4.0430244682537058e+01 4.3367408075615103e+01 -3.0568173216213165e+01 - 21 -2.2233648415360879e+01 -2.5175462230434459e+01 7.7556799814543822e+01 - 22 -3.2896290497889161e+01 -7.4008238647922955e+00 -5.6779521143055455e+01 - 23 5.4738206511131686e+01 3.2962897136082404e+01 -2.0302232663948129e+01 - 24 1.6836507718223405e+01 -6.8988891903011790e+01 4.0189734824880482e+01 - 25 -4.9788824311046007e+01 1.0298368296372720e+01 -4.2822722831087503e+01 - 26 3.2480024672491567e+01 5.8484108401966807e+01 2.1410689331760921e+00 - 27 1.0631780570743651e+01 -7.5812252190257794e+01 2.6785237094554518e+01 - 28 -5.1807337133621779e+01 2.4969152478376095e+01 -3.5819948334944876e+01 - 29 4.1532654602026298e+01 5.0825271587590592e+01 9.0234792187328665e+00 + 1 1.0123638170253564e+00 -1.2509301778440569e+00 2.1945424754579066e+00 + 2 1.2954585046924219e+00 -1.6338868361150480e+00 -3.0887376983421388e+00 + 3 2.5466335168114949e-02 -1.3139527326988556e-02 3.5569205669245270e-02 + 4 -1.2001437337468715e-01 -5.6411226640158199e-02 -2.7885583471425601e-01 + 5 -5.1442751513887475e-01 3.3619784208637826e-01 -5.8612509036156044e-02 + 6 2.0722678070474260e-01 -2.0158659756241346e+00 1.2290591371946380e-01 + 7 1.2567425258842402e-01 1.6224546679223717e-01 1.2761543372504829e+00 + 8 -1.0885715086892622e+00 2.7631213623524755e+00 -1.1767360015218493e-01 + 9 1.4894653246735305e+00 -3.7617960133817596e+00 3.1053923325311867e+00 + 10 -7.1355785210118067e-02 3.1060808027765684e-02 -1.9612261171752393e-01 + 11 -6.7234571380736030e-01 7.7973238090172237e-01 -4.0949779264856478e-01 + 12 2.3138844234286426e+00 -4.3745924070374187e-01 1.4448078371866295e+00 + 13 1.4660368459556450e-01 -1.5128217536790745e-01 -1.4303100577311031e-01 + 14 -8.0170764963523022e-01 2.5412749138723761e-01 -3.4566627523769955e-01 + 15 3.3139749151276299e-01 7.0760705097098225e-02 -7.8854914191841807e-01 + 16 1.6259331841614227e-01 -9.7505266997933193e-01 -1.0324909305416741e+00 + 17 -2.8980837212603436e+00 5.6015117342873459e+00 -3.2180999995572175e+00 + 18 -1.9982604761757349e-02 2.3277591984645070e+00 5.7125759352746586e-01 + 19 -9.3976757841446423e-01 -2.1798221084742129e+00 4.9912918497113157e-01 + 20 5.2304880078763782e-01 -1.3238749831696685e-02 4.5568360647359851e-01 + 21 -4.8423714829029962e-01 1.3829719239752545e+00 -2.9605371449607998e-01 + 22 -5.7875171427304151e-01 -7.4682110680773817e-01 1.1868729457280150e-01 + 23 6.7125646044497977e-01 -2.4953977631185778e-01 6.5241242746355066e-01 + 24 2.4761790219940844e-01 1.3513641756258139e+00 -5.1709840925954764e-01 + 25 -1.1849995949271324e+00 -1.0312698215846192e+00 -5.7625135838985497e-01 + 26 4.6508977239668642e-01 -5.2650955871347083e-01 6.0143069461846999e-01 + 27 -7.4501807019330935e-01 1.4539167477546999e+00 -6.3344271356143678e-01 + 28 -2.3479546881246116e-01 -1.0024440823389091e+00 1.1214449785783520e-01 + 29 1.3369115781539251e+00 -4.6930078970690337e-01 5.1006619404609643e-01 run_vdwl: 0 -run_coul: 531.6316938435255 +run_coul: 254.55391272325517 run_stress: ! |2- - 2.6009236274938775e+02 1.6019149935649892e+02 2.4091972549720990e+02 1.1287278205291214e+02 7.6421249875941328e+01 1.0477154416076908e+00 + 1.0507664945308685e-01 -2.1621055562665443e+01 -1.2221091005466242e+01 1.1755690602099170e+01 -9.5887133975896877e-01 7.0291169104254232e+00 run_forces: ! |2 - 1 -1.4505263340624662e+01 -1.3069826884538898e+01 2.1092273240232334e+01 - 2 1.7216929699569594e+01 1.1222653354918181e+01 -2.1892026658892760e+01 - 3 1.9689659133582163e-01 8.4089709839905469e-01 3.2691229507601138e-01 - 4 -4.1534123724459276e-01 -4.7942040205303499e-01 -6.2372283685814967e-01 - 5 -8.0983629907930077e-01 -7.0364522896705939e-01 1.0613059784230094e-01 - 6 -1.5349604856791370e+01 1.5522546924799940e+01 2.0756281297832874e+01 - 7 2.5720205395173057e+00 -6.5136694191526150e+00 -2.5754699982146299e+01 - 8 5.4783011177980798e+00 -1.3193790377127209e+01 -1.9732158944509290e+01 - 9 6.7823956616450456e+00 2.9328643102381267e+00 2.9267293877977238e+01 - 10 1.5701490834675309e+00 -3.8293776378819531e+00 -4.4681502716367193e-02 - 11 -3.4080685500280089e-01 6.3394138301719993e-01 4.2521300521638838e-01 - 12 3.6335920149821210e-01 -1.4542882227067147e+00 3.4250588402471536e+00 - 13 3.1021896119350991e+00 -1.3629775427304498e+00 -1.9236601260012270e-01 - 14 -1.8791529231416744e+00 4.5416896729772194e-01 -3.4214086468019449e+00 - 15 4.9947705909602841e-01 3.2294681516348525e+00 1.5065456540899641e-01 - 16 9.3152895494380115e+00 -7.2584606007912740e+00 -2.8037105174417249e+01 - 17 -1.2853807315028448e+01 1.2730457404190695e+01 2.2646649542757135e+01 - 18 -3.8896274444740597e+00 -1.8742984005196448e+01 7.8575622292818892e+01 - 19 -3.6657681210304887e+01 -2.4147367233205287e+01 -4.6880920994817359e+01 - 20 4.0110836482195197e+01 4.3026017430590635e+01 -3.0166674909873894e+01 - 21 -2.2266717418800365e+01 -2.5007792207614962e+01 7.7439548112199361e+01 - 22 -3.2986424583514150e+01 -7.4943575521934962e+00 -5.6719145374965272e+01 - 23 5.4861504180702049e+01 3.2888028352139067e+01 -2.0245497429195499e+01 - 24 1.6994333620812245e+01 -6.9071608975734406e+01 4.0281992417369800e+01 - 25 -5.0022578934028282e+01 1.0236526699813956e+01 -4.3029340089682030e+01 - 26 3.2556772729625038e+01 5.8630191330865074e+01 2.2575959885742858e+00 - 27 1.0740675179027832e+01 -7.5810812405906574e+01 2.6655288241835144e+01 - 28 -5.1859214575097845e+01 2.4967730720910470e+01 -3.5779461664571272e+01 - 29 4.1474926685469335e+01 5.0824886566985427e+01 9.1126959066595141e+00 + 1 1.0112873572811580e+00 -1.2530276549384933e+00 2.1914171798830568e+00 + 2 1.2910756618724584e+00 -1.6320587244040410e+00 -3.0856670933130275e+00 + 3 2.5417581409305202e-02 -1.3319027044403169e-02 3.5458204071365587e-02 + 4 -1.1903572341057568e-01 -5.6295979790903755e-02 -2.7900490837896463e-01 + 5 -5.1384628561765300e-01 3.3652540945011555e-01 -5.8862099577371089e-02 + 6 2.0557510022365150e-01 -2.0123835505068053e+00 1.2614266602622692e-01 + 7 1.2735793319460478e-01 1.5853467760283904e-01 1.2701822406914272e+00 + 8 -1.0845932144371644e+00 2.7620784785572590e+00 -1.1812899137169540e-01 + 9 1.4885988571079642e+00 -3.7664983214134815e+00 3.1092838634072559e+00 + 10 -7.1576372143287825e-02 3.0962708284801813e-02 -1.9638669006856277e-01 + 11 -6.7233593581762241e-01 7.8020791757188945e-01 -4.0861239677017752e-01 + 12 2.3142942085570231e+00 -4.3672211901097824e-01 1.4445175637184764e+00 + 13 1.4585640919457624e-01 -1.5151491185814414e-01 -1.4301903387021225e-01 + 14 -8.0211823601348731e-01 2.5440367515238671e-01 -3.4418722798600709e-01 + 15 3.3243392757719303e-01 6.9345316666666712e-02 -7.8979319762464939e-01 + 16 1.6228153766652689e-01 -9.7649864654379481e-01 -1.0325498581820041e+00 + 17 -2.8985741606086464e+00 5.6081200657787251e+00 -3.2210360520327095e+00 + 18 -2.4779821561789064e-02 2.3252812699411116e+00 5.7548476355559375e-01 + 19 -9.3073282073812136e-01 -2.1761428427184564e+00 5.0466585562048227e-01 + 20 5.1962859124254412e-01 -1.3823144408831962e-02 4.4616022167444958e-01 + 21 -4.8279265992792930e-01 1.3791791897341579e+00 -2.9615403241565003e-01 + 22 -5.7432971719583037e-01 -7.4279690320955927e-01 1.2017370691438947e-01 + 23 6.6583750391632179e-01 -2.5085912738116967e-01 6.5043205083268785e-01 + 24 2.4365888962669718e-01 1.3544800679852296e+00 -5.2159886075913586e-01 + 25 -1.1794061912936245e+00 -1.0316188106119735e+00 -5.6975657126355306e-01 + 26 4.6484502577860515e-01 -5.2739147192840230e-01 6.0227258976224862e-01 + 27 -7.4688344945317542e-01 1.4562803511493001e+00 -6.3143583726328756e-01 + 28 -2.3319583763082030e-01 -1.0037908986220945e+00 1.1105104982326264e-01 + 29 1.3360518412010989e+00 -4.7065699348295009e-01 5.0895089489608403e-01 ... From 0bae7108b503bf194fdabea13a9c57da78a8a92c Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 11:09:10 -0600 Subject: [PATCH 55/75] Whitespace and format --- lammps | 1 + src/ML-SNAP/compute_snap.cpp | 144 ++++++++++++++++++----------------- src/ML-SNAP/compute_snap.h | 1 - 3 files changed, 75 insertions(+), 71 deletions(-) create mode 120000 lammps diff --git a/lammps b/lammps new file mode 120000 index 0000000000..6ce10c039d --- /dev/null +++ b/lammps @@ -0,0 +1 @@ +python/lammps/ \ No newline at end of file diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 5b935320ee..dca3fd3c01 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -24,7 +24,6 @@ #include "comm.h" #include "memory.h" #include "error.h" -#include "universe.h" // For MPI #include @@ -268,9 +267,9 @@ void ComputeSnap::init() // allocate memory for global array memory->create(snap,size_array_rows,size_array_cols, - "snap:snap"); + "snap:snap"); memory->create(snapall,size_array_rows,size_array_cols, - "snap:snapall"); + "snap:snapall"); array = snapall; // find compute for reference energy @@ -321,6 +320,7 @@ void ComputeSnap::compute_array() } // clear global array + for (int irow = 0; irow < size_array_rows; irow++){ for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ snap[irow][icoeff] = 0.0; @@ -371,7 +371,9 @@ void ComputeSnap::compute_array() const int typeoffset_global = nvalues*(itype-1); if (dgradflag){ + // dBi/dRi tags + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; @@ -381,7 +383,9 @@ void ComputeSnap::compute_array() snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + // dBi/dRj tags + for (int j=0; jtag[i]-1) + 0][0] = atom->tag[i]-1; snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = j; @@ -451,72 +455,72 @@ void ComputeSnap::compute_array() // accumulate dBi/dRi, -dBi/dRj - if (!dgradflag) { + if (!dgradflag) { - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - } + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; - // diagonal elements of quadratic matrix + // diagonal elements of quadratic matrix - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; + ncount++; - // upper-triangular elements of quadratic matrix + // upper-triangular elements of quadratic matrix - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; - } - } - } - } else { + ncount++; + } + } + } + } else { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { // add to snap array for this proc @@ -531,9 +535,9 @@ void ComputeSnap::compute_array() snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; - } + } - } + } } // loop over jj inside @@ -541,30 +545,30 @@ void ComputeSnap::compute_array() if (!dgradflag) { - // linear contributions + // linear contributions int k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - // quadratic contributions + // quadratic contributions - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } - } + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } } else { int k = 3; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; + numneigh_sum += ninside; } } // if (mask[i] & groupbit) diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 7f7252eaa8..4ef714190c 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -53,7 +53,6 @@ class ComputeSnap : public Compute { double cutmax; int quadraticflag; int bikflag, bik_rows, dgradflag, dgrad_rows; - int rank; Compute *c_pe; Compute *c_virial; From c838c9da6f2751520c9d1e574a0c0ae43ef039d1 Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Thu, 7 Jul 2022 11:43:39 -0600 Subject: [PATCH 56/75] Cleaned up language --- doc/src/compute_sna_atom.rst | 83 ++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index fdff250e24..04f9501d1f 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -68,9 +68,6 @@ Syntax *wselfallflag* value = *0* or *1* *0* = self-contribution only for element of central atom *1* = self-contribution for all elements - *bikflag* value = *0* or *1* (only implemented for compute snap) - *0* = per-atom bispectrum descriptors are summed over atoms - *1* = per-atom bispectrum descriptors are not summed over atoms *switchinnerflag* value = *0* or *1* *0* = do not use inner switching function *1* = use inner switching function @@ -78,9 +75,12 @@ Syntax *sinnerlist* = *ntypes* values of *Sinner* (distance units) *dinner* values = *dinnerlist* *dinnerlist* = *ntypes* values of *Dinner* (distance units) - *dgradflag* value = *0* or *1* - *0* = bispectrum descriptor gradients are summed over neighbors - *1* = bispectrum descriptor gradients are not summed over neighbors + *bikflag* value = *0* or *1* (only implemented for compute snap) + *0* = descriptors are summed over atoms of each type + *1* = descriptors are listed separately for each atom + *dgradflag* value = *0* or *1* (only implemented for compute snap) + *0* = descriptor gradients are summed over atoms of each type + *1* = descriptor gradients are listed separately for each atom pair Examples """""""" @@ -363,15 +363,6 @@ This option is typically used in conjunction with the *chem* keyword, and LAMMPS will generate a warning if both *chem* and *bnormflag* are not both set or not both unset. -The keyword *bikflag* determines whether or not to expand the bispectrum -rows of the global array returned by compute snap. If *bikflag* is set -to *1* then the bispectrum row, which is typically the per-atom bispectrum -descriptors :math:`B_{i,k}` summed over all atoms *i* to produce -:math:`B_k`, becomes bispectrum rows equal to the number of atoms. Thus, -the resulting bispectrum rows are :math:`B_{i,k}` instead of just -:math:`B_k`. In this case, the entries in the final column for these rows -are set to zero. - The keyword *switchinnerflag* with value 1 activates an additional radial switching function similar to :math:`f_c(r)` above, but acting to switch off @@ -396,16 +387,36 @@ When the central atom and the neighbor atom have different types, the values of :math:`S_{inner}` and :math:`D_{inner}` are the arithmetic means of the values for both types. -The keyword *dgradflag* determines whether or not to sum the bispectrum -descriptor gradients over neighboring atoms *i'* as explained with *snad/atom* -above. If *dgradflag* is set to 1 then the descriptor gradient rows of the -global snap array are not summed over atoms *i'*. Instead, each row corresponds +The keywords *bikflag* and *dgradflag* are only used by compute *snap*. +The keyword *bikflag* determines whether or not to list the descriptors +of each atom separately, or sum them together and list in a single row. +If *bikflag* is set +to *0* then a single bispectrum row is used, which contains the per-atom bispectrum +descriptors :math:`B_{i,k}` summed over all atoms *i* to produce +:math:`B_k`. If *bikflag* is set +to *1* this is replaced by a separate bispectrum row equal for each atom. +In this case, the entries in the final column for these rows +are set to zero. + +The keyword *dgradflag* determines whether to sum atom gradients or list +them separately. If *dgradflag* is set to 0, the bispectrum +descriptor gradients w.r.t. atom *j* are summed over all atoms *i'* +of type *I* (similar to *snad/atom* above). +If *dgradflag* is set to 1, gradients are listed separately for each pair of atoms. +Each row corresponds to a single term :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` -where :math:`a` is the Cartesian direction for the gradient. This also changes +where :math:`{r}^a_j` is the *a-th* position coordinate of the atom with global +index *j*. This also changes the number of columns to be equal to the number of bispectrum components, with 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a`, as explained more in the Output info section below. The option *dgradflag=1* -must be used with *bikflag=1*. +requires that *bikflag=1*.[APT: This does not make any sense, since dgradflag +produces its own custom output with no bikflag output] + +.. note:: + + Using *dgradflag* = 1 produces a global array with :math:`N + 3N^2 + 1` rows + which becomes expensive for systems with more than 1000 atoms. .. note:: @@ -517,31 +528,31 @@ components. For the purposes of handling contributions to force, virial, and quadratic combinations, these :math:`N_{elem}^3` sub-blocks are treated as a single block of :math:`K N_{elem}^3` columns. -If the *bik* keyword is set to 1, then the first :math:`N` rows of the snap array -correspond to :math:`B_{i,k}` instead of the sum over atoms :math:`i`. In this case, the entries in the final column for these rows -are set to zero. +If the *bik* keyword is set to 1, the structure of the snap array is expanded. +The first :math:`N` rows of the snap array +correspond to :math:`B_{i,k}` instead of a single row summed over atoms :math:`i`. +In this case, the entries in the final column for these rows +are set to zero. Also, each row contains only non-zero entries for the +columns corresponding to the type of that atom. -If the *dgradflag* keyword is set to 1, this changes the structure of the snap array completely. -Here the *snad/atom* quantities are replaced with rows corresponding to descriptor -gradient components +If the *dgradflag* keyword is set to 1, this changes the structure of the +snap array completely. +Here the *snad/atom* quantities are replaced with rows corresponding to +descriptor gradient components on single atoms: .. math:: \frac{\partial {B_{i,k} }}{\partial {r}^a_j} -where :math:`a` is the Cartesian direction for the gradient. The rows are +where :math:`{r}^a_j` is the *a-th* position coordinate of the atom with global +index *j*. The rows are organized in chunks, where each chunk corresponds to an atom :math:`j` in the system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to atoms :math:`i` in the system of :math:`N` atoms. The total number of rows for these descriptor gradients is therefore :math:`3N^2`. - -For *dgradflag=1*, the number of columns is equal to the number of bispectrum components, -plus 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a` which -identify the atoms :math:`i` and :math:`j`, and Cartesian direction :math:`a` for which -a particular gradient :math:`\frac{\partial {B_{i,k} }}{\partial {r}^a_j}` belongs to. -For the descriptor gradient rows, the first 3 columns contain the indices -:math:`i`, :math:`j`, and :math:`a`, and the remaining columns contain gradients -of different descriptors indexed by :math:`k`. +The number of columns is equal to the number of bispectrum components, +plus 3 additional columns representing the global atom indices :math:`i`, :math:`j`, +and Cartesian direction :math:`a` (0, 1, 2, for x, y, z). The first 3 columns of the first :math:`N` rows belong to the reference potential force components. The first column of the last row, after the first :math:`N + 3N^2` rows, contains the reference potential From b47e5a8d5b79b9780b6ae24940005980c5eca9d4 Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Thu, 7 Jul 2022 13:36:22 -0600 Subject: [PATCH 57/75] Finalized doc page --- doc/src/compute_sna_atom.rst | 24 +- src/ML-SNAP/compute_snap.cpp | 720 ----------------------------------- 2 files changed, 14 insertions(+), 730 deletions(-) delete mode 100644 src/ML-SNAP/compute_snap.cpp diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index 04f9501d1f..f4d2983997 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -394,7 +394,7 @@ If *bikflag* is set to *0* then a single bispectrum row is used, which contains the per-atom bispectrum descriptors :math:`B_{i,k}` summed over all atoms *i* to produce :math:`B_k`. If *bikflag* is set -to *1* this is replaced by a separate bispectrum row equal for each atom. +to *1* this is replaced by a separate per-atom bispectrum row for each atom. In this case, the entries in the final column for these rows are set to zero. @@ -410,8 +410,7 @@ index *j*. This also changes the number of columns to be equal to the number of bispectrum components, with 3 additional columns representing the indices :math:`i`, :math:`j`, and :math:`a`, as explained more in the Output info section below. The option *dgradflag=1* -requires that *bikflag=1*.[APT: This does not make any sense, since dgradflag -produces its own custom output with no bikflag output] +requires that *bikflag=1*. .. note:: @@ -533,10 +532,11 @@ The first :math:`N` rows of the snap array correspond to :math:`B_{i,k}` instead of a single row summed over atoms :math:`i`. In this case, the entries in the final column for these rows are set to zero. Also, each row contains only non-zero entries for the -columns corresponding to the type of that atom. +columns corresponding to the type of that atom. This is not true in the case +of *dgradflag* keyword = 1 (see below). If the *dgradflag* keyword is set to 1, this changes the structure of the -snap array completely. +gloabl array completely. Here the *snad/atom* quantities are replaced with rows corresponding to descriptor gradient components on single atoms: @@ -546,15 +546,19 @@ descriptor gradient components on single atoms: where :math:`{r}^a_j` is the *a-th* position coordinate of the atom with global index *j*. The rows are -organized in chunks, where each chunk corresponds to an atom :math:`j` in the -system of :math:`N` atoms. The rows in an atom :math:`j` chunk correspond to -atoms :math:`i` in the system of :math:`N` atoms. The total number of rows for +organized in chunks, where each chunk corresponds to an atom with global index +:math:`j`. The rows in an atom :math:`j` chunk correspond to +atoms with global index :math:`i`. The total number of rows for these descriptor gradients is therefore :math:`3N^2`. The number of columns is equal to the number of bispectrum components, -plus 3 additional columns representing the global atom indices :math:`i`, :math:`j`, +plus 3 additional left-most columns representing the global atom indices +:math:`i`, :math:`j`, and Cartesian direction :math:`a` (0, 1, 2, for x, y, z). The first 3 columns of the first :math:`N` rows belong to the reference -potential force components. The first column of the last row, after the first +potential force components. The remaining K columns contain the +:math:`B_{i,k}` per-atom descriptors corresponding to the non-zero entries +obtained when *bikflag* = 1. +The first column of the last row, after the first :math:`N + 3N^2` rows, contains the reference potential energy. The virial components are not used with this option. The total number of rows is therefore :math:`N + 3N^2 + 1` and the number of columns is :math:`K + 3`. diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp deleted file mode 100644 index dca3fd3c01..0000000000 --- a/src/ML-SNAP/compute_snap.cpp +++ /dev/null @@ -1,720 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 "compute_snap.h" -#include "sna.h" -#include "atom.h" -#include "update.h" -#include "modify.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "force.h" -#include "pair.h" -#include "comm.h" -#include "memory.h" -#include "error.h" - -#include - -using namespace LAMMPS_NS; - -enum{SCALAR,VECTOR,ARRAY}; - -ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), snap(nullptr), - snapall(nullptr), snap_peratom(nullptr), radelem(nullptr), wjelem(nullptr), - sinnerelem(nullptr), dinnerelem(nullptr), snaptr(nullptr) -{ - - array_flag = 1; - extarray = 0; - - // begin code common to all SNAP computes - - double rfac0, rmin0; - int twojmax, switchflag, bzeroflag, bnormflag, wselfallflag; - - int ntypes = atom->ntypes; - int nargmin = 6 + 2 * ntypes; - - if (narg < nargmin) error->all(FLERR, "Illegal compute {} command", style); - - // default values - - rmin0 = 0.0; - switchflag = 1; - bzeroflag = 1; - quadraticflag = 0; - bikflag = 0; - dgradflag = 0; - chemflag = 0; - bnormflag = 0; - wselfallflag = 0; - switchinnerflag = 0; - nelements = 1; - - // process required arguments - - memory->create(radelem, ntypes + 1, "sna/atom:radelem"); // offset by 1 to match up with types - memory->create(wjelem, ntypes + 1, "sna/atom:wjelem"); - - rcutfac = utils::numeric(FLERR, arg[3], false, lmp); - rfac0 = utils::numeric(FLERR, arg[4], false, lmp); - twojmax = utils::inumeric(FLERR, arg[5], false, lmp); - - for (int i = 0; i < ntypes; i++) - radelem[i + 1] = utils::numeric(FLERR, arg[6 + i], false, lmp); - for (int i = 0; i < ntypes; i++) - wjelem[i + 1] = utils::numeric(FLERR, arg[6 + ntypes + i], false, lmp); - - // construct cutsq - - double cut; - cutmax = 0.0; - memory->create(cutsq, ntypes + 1, ntypes + 1, "sna/atom:cutsq"); - for (int i = 1; i <= ntypes; i++) { - cut = 2.0 * radelem[i] * rcutfac; - if (cut > cutmax) cutmax = cut; - cutsq[i][i] = cut * cut; - for (int j = i + 1; j <= ntypes; j++) { - cut = (radelem[i] + radelem[j]) * rcutfac; - cutsq[i][j] = cutsq[j][i] = cut * cut; - } - } - - // set local input checks - - int sinnerflag = 0; - int dinnerflag = 0; - - // process optional args - - int iarg = nargmin; - - while (iarg < narg) { - if (strcmp(arg[iarg], "rmin0") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - rmin0 = utils::numeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "switchflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - switchflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "bzeroflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - bzeroflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "quadraticflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - quadraticflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "chem") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - chemflag = 1; - memory->create(map, ntypes + 1, "compute_sna_grid:map"); - nelements = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - for (int i = 0; i < ntypes; i++) { - int jelem = utils::inumeric(FLERR, arg[iarg + 2 + i], false, lmp); - if (jelem < 0 || jelem >= nelements) error->all(FLERR, "Illegal compute {} command", style); - map[i + 1] = jelem; - } - iarg += 2 + ntypes; - } else if (strcmp(arg[iarg], "bnormflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - bnormflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "wselfallflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - wselfallflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg], "bikflag") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); - bikflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - iarg += 2; - } else if (strcmp(arg[iarg],"dgradflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - dgradflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); - switchinnerflag = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg], "sinner") == 0) { - iarg++; - if (iarg + ntypes > narg) error->all(FLERR, "Illegal compute {} command", style); - memory->create(sinnerelem, ntypes + 1, "snap:sinnerelem"); - for (int i = 0; i < ntypes; i++) - sinnerelem[i + 1] = utils::numeric(FLERR, arg[iarg + i], false, lmp); - sinnerflag = 1; - iarg += ntypes; - } else if (strcmp(arg[iarg], "dinner") == 0) { - iarg++; - if (iarg + ntypes > narg) error->all(FLERR, "Illegal compute {} command", style); - memory->create(dinnerelem, ntypes + 1, "snap:dinnerelem"); - for (int i = 0; i < ntypes; i++) - dinnerelem[i + 1] = utils::numeric(FLERR, arg[iarg + i], false, lmp); - dinnerflag = 1; - iarg += ntypes; - } else - error->all(FLERR, "Illegal compute {} command", style); - } - - if (switchinnerflag && !(sinnerflag && dinnerflag)) - error->all( - FLERR, - "Illegal compute {} command: switchinnerflag = 1, missing sinner/dinner keyword", - style); - - if (!switchinnerflag && (sinnerflag || dinnerflag)) - error->all( - FLERR, - "Illegal compute {} command: switchinnerflag = 0, unexpected sinner/dinner keyword", - style); - - if (dgradflag && !bikflag) - error->all(FLERR,"Illegal compute snap command: dgradflag=1 requires bikflag=1"); - - if (dgradflag && quadraticflag) - error->all(FLERR,"Illegal compute snap command: dgradflag=1 not implemented for quadratic SNAP"); - - snaptr = new SNA(lmp, rfac0, twojmax, - rmin0, switchflag, bzeroflag, - chemflag, bnormflag, wselfallflag, - nelements, switchinnerflag); - - ncoeff = snaptr->ncoeff; - nvalues = ncoeff; - if (quadraticflag) nvalues += (ncoeff * (ncoeff + 1)) / 2; - - // end code common to all SNAP computes - - ndims_force = 3; - ndims_virial = 6; - yoffset = nvalues; - zoffset = 2 * nvalues; - natoms = atom->natoms; - bik_rows = 1; - if (bikflag) bik_rows = natoms; - dgrad_rows = ndims_force*natoms; - size_array_rows = bik_rows+dgrad_rows + ndims_virial; - if (dgradflag){ - size_array_rows = bik_rows + 3*natoms*natoms + 1; - size_array_cols = nvalues + 3; - error->warning(FLERR,"dgradflag=1 creates a N^2 array, beware of large systems."); - } - else size_array_cols = nvalues*atom->ntypes + 1; - lastcol = size_array_cols-1; - - ndims_peratom = ndims_force; - size_peratom = ndims_peratom*nvalues*atom->ntypes; - - nmax = 0; -} - -/* ---------------------------------------------------------------------- */ - -ComputeSnap::~ComputeSnap() -{ - - memory->destroy(snap); - memory->destroy(snapall); - memory->destroy(snap_peratom); - memory->destroy(radelem); - memory->destroy(wjelem); - memory->destroy(cutsq); - delete snaptr; - if (chemflag) memory->destroy(map); - - if (switchinnerflag) { - memory->destroy(sinnerelem); - memory->destroy(dinnerelem); - } - -} - -/* ---------------------------------------------------------------------- */ - -void ComputeSnap::init() -{ - if (force->pair == nullptr) - error->all(FLERR,"Compute snap requires a pair style be defined"); - - if (cutmax > force->pair->cutforce){ - error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); - } - - // need an occasional full neighbor list - - neighbor->add_request(this, NeighConst::REQ_FULL | NeighConst::REQ_OCCASIONAL); - - if (modify->get_compute_by_style("snap").size() > 1 && comm->me == 0) - error->warning(FLERR,"More than one compute snap"); - snaptr->init(); - - // allocate memory for global array - - memory->create(snap,size_array_rows,size_array_cols, - "snap:snap"); - memory->create(snapall,size_array_rows,size_array_cols, - "snap:snapall"); - array = snapall; - - // find compute for reference energy - - std::string id_pe = std::string("thermo_pe"); - int ipe = modify->find_compute(id_pe); - if (ipe == -1) - error->all(FLERR,"compute thermo_pe does not exist."); - c_pe = modify->compute[ipe]; - - // add compute for reference virial tensor - - std::string id_virial = std::string("snap_press"); - std::string pcmd = id_virial + " all pressure NULL virial"; - modify->add_compute(pcmd); - - int ivirial = modify->find_compute(id_virial); - if (ivirial == -1) - error->all(FLERR,"compute snap_press does not exist."); - c_virial = modify->compute[ivirial]; - -} - - -/* ---------------------------------------------------------------------- */ - -void ComputeSnap::init_list(int /*id*/, NeighList *ptr) -{ - list = ptr; -} - -/* ---------------------------------------------------------------------- */ - -void ComputeSnap::compute_array() -{ - - int ntotal = atom->nlocal + atom->nghost; - - invoked_array = update->ntimestep; - - // grow snap_peratom array if necessary - - if (atom->nmax > nmax) { - memory->destroy(snap_peratom); - nmax = atom->nmax; - memory->create(snap_peratom,nmax,size_peratom, - "snap:snap_peratom"); - } - - // clear global array - - for (int irow = 0; irow < size_array_rows; irow++){ - for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ - snap[irow][icoeff] = 0.0; - } - } - - // clear local peratom array - for (int i = 0; i < ntotal; i++) - for (int icoeff = 0; icoeff < size_peratom; icoeff++) { - snap_peratom[i][icoeff] = 0.0; - } - - // invoke full neighbor list (will copy or build if necessary) - - neighbor->build_one(list); - - const int inum = list->inum; - const int* const ilist = list->ilist; - const int* const numneigh = list->numneigh; - int** const firstneigh = list->firstneigh; - int * const type = atom->type; - - // compute sna derivatives for each atom in group - // use full neighbor list to count atoms less than cutoff - - double** const x = atom->x; - const int* const mask = atom->mask; - int ninside; - int numneigh_sum = 0; - int dgrad_row_indx; - for (int ii = 0; ii < inum; ii++) { - int irow = 0; - if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; - const int i = ilist[ii]; - if (mask[i] & groupbit) { - - const double xtmp = x[i][0]; - const double ytmp = x[i][1]; - const double ztmp = x[i][2]; - const int itype = type[i]; - int ielem = 0; - if (chemflag) - ielem = map[itype]; - const double radi = radelem[itype]; - const int* const jlist = firstneigh[i]; - const int jnum = numneigh[i]; - const int typeoffset_local = ndims_peratom*nvalues*(itype-1); - const int typeoffset_global = nvalues*(itype-1); - - if (dgradflag){ - - // dBi/dRi tags - - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; - - // dBi/dRj tags - - for (int j=0; jtag[i]-1) + 0][0] = atom->tag[i]-1; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = j; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = j; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = j; - snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; - } - } - - // insure rij, inside, and typej are of size jnum - - snaptr->grow_rij(jnum); - - // rij[][3] = displacements between atom I and those neighbors - // inside = indices of neighbors of I within cutoff - // typej = types of neighbors of I within cutoff - // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi - - // assign quantities in snaptr - - ninside=0; - for (int jj = 0; jj < jnum; jj++) { - int j = jlist[jj]; - j &= NEIGHMASK; - - const double delx = x[j][0] - xtmp; - const double dely = x[j][1] - ytmp; - const double delz = x[j][2] - ztmp; - const double rsq = delx*delx + dely*dely + delz*delz; - int jtype = type[j]; - int jelem = 0; - if (chemflag) - jelem = map[jtype]; - if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { - snaptr->rij[ninside][0] = delx; - snaptr->rij[ninside][1] = dely; - snaptr->rij[ninside][2] = delz; - snaptr->inside[ninside] = j; - snaptr->wj[ninside] = wjelem[jtype]; - snaptr->rcutij[ninside] = (radi+radelem[jtype])*rcutfac; - if (switchinnerflag) { - snaptr->sinnerij[ninside] = 0.5*(sinnerelem[itype]+sinnerelem[jtype]); - snaptr->dinnerij[ninside] = 0.5*(dinnerelem[itype]+dinnerelem[jtype]); - } - if (chemflag) snaptr->element[ninside] = jelem; - ninside++; - } - } - - // compute bispectrum for atom i - - snaptr->compute_ui(ninside, ielem); - snaptr->compute_zi(); - snaptr->compute_bi(ielem); - - // loop over neighbors for descriptors derivatives - - for (int jj = 0; jj < ninside; jj++) { - const int j = snaptr->inside[jj]; - - snaptr->compute_duidrj(jj); - snaptr->compute_dbidrj(); - - // accumulate dBi/dRi, -dBi/dRj - - if (!dgradflag) { - - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; - - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - } - - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; - - // diagonal elements of quadratic matrix - - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; - - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; - - ncount++; - - // upper-triangular elements of quadratic matrix - - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; - - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; - - ncount++; - } - } - } - } else { - - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - - // add to snap array for this proc - - // dBi/dRj - - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - - // dBi/dRi - - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; - } - - } - - } // loop over jj inside - - // accumulate Bi - - if (!dgradflag) { - - // linear contributions - - int k = typeoffset_global; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - snap[irow][k++] += snaptr->blist[icoeff]; - - // quadratic contributions - - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } - } - - } else { - int k = 3; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; - } - - } // if (mask[i] & groupbit) - - } // for (int ii = 0; ii < inum; ii++) { - - // accumulate bispectrum force contributions to global array - - if (!dgradflag) { - - for (int itype = 0; itype < atom->ntypes; itype++) { - const int typeoffset_local = ndims_peratom*nvalues*itype; - const int typeoffset_global = nvalues*itype; - for (int icoeff = 0; icoeff < nvalues; icoeff++) { - for (int i = 0; i < ntotal; i++) { - double *snadi = snap_peratom[i]+typeoffset_local; - int iglobal = atom->tag[i]; - int irow = 3*(iglobal-1)+bik_rows; - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; - snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; - snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; - } - } - } - - } - - // accumulate forces to global array - - if (!dgradflag) { - for (int i = 0; i < atom->nlocal; i++) { - int iglobal = atom->tag[i]; - int irow = 3*(iglobal-1)+bik_rows; - snap[irow++][lastcol] = atom->f[i][0]; - snap[irow++][lastcol] = atom->f[i][1]; - snap[irow][lastcol] = atom->f[i][2]; - } - - } else { - - // for dgradflag=1, put forces at first 3 columns of bik rows - - for (int i=0; inlocal; i++){ - int iglobal = atom->tag[i]; - snap[iglobal-1][0+0] = atom->f[i][0]; - snap[iglobal-1][0+1] = atom->f[i][1]; - snap[iglobal-1][0+2] = atom->f[i][2]; - } - } - - // accumulate bispectrum virial contributions to global array - - dbdotr_compute(); - - // sum up over all processes - - MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); - - // assign energy to last column - - if (!dgradflag) { - for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; - int irow = 0; - double reference_energy = c_pe->compute_scalar(); - snapall[irow][lastcol] = reference_energy; - - } else { - - // assign reference energy right after the dgrad rows, first column - - int irow = bik_rows + 3*natoms*natoms; - double reference_energy = c_pe->compute_scalar(); - snapall[irow][0] = reference_energy; - } - - // assign virial stress to last column - // switch to Voigt notation - - if (!dgradflag) { - c_virial->compute_vector(); - int irow = 3*natoms+bik_rows; - snapall[irow++][lastcol] = c_virial->vector[0]; - snapall[irow++][lastcol] = c_virial->vector[1]; - snapall[irow++][lastcol] = c_virial->vector[2]; - snapall[irow++][lastcol] = c_virial->vector[5]; - snapall[irow++][lastcol] = c_virial->vector[4]; - snapall[irow][lastcol] = c_virial->vector[3]; - } - -} - -/* ---------------------------------------------------------------------- - compute global virial contributions via summing r_i.dB^j/dr_i over - own & ghost atoms -------------------------------------------------------------------------- */ - -void ComputeSnap::dbdotr_compute() -{ - - // no virial terms for dgrad yet - - if (dgradflag) return; - - double **x = atom->x; - - int irow0 = bik_rows+ndims_force*natoms; - - // sum over bispectrum contributions to forces - // on all particles including ghosts - - int nall = atom->nlocal + atom->nghost; - for (int i = 0; i < nall; i++) - for (int itype = 0; itype < atom->ntypes; itype++) { - const int typeoffset_local = ndims_peratom*nvalues*itype; - const int typeoffset_global = nvalues*itype; - double *snadi = snap_peratom[i]+typeoffset_local; - for (int icoeff = 0; icoeff < nvalues; icoeff++) { - double dbdx = snadi[icoeff]; - double dbdy = snadi[icoeff+yoffset]; - double dbdz = snadi[icoeff+zoffset]; - int irow = irow0; - snap[irow++][icoeff+typeoffset_global] += dbdx*x[i][0]; - snap[irow++][icoeff+typeoffset_global] += dbdy*x[i][1]; - snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][2]; - snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][1]; - snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][0]; - snap[irow][icoeff+typeoffset_global] += dbdy*x[i][0]; - } - } -} - -/* ---------------------------------------------------------------------- - memory usage -------------------------------------------------------------------------- */ - -double ComputeSnap::memory_usage() -{ - - double bytes = (double)size_array_rows*size_array_cols * - sizeof(double); // snap - bytes += (double)size_array_rows*size_array_cols * - sizeof(double); // snapall - bytes += (double)nmax*size_peratom * sizeof(double); // snap_peratom - bytes += snaptr->memory_usage(); // SNA object - int n = atom->ntypes+1; - bytes += (double)n*sizeof(int); // map - - return bytes; -} From 9aa819d91e6107acb49d601cff703bd99db76103 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 13:46:01 -0600 Subject: [PATCH 58/75] Put compute_snap back --- src/ML-SNAP/compute_snap.cpp | 720 +++++++++++++++++++++++++++++++++++ 1 file changed, 720 insertions(+) create mode 100644 src/ML-SNAP/compute_snap.cpp diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp new file mode 100644 index 0000000000..dca3fd3c01 --- /dev/null +++ b/src/ML-SNAP/compute_snap.cpp @@ -0,0 +1,720 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 "compute_snap.h" +#include "sna.h" +#include "atom.h" +#include "update.h" +#include "modify.h" +#include "neighbor.h" +#include "neigh_list.h" +#include "force.h" +#include "pair.h" +#include "comm.h" +#include "memory.h" +#include "error.h" + +#include + +using namespace LAMMPS_NS; + +enum{SCALAR,VECTOR,ARRAY}; + +ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), snap(nullptr), + snapall(nullptr), snap_peratom(nullptr), radelem(nullptr), wjelem(nullptr), + sinnerelem(nullptr), dinnerelem(nullptr), snaptr(nullptr) +{ + + array_flag = 1; + extarray = 0; + + // begin code common to all SNAP computes + + double rfac0, rmin0; + int twojmax, switchflag, bzeroflag, bnormflag, wselfallflag; + + int ntypes = atom->ntypes; + int nargmin = 6 + 2 * ntypes; + + if (narg < nargmin) error->all(FLERR, "Illegal compute {} command", style); + + // default values + + rmin0 = 0.0; + switchflag = 1; + bzeroflag = 1; + quadraticflag = 0; + bikflag = 0; + dgradflag = 0; + chemflag = 0; + bnormflag = 0; + wselfallflag = 0; + switchinnerflag = 0; + nelements = 1; + + // process required arguments + + memory->create(radelem, ntypes + 1, "sna/atom:radelem"); // offset by 1 to match up with types + memory->create(wjelem, ntypes + 1, "sna/atom:wjelem"); + + rcutfac = utils::numeric(FLERR, arg[3], false, lmp); + rfac0 = utils::numeric(FLERR, arg[4], false, lmp); + twojmax = utils::inumeric(FLERR, arg[5], false, lmp); + + for (int i = 0; i < ntypes; i++) + radelem[i + 1] = utils::numeric(FLERR, arg[6 + i], false, lmp); + for (int i = 0; i < ntypes; i++) + wjelem[i + 1] = utils::numeric(FLERR, arg[6 + ntypes + i], false, lmp); + + // construct cutsq + + double cut; + cutmax = 0.0; + memory->create(cutsq, ntypes + 1, ntypes + 1, "sna/atom:cutsq"); + for (int i = 1; i <= ntypes; i++) { + cut = 2.0 * radelem[i] * rcutfac; + if (cut > cutmax) cutmax = cut; + cutsq[i][i] = cut * cut; + for (int j = i + 1; j <= ntypes; j++) { + cut = (radelem[i] + radelem[j]) * rcutfac; + cutsq[i][j] = cutsq[j][i] = cut * cut; + } + } + + // set local input checks + + int sinnerflag = 0; + int dinnerflag = 0; + + // process optional args + + int iarg = nargmin; + + while (iarg < narg) { + if (strcmp(arg[iarg], "rmin0") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + rmin0 = utils::numeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "switchflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + switchflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "bzeroflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + bzeroflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "quadraticflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + quadraticflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "chem") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + chemflag = 1; + memory->create(map, ntypes + 1, "compute_sna_grid:map"); + nelements = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + for (int i = 0; i < ntypes; i++) { + int jelem = utils::inumeric(FLERR, arg[iarg + 2 + i], false, lmp); + if (jelem < 0 || jelem >= nelements) error->all(FLERR, "Illegal compute {} command", style); + map[i + 1] = jelem; + } + iarg += 2 + ntypes; + } else if (strcmp(arg[iarg], "bnormflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + bnormflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "wselfallflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + wselfallflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg], "bikflag") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal compute {} command", style); + bikflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else if (strcmp(arg[iarg],"dgradflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + dgradflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { + if (iarg+2 > narg) + error->all(FLERR,"Illegal compute snap command"); + switchinnerflag = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg], "sinner") == 0) { + iarg++; + if (iarg + ntypes > narg) error->all(FLERR, "Illegal compute {} command", style); + memory->create(sinnerelem, ntypes + 1, "snap:sinnerelem"); + for (int i = 0; i < ntypes; i++) + sinnerelem[i + 1] = utils::numeric(FLERR, arg[iarg + i], false, lmp); + sinnerflag = 1; + iarg += ntypes; + } else if (strcmp(arg[iarg], "dinner") == 0) { + iarg++; + if (iarg + ntypes > narg) error->all(FLERR, "Illegal compute {} command", style); + memory->create(dinnerelem, ntypes + 1, "snap:dinnerelem"); + for (int i = 0; i < ntypes; i++) + dinnerelem[i + 1] = utils::numeric(FLERR, arg[iarg + i], false, lmp); + dinnerflag = 1; + iarg += ntypes; + } else + error->all(FLERR, "Illegal compute {} command", style); + } + + if (switchinnerflag && !(sinnerflag && dinnerflag)) + error->all( + FLERR, + "Illegal compute {} command: switchinnerflag = 1, missing sinner/dinner keyword", + style); + + if (!switchinnerflag && (sinnerflag || dinnerflag)) + error->all( + FLERR, + "Illegal compute {} command: switchinnerflag = 0, unexpected sinner/dinner keyword", + style); + + if (dgradflag && !bikflag) + error->all(FLERR,"Illegal compute snap command: dgradflag=1 requires bikflag=1"); + + if (dgradflag && quadraticflag) + error->all(FLERR,"Illegal compute snap command: dgradflag=1 not implemented for quadratic SNAP"); + + snaptr = new SNA(lmp, rfac0, twojmax, + rmin0, switchflag, bzeroflag, + chemflag, bnormflag, wselfallflag, + nelements, switchinnerflag); + + ncoeff = snaptr->ncoeff; + nvalues = ncoeff; + if (quadraticflag) nvalues += (ncoeff * (ncoeff + 1)) / 2; + + // end code common to all SNAP computes + + ndims_force = 3; + ndims_virial = 6; + yoffset = nvalues; + zoffset = 2 * nvalues; + natoms = atom->natoms; + bik_rows = 1; + if (bikflag) bik_rows = natoms; + dgrad_rows = ndims_force*natoms; + size_array_rows = bik_rows+dgrad_rows + ndims_virial; + if (dgradflag){ + size_array_rows = bik_rows + 3*natoms*natoms + 1; + size_array_cols = nvalues + 3; + error->warning(FLERR,"dgradflag=1 creates a N^2 array, beware of large systems."); + } + else size_array_cols = nvalues*atom->ntypes + 1; + lastcol = size_array_cols-1; + + ndims_peratom = ndims_force; + size_peratom = ndims_peratom*nvalues*atom->ntypes; + + nmax = 0; +} + +/* ---------------------------------------------------------------------- */ + +ComputeSnap::~ComputeSnap() +{ + + memory->destroy(snap); + memory->destroy(snapall); + memory->destroy(snap_peratom); + memory->destroy(radelem); + memory->destroy(wjelem); + memory->destroy(cutsq); + delete snaptr; + if (chemflag) memory->destroy(map); + + if (switchinnerflag) { + memory->destroy(sinnerelem); + memory->destroy(dinnerelem); + } + +} + +/* ---------------------------------------------------------------------- */ + +void ComputeSnap::init() +{ + if (force->pair == nullptr) + error->all(FLERR,"Compute snap requires a pair style be defined"); + + if (cutmax > force->pair->cutforce){ + error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); + } + + // need an occasional full neighbor list + + neighbor->add_request(this, NeighConst::REQ_FULL | NeighConst::REQ_OCCASIONAL); + + if (modify->get_compute_by_style("snap").size() > 1 && comm->me == 0) + error->warning(FLERR,"More than one compute snap"); + snaptr->init(); + + // allocate memory for global array + + memory->create(snap,size_array_rows,size_array_cols, + "snap:snap"); + memory->create(snapall,size_array_rows,size_array_cols, + "snap:snapall"); + array = snapall; + + // find compute for reference energy + + std::string id_pe = std::string("thermo_pe"); + int ipe = modify->find_compute(id_pe); + if (ipe == -1) + error->all(FLERR,"compute thermo_pe does not exist."); + c_pe = modify->compute[ipe]; + + // add compute for reference virial tensor + + std::string id_virial = std::string("snap_press"); + std::string pcmd = id_virial + " all pressure NULL virial"; + modify->add_compute(pcmd); + + int ivirial = modify->find_compute(id_virial); + if (ivirial == -1) + error->all(FLERR,"compute snap_press does not exist."); + c_virial = modify->compute[ivirial]; + +} + + +/* ---------------------------------------------------------------------- */ + +void ComputeSnap::init_list(int /*id*/, NeighList *ptr) +{ + list = ptr; +} + +/* ---------------------------------------------------------------------- */ + +void ComputeSnap::compute_array() +{ + + int ntotal = atom->nlocal + atom->nghost; + + invoked_array = update->ntimestep; + + // grow snap_peratom array if necessary + + if (atom->nmax > nmax) { + memory->destroy(snap_peratom); + nmax = atom->nmax; + memory->create(snap_peratom,nmax,size_peratom, + "snap:snap_peratom"); + } + + // clear global array + + for (int irow = 0; irow < size_array_rows; irow++){ + for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ + snap[irow][icoeff] = 0.0; + } + } + + // clear local peratom array + for (int i = 0; i < ntotal; i++) + for (int icoeff = 0; icoeff < size_peratom; icoeff++) { + snap_peratom[i][icoeff] = 0.0; + } + + // invoke full neighbor list (will copy or build if necessary) + + neighbor->build_one(list); + + const int inum = list->inum; + const int* const ilist = list->ilist; + const int* const numneigh = list->numneigh; + int** const firstneigh = list->firstneigh; + int * const type = atom->type; + + // compute sna derivatives for each atom in group + // use full neighbor list to count atoms less than cutoff + + double** const x = atom->x; + const int* const mask = atom->mask; + int ninside; + int numneigh_sum = 0; + int dgrad_row_indx; + for (int ii = 0; ii < inum; ii++) { + int irow = 0; + if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; + const int i = ilist[ii]; + if (mask[i] & groupbit) { + + const double xtmp = x[i][0]; + const double ytmp = x[i][1]; + const double ztmp = x[i][2]; + const int itype = type[i]; + int ielem = 0; + if (chemflag) + ielem = map[itype]; + const double radi = radelem[itype]; + const int* const jlist = firstneigh[i]; + const int jnum = numneigh[i]; + const int typeoffset_local = ndims_peratom*nvalues*(itype-1); + const int typeoffset_global = nvalues*(itype-1); + + if (dgradflag){ + + // dBi/dRi tags + + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = atom->tag[i]-1; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + + // dBi/dRj tags + + for (int j=0; jtag[i]-1) + 0][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 1][2] = 1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][0] = atom->tag[i]-1; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][1] = j; + snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 2][2] = 2; + } + } + + // insure rij, inside, and typej are of size jnum + + snaptr->grow_rij(jnum); + + // rij[][3] = displacements between atom I and those neighbors + // inside = indices of neighbors of I within cutoff + // typej = types of neighbors of I within cutoff + // note Rij sign convention => dU/dRij = dU/dRj = -dU/dRi + + // assign quantities in snaptr + + ninside=0; + for (int jj = 0; jj < jnum; jj++) { + int j = jlist[jj]; + j &= NEIGHMASK; + + const double delx = x[j][0] - xtmp; + const double dely = x[j][1] - ytmp; + const double delz = x[j][2] - ztmp; + const double rsq = delx*delx + dely*dely + delz*delz; + int jtype = type[j]; + int jelem = 0; + if (chemflag) + jelem = map[jtype]; + if (rsq < cutsq[itype][jtype]&&rsq>1e-20) { + snaptr->rij[ninside][0] = delx; + snaptr->rij[ninside][1] = dely; + snaptr->rij[ninside][2] = delz; + snaptr->inside[ninside] = j; + snaptr->wj[ninside] = wjelem[jtype]; + snaptr->rcutij[ninside] = (radi+radelem[jtype])*rcutfac; + if (switchinnerflag) { + snaptr->sinnerij[ninside] = 0.5*(sinnerelem[itype]+sinnerelem[jtype]); + snaptr->dinnerij[ninside] = 0.5*(dinnerelem[itype]+dinnerelem[jtype]); + } + if (chemflag) snaptr->element[ninside] = jelem; + ninside++; + } + } + + // compute bispectrum for atom i + + snaptr->compute_ui(ninside, ielem); + snaptr->compute_zi(); + snaptr->compute_bi(ielem); + + // loop over neighbors for descriptors derivatives + + for (int jj = 0; jj < ninside; jj++) { + const int j = snaptr->inside[jj]; + + snaptr->compute_duidrj(jj); + snaptr->compute_dbidrj(); + + // accumulate dBi/dRi, -dBi/dRj + + if (!dgradflag) { + + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; + + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } + + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; + + // diagonal elements of quadratic matrix + + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; + + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; + + ncount++; + + // upper-triangular elements of quadratic matrix + + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; + + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; + + ncount++; + } + } + } + } else { + + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + + // add to snap array for this proc + + // dBi/dRj + + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; + + // dBi/dRi + + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + } + + } + + } // loop over jj inside + + // accumulate Bi + + if (!dgradflag) { + + // linear contributions + + int k = typeoffset_global; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + + // quadratic contributions + + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } + + } else { + int k = 3; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + numneigh_sum += ninside; + } + + } // if (mask[i] & groupbit) + + } // for (int ii = 0; ii < inum; ii++) { + + // accumulate bispectrum force contributions to global array + + if (!dgradflag) { + + for (int itype = 0; itype < atom->ntypes; itype++) { + const int typeoffset_local = ndims_peratom*nvalues*itype; + const int typeoffset_global = nvalues*itype; + for (int icoeff = 0; icoeff < nvalues; icoeff++) { + for (int i = 0; i < ntotal; i++) { + double *snadi = snap_peratom[i]+typeoffset_local; + int iglobal = atom->tag[i]; + int irow = 3*(iglobal-1)+bik_rows; + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff]; + snap[irow++][icoeff+typeoffset_global] += snadi[icoeff+yoffset]; + snap[irow][icoeff+typeoffset_global] += snadi[icoeff+zoffset]; + } + } + } + + } + + // accumulate forces to global array + + if (!dgradflag) { + for (int i = 0; i < atom->nlocal; i++) { + int iglobal = atom->tag[i]; + int irow = 3*(iglobal-1)+bik_rows; + snap[irow++][lastcol] = atom->f[i][0]; + snap[irow++][lastcol] = atom->f[i][1]; + snap[irow][lastcol] = atom->f[i][2]; + } + + } else { + + // for dgradflag=1, put forces at first 3 columns of bik rows + + for (int i=0; inlocal; i++){ + int iglobal = atom->tag[i]; + snap[iglobal-1][0+0] = atom->f[i][0]; + snap[iglobal-1][0+1] = atom->f[i][1]; + snap[iglobal-1][0+2] = atom->f[i][2]; + } + } + + // accumulate bispectrum virial contributions to global array + + dbdotr_compute(); + + // sum up over all processes + + MPI_Allreduce(&snap[0][0],&snapall[0][0],size_array_rows*size_array_cols,MPI_DOUBLE,MPI_SUM,world); + + // assign energy to last column + + if (!dgradflag) { + for (int i = 0; i < bik_rows; i++) snapall[i][lastcol] = 0; + int irow = 0; + double reference_energy = c_pe->compute_scalar(); + snapall[irow][lastcol] = reference_energy; + + } else { + + // assign reference energy right after the dgrad rows, first column + + int irow = bik_rows + 3*natoms*natoms; + double reference_energy = c_pe->compute_scalar(); + snapall[irow][0] = reference_energy; + } + + // assign virial stress to last column + // switch to Voigt notation + + if (!dgradflag) { + c_virial->compute_vector(); + int irow = 3*natoms+bik_rows; + snapall[irow++][lastcol] = c_virial->vector[0]; + snapall[irow++][lastcol] = c_virial->vector[1]; + snapall[irow++][lastcol] = c_virial->vector[2]; + snapall[irow++][lastcol] = c_virial->vector[5]; + snapall[irow++][lastcol] = c_virial->vector[4]; + snapall[irow][lastcol] = c_virial->vector[3]; + } + +} + +/* ---------------------------------------------------------------------- + compute global virial contributions via summing r_i.dB^j/dr_i over + own & ghost atoms +------------------------------------------------------------------------- */ + +void ComputeSnap::dbdotr_compute() +{ + + // no virial terms for dgrad yet + + if (dgradflag) return; + + double **x = atom->x; + + int irow0 = bik_rows+ndims_force*natoms; + + // sum over bispectrum contributions to forces + // on all particles including ghosts + + int nall = atom->nlocal + atom->nghost; + for (int i = 0; i < nall; i++) + for (int itype = 0; itype < atom->ntypes; itype++) { + const int typeoffset_local = ndims_peratom*nvalues*itype; + const int typeoffset_global = nvalues*itype; + double *snadi = snap_peratom[i]+typeoffset_local; + for (int icoeff = 0; icoeff < nvalues; icoeff++) { + double dbdx = snadi[icoeff]; + double dbdy = snadi[icoeff+yoffset]; + double dbdz = snadi[icoeff+zoffset]; + int irow = irow0; + snap[irow++][icoeff+typeoffset_global] += dbdx*x[i][0]; + snap[irow++][icoeff+typeoffset_global] += dbdy*x[i][1]; + snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][2]; + snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][1]; + snap[irow++][icoeff+typeoffset_global] += dbdz*x[i][0]; + snap[irow][icoeff+typeoffset_global] += dbdy*x[i][0]; + } + } +} + +/* ---------------------------------------------------------------------- + memory usage +------------------------------------------------------------------------- */ + +double ComputeSnap::memory_usage() +{ + + double bytes = (double)size_array_rows*size_array_cols * + sizeof(double); // snap + bytes += (double)size_array_rows*size_array_cols * + sizeof(double); // snapall + bytes += (double)nmax*size_peratom * sizeof(double); // snap_peratom + bytes += snaptr->memory_usage(); // SNA object + int n = atom->ntypes+1; + bytes += (double)n*sizeof(int); // map + + return bytes; +} From 79620c5303b0259ca5bf15efe106fe47d3db96de Mon Sep 17 00:00:00 2001 From: Aidan Thompson Date: Thu, 7 Jul 2022 13:53:46 -0600 Subject: [PATCH 59/75] Whitespace --- src/ML-SNAP/compute_snap.cpp | 162 +++++++++++++++++------------------ 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index dca3fd3c01..4c30aa4caa 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -455,89 +455,89 @@ void ComputeSnap::compute_array() // accumulate dBi/dRi, -dBi/dRj - if (!dgradflag) { + if (!dgradflag) { - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - } + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; - // diagonal elements of quadratic matrix + // diagonal elements of quadratic matrix - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; + ncount++; - // upper-triangular elements of quadratic matrix + // upper-triangular elements of quadratic matrix - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; - } - } - } - } else { + ncount++; + } + } + } + } else { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - // add to snap array for this proc + // add to snap array for this proc - // dBi/dRj + // dBi/dRj - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - // dBi/dRi + // dBi/dRi - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; - } + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + } - } + } } // loop over jj inside @@ -545,31 +545,31 @@ void ComputeSnap::compute_array() if (!dgradflag) { - // linear contributions + // linear contributions - int k = typeoffset_global; + int k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - // quadratic contributions + // quadratic contributions - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } - } + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } } else { - int k = 3; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; - } + int k = 3; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + numneigh_sum += ninside; + } } // if (mask[i] & groupbit) From 87acd69b7126643b009d5fa5ad19248818c67160 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 16:44:57 -0600 Subject: [PATCH 60/75] More whitespace fixes --- src/ML-SNAP/compute_snap.cpp | 165 +++++++++++++++++------------------ 1 file changed, 82 insertions(+), 83 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 4c30aa4caa..515af28360 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -399,7 +399,7 @@ void ComputeSnap::compute_array() } } - // insure rij, inside, and typej are of size jnum + // insure rij, inside, and typej are of size jnum snaptr->grow_rij(jnum); @@ -455,89 +455,88 @@ void ComputeSnap::compute_array() // accumulate dBi/dRi, -dBi/dRj - if (!dgradflag) { + if (!dgradflag) { - double *snadi = snap_peratom[i]+typeoffset_local; - double *snadj = snap_peratom[j]+typeoffset_local; + double *snadi = snap_peratom[i]+typeoffset_local; + double *snadj = snap_peratom[j]+typeoffset_local; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - snadi[icoeff] += snaptr->dblist[icoeff][0]; - snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; - snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; + snadi[icoeff] += snaptr->dblist[icoeff][0]; + snadi[icoeff+yoffset] += snaptr->dblist[icoeff][1]; + snadi[icoeff+zoffset] += snaptr->dblist[icoeff][2]; - snadj[icoeff] -= snaptr->dblist[icoeff][0]; - snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; - snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; - } + snadj[icoeff] -= snaptr->dblist[icoeff][0]; + snadj[icoeff+yoffset] -= snaptr->dblist[icoeff][1]; + snadj[icoeff+zoffset] -= snaptr->dblist[icoeff][2]; + } - if (quadraticflag) { - const int quadraticoffset = ncoeff; - snadi += quadraticoffset; - snadj += quadraticoffset; - int ncount = 0; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bi = snaptr->blist[icoeff]; - double bix = snaptr->dblist[icoeff][0]; - double biy = snaptr->dblist[icoeff][1]; - double biz = snaptr->dblist[icoeff][2]; + if (quadraticflag) { + const int quadraticoffset = ncoeff; + snadi += quadraticoffset; + snadj += quadraticoffset; + int ncount = 0; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bi = snaptr->blist[icoeff]; + double bix = snaptr->dblist[icoeff][0]; + double biy = snaptr->dblist[icoeff][1]; + double biz = snaptr->dblist[icoeff][2]; - // diagonal elements of quadratic matrix + // diagonal elements of quadratic matrix - double dbxtmp = bi*bix; - double dbytmp = bi*biy; - double dbztmp = bi*biz; + double dbxtmp = bi*bix; + double dbytmp = bi*biy; + double dbztmp = bi*biz; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; + ncount++; - // upper-triangular elements of quadratic matrix + // upper-triangular elements of quadratic matrix - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double dbxtmp = bi*snaptr->dblist[jcoeff][0] - + bix*snaptr->blist[jcoeff]; - double dbytmp = bi*snaptr->dblist[jcoeff][1] - + biy*snaptr->blist[jcoeff]; - double dbztmp = bi*snaptr->dblist[jcoeff][2] - + biz*snaptr->blist[jcoeff]; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double dbxtmp = bi*snaptr->dblist[jcoeff][0] + + bix*snaptr->blist[jcoeff]; + double dbytmp = bi*snaptr->dblist[jcoeff][1] + + biy*snaptr->blist[jcoeff]; + double dbztmp = bi*snaptr->dblist[jcoeff][2] + + biz*snaptr->blist[jcoeff]; - snadi[ncount] += dbxtmp; - snadi[ncount+yoffset] += dbytmp; - snadi[ncount+zoffset] += dbztmp; - snadj[ncount] -= dbxtmp; - snadj[ncount+yoffset] -= dbytmp; - snadj[ncount+zoffset] -= dbztmp; + snadi[ncount] += dbxtmp; + snadi[ncount+yoffset] += dbytmp; + snadi[ncount+zoffset] += dbztmp; + snadj[ncount] -= dbxtmp; + snadj[ncount+yoffset] -= dbytmp; + snadj[ncount+zoffset] -= dbztmp; - ncount++; - } - } - } - } else { + ncount++; + } + } + } + } else { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - // add to snap array for this proc + // add to snap array for this proc - // dBi/dRj + // dBi/dRj - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - // dBi/dRi + // dBi/dRi - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; - } - - } + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + } + } } // loop over jj inside @@ -545,31 +544,31 @@ void ComputeSnap::compute_array() if (!dgradflag) { - // linear contributions + // linear contributions - int k = typeoffset_global; + int k = typeoffset_global; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - // quadratic contributions + // quadratic contributions - if (quadraticflag) { - for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - double bveci = snaptr->blist[icoeff]; - snap[irow][k++] += 0.5*bveci*bveci; - for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { - double bvecj = snaptr->blist[jcoeff]; - snap[irow][k++] += bveci*bvecj; - } - } - } + if (quadraticflag) { + for (int icoeff = 0; icoeff < ncoeff; icoeff++) { + double bveci = snaptr->blist[icoeff]; + snap[irow][k++] += 0.5*bveci*bveci; + for (int jcoeff = icoeff+1; jcoeff < ncoeff; jcoeff++) { + double bvecj = snaptr->blist[jcoeff]; + snap[irow][k++] += bveci*bvecj; + } + } + } } else { - int k = 3; - for (int icoeff = 0; icoeff < ncoeff; icoeff++) - snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; - } + int k = 3; + for (int icoeff = 0; icoeff < ncoeff; icoeff++) + snap[irow][k++] += snaptr->blist[icoeff]; + numneigh_sum += ninside; + } } // if (mask[i] & groupbit) From fcb60588116499e5e89249f25088a85e5cdb7943 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 16:51:11 -0600 Subject: [PATCH 61/75] More formatting --- src/ML-SNAP/compute_snap.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 515af28360..3200ecd756 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -522,21 +522,21 @@ void ComputeSnap::compute_array() for (int icoeff = 0; icoeff < ncoeff; icoeff++) { - // add to snap array for this proc + // add to snap array for this proc - // dBi/dRj + // dBi/dRj - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] -= snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] -= snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] -= snaptr->dblist[icoeff][2]; - // dBi/dRi + // dBi/dRi - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; - snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][icoeff+3] += snaptr->dblist[icoeff][0]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][icoeff+3] += snaptr->dblist[icoeff][1]; + snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; + } } - } } // loop over jj inside @@ -592,7 +592,6 @@ void ComputeSnap::compute_array() } } } - } // accumulate forces to global array From ae615eb81552b3a342b69b572d93cd840beb736f Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 16:53:20 -0600 Subject: [PATCH 62/75] More whitespace in docs --- doc/src/compute_sna_atom.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index f4d2983997..5efecd6738 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -77,7 +77,7 @@ Syntax *dinnerlist* = *ntypes* values of *Dinner* (distance units) *bikflag* value = *0* or *1* (only implemented for compute snap) *0* = descriptors are summed over atoms of each type - *1* = descriptors are listed separately for each atom + *1* = descriptors are listed separately for each atom *dgradflag* value = *0* or *1* (only implemented for compute snap) *0* = descriptor gradients are summed over atoms of each type *1* = descriptor gradients are listed separately for each atom pair From 5bca3fd0b013955c05f871ec9f1259267b6fbf57 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:00:52 -0600 Subject: [PATCH 63/75] Remove an output file --- examples/mliap/Ta06A.mliap.pytorch.model.pt | Bin 2087 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/mliap/Ta06A.mliap.pytorch.model.pt diff --git a/examples/mliap/Ta06A.mliap.pytorch.model.pt b/examples/mliap/Ta06A.mliap.pytorch.model.pt deleted file mode 100644 index fc2e10398ec6fae4978e37c11ac899615096ef7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2087 zcmah~YfKzf6rSbHP~Nmf(8a2ihhK`>yjTIC9p(ZBYJ2SAmAoeDAZtgwj zeD~b%JjTZ)CQ(#c8nt5NPFXofsRB7nV~YujtjHX`O%pX8$@-!Wry(8E z103Fqk}k>|N8=M3=qDj#1lIT=lg%d9y?kKcAg=~FK~V-Y$TE?$*>yxFYFuALlHzqB zr<2Vh(mq9vG0Zg$^VqytQCLMbZoVllU~`FB(|H|nLHs&fy4IAgV~dDXQutu3N)%+h zsa9xl5mJH?32AVbd050IV_STNBq;vx(6slN2~i?g zwB9SE3z(%1BXFM|?iVaZ9uRUm1?!C)1?w>n3YOcYg5_74V7XGxreIgJNUw||P!TZm z!X?Wt+hLa^%7|C#UDg5*VNW&v zhoKk;<2(lSjFWL3gNKisoHgjs=!dOi=H`)6*ye{O78e%PEAkq6glYlbE7S-UtQ)oq zb&NxY9VXlecp1kCG+R(DI<)$s&4SuF3Xl4sJz|ICF)Jj@2<-AhhaHm6Xh?S3A@SKE z*<**qZ-->BS#P)q`Vg;2RGc;Y7$>_1`>90q=wTP*i1gyXX(s(aG$;lTJl<)hR!~12 zM$m1u)`yc+-%?8hYbGNuKvK#YJi(N(nHW~)f=COfVi@zP!G3%hZJr{WeYi+9=pk3R zd`xOuf@8-!ZJ07LeXDk*EUGE4D^*puc^jAYq__rkhfV8n)61ppqXye>`R3pUM~s^b zp}#McOdGZDAN}$*@2oMWGG{ixjHkQjJ>!>&vz{N%Z~Wot#5vDJmweiB?YhxCUpILC z=Q-ob*usso>#rKOI$A59ublKqP3JS$et*q)#yy^YyJFJg_8z(nH!d1{z0@=Q)dhn; z;WTD`nlVb>dgE)R=&Ipr_;oz%i&cEMM0 zD}UM;Zl3aAN`2P|O%={7-Zu>?>AT23&%I!r?4B=t>G}zyHS=x$&#^;Bam7b}o$_5U zl25;4`F(0=f|_Z;V_W-e@qv|Gwp8k=B!|vIqCoOHcI`F;J$d| z=_{nales!>HnnvLcaXGeMQ-!fuK>P7+!n=~X>#*t6a8HY_*OD~jDsvo|Fqb=${~Wd zvHTCW+&Y#k((R+;Rh*}>)>YNZZDaX}x214rjI(tww~c)f`9DPmsmVoKn?E0ulCsIx ta3)qR>5>LS#^U Date: Thu, 7 Jul 2022 17:04:20 -0600 Subject: [PATCH 64/75] Remove symbolic link directory --- python/lammps/__init__.py | 49 - python/lammps/constants.py | 48 - python/lammps/core.py | 2097 ------------------------------- python/lammps/data.py | 92 -- python/lammps/formats.py | 227 ---- python/lammps/mliap/__init__.py | 20 - python/lammps/mliap/loader.py | 52 - python/lammps/mliap/pytorch.py | 326 ----- python/lammps/numpy_wrapper.py | 483 ------- python/lammps/pylammps.py | 990 --------------- 10 files changed, 4384 deletions(-) delete mode 100644 python/lammps/__init__.py delete mode 100644 python/lammps/constants.py delete mode 100644 python/lammps/core.py delete mode 100644 python/lammps/data.py delete mode 100644 python/lammps/formats.py delete mode 100644 python/lammps/mliap/__init__.py delete mode 100644 python/lammps/mliap/loader.py delete mode 100644 python/lammps/mliap/pytorch.py delete mode 100644 python/lammps/numpy_wrapper.py delete mode 100644 python/lammps/pylammps.py diff --git a/python/lammps/__init__.py b/python/lammps/__init__.py deleted file mode 100644 index fc35e45225..0000000000 --- a/python/lammps/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -LAMMPS module global members: - -.. data:: __version__ - - Numerical representation of the LAMMPS version this - module was taken from. Has the same format as the - result of :py:func:`lammps.version`. -""" - -from .constants import * # lgtm [py/polluting-import] -from .core import * # lgtm [py/polluting-import] -from .data import * # lgtm [py/polluting-import] -from .pylammps import * # lgtm [py/polluting-import] - -# convert installed module string version to numeric version -def get_version_number(): - import time - from os.path import join - from sys import version_info - - # must report 0 when inside LAMMPS source tree - if __file__.find(join('python', 'lammps', '__init__.py')) > 0: - return 0 - - vstring = None - if version_info.major == 3 and version_info.minor >= 8: - from importlib.metadata import version, PackageNotFoundError - try: - vstring = version('lammps') - except PackageNotFoundError: - # nothing to do, ignore - pass - - else: - from pkg_resources import get_distribution, DistributionNotFound - try: - vstring = get_distribution('lammps').version - except DistributionNotFound: - # nothing to do, ignore - pass - - if not vstring: - return 0 - - t = time.strptime(vstring, "%Y.%m.%d") - return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday - -__version__ = get_version_number() diff --git a/python/lammps/constants.py b/python/lammps/constants.py deleted file mode 100644 index a50d58b28f..0000000000 --- a/python/lammps/constants.py +++ /dev/null @@ -1,48 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -# various symbolic constants to be used -# in certain calls to select data formats -LAMMPS_AUTODETECT = None -LAMMPS_INT = 0 -LAMMPS_INT_2D = 1 -LAMMPS_DOUBLE = 2 -LAMMPS_DOUBLE_2D = 3 -LAMMPS_INT64 = 4 -LAMMPS_INT64_2D = 5 -LAMMPS_STRING = 6 - -# these must be kept in sync with the enums in library.h -LMP_STYLE_GLOBAL = 0 -LMP_STYLE_ATOM = 1 -LMP_STYLE_LOCAL = 2 - -LMP_TYPE_SCALAR = 0 -LMP_TYPE_VECTOR = 1 -LMP_TYPE_ARRAY = 2 -LMP_SIZE_VECTOR = 3 -LMP_SIZE_ROWS = 4 -LMP_SIZE_COLS = 5 - -LMP_VAR_EQUAL = 0 -LMP_VAR_ATOM = 1 - -# ------------------------------------------------------------------------- - -def get_ctypes_int(size): - from ctypes import c_int, c_int32, c_int64 - if size == 4: - return c_int32 - elif size == 8: - return c_int64 - return c_int diff --git a/python/lammps/core.py b/python/lammps/core.py deleted file mode 100644 index 930a40a4b0..0000000000 --- a/python/lammps/core.py +++ /dev/null @@ -1,2097 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- -# Python wrapper for the LAMMPS library via ctypes - -# for python2/3 compatibility - -from __future__ import print_function - -import os -import sys -from ctypes import * # lgtm [py/polluting-import] -from os.path import dirname,abspath,join -from inspect import getsourcefile - -from .constants import * # lgtm [py/polluting-import] -from .data import * # lgtm [py/polluting-import] - -# ------------------------------------------------------------------------- - -class MPIAbortException(Exception): - def __init__(self, message): - self.message = message - - def __str__(self): - return repr(self.message) - -# ------------------------------------------------------------------------- - -class ExceptionCheck: - """Utility class to rethrow LAMMPS C++ exceptions as Python exceptions""" - def __init__(self, lmp): - self.lmp = lmp - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_value, traceback): - if self.lmp.has_exceptions and self.lmp.lib.lammps_has_error(self.lmp.lmp): - raise self.lmp._lammps_exception - -# ------------------------------------------------------------------------- - -class lammps(object): - """Create an instance of the LAMMPS Python class. - - .. _mpi4py_docs: https://mpi4py.readthedocs.io/ - - This is a Python wrapper class that exposes the LAMMPS C-library - interface to Python. It either requires that LAMMPS has been compiled - as shared library which is then dynamically loaded via the ctypes - Python module or that this module called from a Python function that - is called from a Python interpreter embedded into a LAMMPS executable, - for example through the :doc:`python invoke ` command. - When the class is instantiated it calls the :cpp:func:`lammps_open` - function of the LAMMPS C-library interface, which in - turn will create an instance of the :cpp:class:`LAMMPS ` - C++ class. The handle to this C++ class is stored internally - and automatically passed to the calls to the C library interface. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - """ - - # ------------------------------------------------------------------------- - # create an instance of LAMMPS - - def __init__(self,name='',cmdargs=None,ptr=None,comm=None): - self.comm = comm - self.opened = 0 - - # determine module file location - - modpath = dirname(abspath(getsourcefile(lambda:0))) - # for windows installers the shared library is in a different folder - winpath = abspath(os.path.join(modpath,'..','..','bin')) - # allow override for running tests on Windows - if (os.environ.get("LAMMPSDLLPATH")): - winpath = os.environ.get("LAMMPSDLLPATH") - self.lib = None - self.lmp = None - - # if a pointer to a LAMMPS object is handed in - # when being called from a Python interpreter - # embedded into a LAMMPS executable, all library - # symbols should already be available so we do not - # load a shared object. - - try: - if ptr is not None: self.lib = CDLL("",RTLD_GLOBAL) - except OSError: - self.lib = None - - # load liblammps.so unless name is given - # if name = "g++", load liblammps_g++.so - # try loading the LAMMPS shared object from the location - # of the lammps package with an absolute path, - # so that LD_LIBRARY_PATH does not need to be set for regular install - # fall back to loading with a relative path, - # typically requires LD_LIBRARY_PATH to be set appropriately - # guess shared library extension based on OS, if not inferred from actual file - - if any([f.startswith('liblammps') and f.endswith('.dylib') - for f in os.listdir(modpath)]): - lib_ext = ".dylib" - elif any([f.startswith('liblammps') and f.endswith('.dll') - for f in os.listdir(modpath)]): - lib_ext = ".dll" - elif os.path.exists(winpath) and any([f.startswith('liblammps') and f.endswith('.dll') - for f in os.listdir(winpath)]): - lib_ext = ".dll" - modpath = winpath - elif any([f.startswith('liblammps') and f.endswith('.so') - for f in os.listdir(modpath)]): - lib_ext = ".so" - else: - import platform - if platform.system() == "Darwin": - lib_ext = ".dylib" - elif platform.system() == "Windows": - lib_ext = ".dll" - else: - lib_ext = ".so" - - if not self.lib: - if name: - libpath = join(modpath,"liblammps_%s" % name + lib_ext) - else: - libpath = join(modpath,"liblammps" + lib_ext) - if not os.path.isfile(libpath): - if name: - libpath = "liblammps_%s" % name + lib_ext - else: - libpath = "liblammps" + lib_ext - self.lib = CDLL(libpath,RTLD_GLOBAL) - - # declare all argument and return types for all library methods here. - # exceptions are where the arguments depend on certain conditions and - # then are defined where the functions are used. - self.lib.lammps_extract_setting.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_setting.restype = c_int - - # set default types - # needed in later declarations - self.c_bigint = get_ctypes_int(self.extract_setting("bigint")) - self.c_tagint = get_ctypes_int(self.extract_setting("tagint")) - self.c_imageint = get_ctypes_int(self.extract_setting("imageint")) - - self.lib.lammps_open.restype = c_void_p - self.lib.lammps_open_no_mpi.restype = c_void_p - self.lib.lammps_close.argtypes = [c_void_p] - self.lib.lammps_flush_buffers.argtypes = [c_void_p] - self.lib.lammps_free.argtypes = [c_void_p] - - self.lib.lammps_file.argtypes = [c_void_p, c_char_p] - self.lib.lammps_file.restype = None - - self.lib.lammps_command.argtypes = [c_void_p, c_char_p] - self.lib.lammps_command.restype = c_char_p - self.lib.lammps_commands_list.restype = None - self.lib.lammps_commands_string.argtypes = [c_void_p, c_char_p] - self.lib.lammps_commands_string.restype = None - - self.lib.lammps_get_natoms.argtypes = [c_void_p] - self.lib.lammps_get_natoms.restype = c_double - self.lib.lammps_extract_box.argtypes = \ - [c_void_p,POINTER(c_double),POINTER(c_double), - POINTER(c_double),POINTER(c_double),POINTER(c_double), - POINTER(c_int),POINTER(c_int)] - self.lib.lammps_extract_box.restype = None - - self.lib.lammps_reset_box.argtypes = \ - [c_void_p,POINTER(c_double),POINTER(c_double),c_double,c_double,c_double] - self.lib.lammps_reset_box.restype = None - - self.lib.lammps_gather_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_atoms.restype = None - - self.lib.lammps_gather_atoms_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_atoms_concat.restype = None - - self.lib.lammps_gather_atoms_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_gather_atoms_subset.restype = None - - self.lib.lammps_scatter_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_scatter_atoms.restype = None - - self.lib.lammps_scatter_atoms_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_scatter_atoms_subset.restype = None - - self.lib.lammps_gather_bonds.argtypes = [c_void_p,c_void_p] - self.lib.lammps_gather_bonds.restype = None - - self.lib.lammps_gather.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather.restype = None - - self.lib.lammps_gather_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_concat.restype = None - - self.lib.lammps_gather_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_gather_subset.restype = None - - self.lib.lammps_scatter.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_scatter.restype = None - - self.lib.lammps_scatter_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_scatter_subset.restype = None - - - self.lib.lammps_find_pair_neighlist.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int] - self.lib.lammps_find_pair_neighlist.restype = c_int - - self.lib.lammps_find_fix_neighlist.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_find_fix_neighlist.restype = c_int - - self.lib.lammps_find_compute_neighlist.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_find_compute_neighlist.restype = c_int - - self.lib.lammps_neighlist_num_elements.argtypes = [c_void_p, c_int] - self.lib.lammps_neighlist_num_elements.restype = c_int - - self.lib.lammps_neighlist_element_neighbors.argtypes = \ - [c_void_p, c_int, c_int, POINTER(c_int), POINTER(c_int), POINTER(POINTER(c_int))] - self.lib.lammps_neighlist_element_neighbors.restype = None - - self.lib.lammps_is_running.argtypes = [c_void_p] - self.lib.lammps_is_running.restype = c_int - - self.lib.lammps_force_timeout.argtypes = [c_void_p] - - self.lib.lammps_has_error.argtypes = [c_void_p] - self.lib.lammps_has_error.restype = c_int - - self.lib.lammps_get_last_error_message.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_get_last_error_message.restype = c_int - - self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_global_datatype.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_global_datatype.restype = c_int - self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int] - - self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p] - self.lib.lammps_get_thermo.restype = c_double - - self.lib.lammps_encode_image_flags.restype = self.c_imageint - - self.lib.lammps_config_package_name.argtypes = [c_int, c_char_p, c_int] - self.lib.lammps_config_accelerator.argtypes = [c_char_p, c_char_p, c_char_p] - - self.lib.lammps_set_variable.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_has_style.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_style_count.argtypes = [c_void_p, c_char_p] - - self.lib.lammps_style_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] - - self.lib.lammps_has_id.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_id_count.argtypes = [c_void_p, c_char_p] - - self.lib.lammps_id_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] - - self.lib.lammps_plugin_count.argtypes = [ ] - self.lib.lammps_plugin_name.argtypes = [c_int, c_char_p, c_char_p, c_int] - - self.lib.lammps_version.argtypes = [c_void_p] - - self.lib.lammps_get_os_info.argtypes = [c_char_p, c_int] - self.lib.lammps_get_gpu_device_info.argtypes = [c_char_p, c_int] - - self.lib.lammps_get_mpi_comm.argtypes = [c_void_p] - - self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)] - - self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_atom_datatype.restype = c_int - - self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] - - self.lib.lammps_extract_variable.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_fix_external_get_force.argtypes = [c_void_p, c_char_p] - self.lib.lammps_fix_external_get_force.restype = POINTER(POINTER(c_double)) - - self.lib.lammps_fix_external_set_energy_global.argtypes = [c_void_p, c_char_p, c_double] - self.lib.lammps_fix_external_set_virial_global.argtypes = [c_void_p, c_char_p, POINTER(c_double)] - self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] - self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] - - self.lib.lammps_fix_external_set_vector_length.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_fix_external_set_vector.argtypes = [c_void_p, c_char_p, c_int, c_double] - - # detect if Python is using a version of mpi4py that can pass communicators - # only needed if LAMMPS has been compiled with MPI support. - self.has_mpi4py = False - if self.has_mpi_support: - try: - from mpi4py import __version__ as mpi4py_version - # tested to work with mpi4py versions 2 and 3 - self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3'] - except ImportError: - # ignore failing import - pass - - # if no ptr provided, create an instance of LAMMPS - # we can pass an MPI communicator from mpi4py v2.0.0 and later - # no_mpi call lets LAMMPS use MPI_COMM_WORLD - # cargs = array of C strings from args - # if ptr, then are embedding Python in LAMMPS input script - # ptr is the desired instance of LAMMPS - # just convert it to ctypes ptr and store in self.lmp - - if ptr is None: - - # with mpi4py v2+, we can pass MPI communicators to LAMMPS - # need to adjust for type of MPI communicator object - # allow for int (like MPICH) or void* (like OpenMPI) - if self.has_mpi_support and self.has_mpi4py: - from mpi4py import MPI - self.MPI = MPI - - if comm is not None: - if not self.has_mpi_support: - raise Exception('LAMMPS not compiled with real MPI library') - if not self.has_mpi4py: - raise Exception('Python mpi4py version is not 2 or 3') - if self.MPI._sizeof(self.MPI.Comm) == sizeof(c_int): - MPI_Comm = c_int - else: - MPI_Comm = c_void_p - - # Detect whether LAMMPS and mpi4py definitely use different MPI libs - if sizeof(MPI_Comm) != self.lib.lammps_config_has_mpi_support(): - raise Exception('Inconsistent MPI library in LAMMPS and mpi4py') - - narg = 0 - cargs = None - if cmdargs is not None: - cmdargs.insert(0,"lammps") - narg = len(cmdargs) - for i in range(narg): - if type(cmdargs[i]) is str: - cmdargs[i] = cmdargs[i].encode() - cargs = (c_char_p*narg)(*cmdargs) - self.lib.lammps_open.argtypes = [c_int, c_char_p*narg, MPI_Comm, c_void_p] - else: - self.lib.lammps_open.argtypes = [c_int, c_char_p, MPI_Comm, c_void_p] - - self.opened = 1 - comm_ptr = self.MPI._addressof(comm) - comm_val = MPI_Comm.from_address(comm_ptr) - self.lmp = c_void_p(self.lib.lammps_open(narg,cargs,comm_val,None)) - - else: - if self.has_mpi4py and self.has_mpi_support: - self.comm = self.MPI.COMM_WORLD - self.opened = 1 - if cmdargs is not None: - cmdargs.insert(0,"lammps") - narg = len(cmdargs) - for i in range(narg): - if type(cmdargs[i]) is str: - cmdargs[i] = cmdargs[i].encode() - cargs = (c_char_p*narg)(*cmdargs) - self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p*narg, c_void_p] - self.lmp = c_void_p(self.lib.lammps_open_no_mpi(narg,cargs,None)) - else: - self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p, c_void_p] - self.lmp = c_void_p(self.lib.lammps_open_no_mpi(0,None,None)) - - else: - # magic to convert ptr to ctypes ptr - if sys.version_info >= (3, 0): - # Python 3 (uses PyCapsule API) - pythonapi.PyCapsule_GetPointer.restype = c_void_p - pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] - self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None)) - else: - # Python 2 (uses PyCObject API) - pythonapi.PyCObject_AsVoidPtr.restype = c_void_p - pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] - self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) - - # check if library initilialization failed - if not self.lmp: - raise(RuntimeError("Failed to initialize LAMMPS object")) - - # optional numpy support (lazy loading) - self._numpy = None - - self._installed_packages = None - self._available_styles = None - - # check if liblammps version matches the installed python module version - # but not for in-place usage, i.e. when the version is 0 - import lammps - if lammps.__version__ > 0 and lammps.__version__ != self.lib.lammps_version(self.lmp): - raise(AttributeError("LAMMPS Python module installed for LAMMPS version %d, but shared library is version %d" \ - % (lammps.__version__, self.lib.lammps_version(self.lmp)))) - - # add way to insert Python callback for fix external - self.callback = {} - self.FIX_EXTERNAL_CALLBACK_FUNC = CFUNCTYPE(None, py_object, self.c_bigint, c_int, POINTER(self.c_tagint), POINTER(POINTER(c_double)), POINTER(POINTER(c_double))) - self.lib.lammps_set_fix_external_callback.argtypes = [c_void_p, c_char_p, self.FIX_EXTERNAL_CALLBACK_FUNC, py_object] - self.lib.lammps_set_fix_external_callback.restype = None - - # ------------------------------------------------------------------------- - # shut-down LAMMPS instance - - def __del__(self): - self.close() - - # ------------------------------------------------------------------------- - # context manager implementation - - def __enter__(self): - return self - - def __exit__(self, ex_type, ex_value, ex_traceback): - self.close() - - # ------------------------------------------------------------------------- - - @property - def numpy(self): - """ Return object to access numpy versions of API - - It provides alternative implementations of API functions that - return numpy arrays instead of ctypes pointers. If numpy is not installed, - accessing this property will lead to an ImportError. - - :return: instance of numpy wrapper object - :rtype: numpy_wrapper - """ - if not self._numpy: - from .numpy_wrapper import numpy_wrapper - self._numpy = numpy_wrapper(self) - return self._numpy - - # ------------------------------------------------------------------------- - - def close(self): - """Explicitly delete a LAMMPS instance through the C-library interface. - - This is a wrapper around the :cpp:func:`lammps_close` function of the C-library interface. - """ - if self.lmp and self.opened: - self.lib.lammps_close(self.lmp) - self.lmp = None - self.opened = 0 - - # ------------------------------------------------------------------------- - - 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`. - - You cannot create or use any LAMMPS instances after this function is called - unless LAMMPS was compiled without MPI and without Kokkos support. - """ - self.close() - self.lib.lammps_kokkos_finalize() - self.lib.lammps_mpi_finalize() - - # ------------------------------------------------------------------------- - - def version(self): - """Return a numerical representation of the LAMMPS version in use. - - This is a wrapper around the :cpp:func:`lammps_version` function of the C-library interface. - - :return: version number - :rtype: int - """ - return self.lib.lammps_version(self.lmp) - - # ------------------------------------------------------------------------- - - def get_os_info(self): - """Return a string with information about the OS and compiler runtime - - This is a wrapper around the :cpp:func:`lammps_get_os_info` function of the C-library interface. - - :return: OS info string - :rtype: string - """ - - sb = create_string_buffer(512) - self.lib.lammps_get_os_info(sb,512) - return sb.value.decode() - - # ------------------------------------------------------------------------- - - def get_mpi_comm(self): - """Get the MPI communicator in use by the current LAMMPS instance - - This is a wrapper around the :cpp:func:`lammps_get_mpi_comm` function - of the C-library interface. It will return ``None`` if either the - LAMMPS library was compiled without MPI support or the mpi4py - Python module is not available. - - :return: MPI communicator - :rtype: MPI_Comm - """ - - if self.has_mpi4py and self.has_mpi_support: - from mpi4py import MPI - f_comm = self.lib.lammps_get_mpi_comm(self.lmp) - c_comm = MPI.Comm.f2py(f_comm) - return c_comm - else: - return None - - # ------------------------------------------------------------------------- - - @property - def _lammps_exception(self): - sb = create_string_buffer(100) - error_type = self.lib.lammps_get_last_error_message(self.lmp, sb, 100) - error_msg = sb.value.decode().strip() - - if error_type == 2: - return MPIAbortException(error_msg) - return Exception(error_msg) - - # ------------------------------------------------------------------------- - - def file(self, path): - """Read LAMMPS commands from a file. - - This is a wrapper around the :cpp:func:`lammps_file` function of the C-library interface. - It will open the file with the name/path `file` and process the LAMMPS commands line by line until - the end. The function will return when the end of the file is reached. - - :param path: Name of the file/path with LAMMPS commands - :type path: string - """ - if path: path = path.encode() - else: return - - with ExceptionCheck(self): - self.lib.lammps_file(self.lmp, path) - - # ------------------------------------------------------------------------- - - def command(self,cmd): - """Process a single LAMMPS input command from a string. - - This is a wrapper around the :cpp:func:`lammps_command` - function of the C-library interface. - - :param cmd: a single lammps command - :type cmd: string - """ - if cmd: cmd = cmd.encode() - else: return - - with ExceptionCheck(self): - self.lib.lammps_command(self.lmp,cmd) - - # ------------------------------------------------------------------------- - - def commands_list(self,cmdlist): - """Process multiple LAMMPS input commands from a list of strings. - - This is a wrapper around the - :cpp:func:`lammps_commands_list` function of - the C-library interface. - - :param cmdlist: a single lammps command - :type cmdlist: list of strings - """ - cmds = [x.encode() for x in cmdlist if type(x) is str] - narg = len(cmdlist) - args = (c_char_p * narg)(*cmds) - self.lib.lammps_commands_list.argtypes = [c_void_p, c_int, c_char_p * narg] - - with ExceptionCheck(self): - self.lib.lammps_commands_list(self.lmp,narg,args) - - # ------------------------------------------------------------------------- - - def commands_string(self,multicmd): - """Process a block of LAMMPS input commands from a string. - - This is a wrapper around the - :cpp:func:`lammps_commands_string` - function of the C-library interface. - - :param multicmd: text block of lammps commands - :type multicmd: string - """ - if type(multicmd) is str: multicmd = multicmd.encode() - - with ExceptionCheck(self): - self.lib.lammps_commands_string(self.lmp,c_char_p(multicmd)) - - # ------------------------------------------------------------------------- - - def get_natoms(self): - """Get the total number of atoms in the LAMMPS instance. - - Will be precise up to 53-bit signed integer due to the - underlying :cpp:func:`lammps_get_natoms` function returning a double. - - :return: number of atoms - :rtype: int - """ - return int(self.lib.lammps_get_natoms(self.lmp)) - - # ------------------------------------------------------------------------- - - def extract_box(self): - """Extract simulation box parameters - - This is a wrapper around the :cpp:func:`lammps_extract_box` function - of the C-library interface. Unlike in the C function, the result is - returned as a list. - - :return: list of the extracted data: boxlo, boxhi, xy, yz, xz, periodicity, box_change - :rtype: [ 3*double, 3*double, double, double, 3*int, int] - """ - boxlo = (3*c_double)() - boxhi = (3*c_double)() - xy = c_double() - yz = c_double() - xz = c_double() - periodicity = (3*c_int)() - box_change = c_int() - - with ExceptionCheck(self): - self.lib.lammps_extract_box(self.lmp,boxlo,boxhi, - byref(xy),byref(yz),byref(xz), - periodicity,byref(box_change)) - - boxlo = boxlo[:3] - boxhi = boxhi[:3] - xy = xy.value - yz = yz.value - xz = xz.value - periodicity = periodicity[:3] - box_change = box_change.value - - return boxlo,boxhi,xy,yz,xz,periodicity,box_change - - # ------------------------------------------------------------------------- - - def reset_box(self,boxlo,boxhi,xy,yz,xz): - """Reset simulation box parameters - - This is a wrapper around the :cpp:func:`lammps_reset_box` function - of the C-library interface. - - :param boxlo: new lower box boundaries - :type boxlo: list of 3 floating point numbers - :param boxhi: new upper box boundaries - :type boxhi: list of 3 floating point numbers - :param xy: xy tilt factor - :type xy: float - :param yz: yz tilt factor - :type yz: float - :param xz: xz tilt factor - :type xz: float - """ - cboxlo = (3*c_double)(*boxlo) - cboxhi = (3*c_double)(*boxhi) - with ExceptionCheck(self): - self.lib.lammps_reset_box(self.lmp,cboxlo,cboxhi,xy,yz,xz) - - # ------------------------------------------------------------------------- - - def get_thermo(self,name): - """Get current value of a thermo keyword - - This is a wrapper around the :cpp:func:`lammps_get_thermo` - function of the C-library interface. - - :param name: name of thermo keyword - :type name: string - :return: value of thermo keyword - :rtype: double or None - """ - if name: name = name.encode() - else: return None - - with ExceptionCheck(self): - return self.lib.lammps_get_thermo(self.lmp,name) - - # ------------------------------------------------------------------------- - - def extract_setting(self, name): - """Query LAMMPS about global settings that can be expressed as an integer. - - This is a wrapper around the :cpp:func:`lammps_extract_setting` - function of the C-library interface. Its documentation includes - a list of the supported keywords. - - :param name: name of the setting - :type name: string - :return: value of the setting - :rtype: int - """ - if name: name = name.encode() - else: return None - return int(self.lib.lammps_extract_setting(self.lmp,name)) - - # ------------------------------------------------------------------------- - # extract global info datatype - - def extract_global_datatype(self, name): - """Retrieve global property datatype from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_global_datatype` - function of the C-library interface. Its documentation includes a - list of the supported keywords. - This function returns ``None`` if the keyword is not - recognized. Otherwise it will return a positive integer value that - corresponds to one of the :ref:`data type ` - constants define in the :py:mod:`lammps` module. - - :param name: name of the property - :type name: string - :return: data type of global property, see :ref:`py_datatype_constants` - :rtype: int - """ - if name: name = name.encode() - else: return None - return self.lib.lammps_extract_global_datatype(self.lmp, name) - - # ------------------------------------------------------------------------- - # extract global info - - def extract_global(self, name, dtype=LAMMPS_AUTODETECT): - """Query LAMMPS about global settings of different types. - - This is a wrapper around the :cpp:func:`lammps_extract_global` function - of the C-library interface. Since there are no pointers in Python, this - method will - unlike the C function - return the value or a list of - values. The :cpp:func:`lammps_extract_global` documentation includes a - list of the supported keywords and their data types. - Since Python needs to know the data type to be able to interpret - the result, by default, this function will try to auto-detect the data type - by asking the library. You can also force a specific data type. For that - purpose the :py:mod:`lammps` module contains :ref:`data type ` - constants. This function returns ``None`` if either the keyword is not recognized, - or an invalid data type constant is used. - - :param name: name of the property - :type name: string - :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :return: value of the property or list of values or None - :rtype: int, float, list, or NoneType - """ - - if dtype == LAMMPS_AUTODETECT: - dtype = self.extract_global_datatype(name) - - # set length of vector for items that are not a scalar - vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3, - 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } - if name in vec_dict: - veclen = vec_dict[name] - elif name == 'respa_dt': - veclen = self.extract_global('respa_levels',LAMMPS_INT) - else: - veclen = 1 - - if name: name = name.encode() - else: return None - - if dtype == LAMMPS_INT: - self.lib.lammps_extract_global.restype = POINTER(c_int32) - target_type = int - elif dtype == LAMMPS_INT64: - self.lib.lammps_extract_global.restype = POINTER(c_int64) - target_type = int - elif dtype == LAMMPS_DOUBLE: - self.lib.lammps_extract_global.restype = POINTER(c_double) - target_type = float - elif dtype == LAMMPS_STRING: - self.lib.lammps_extract_global.restype = c_char_p - target_type = str - else: - target_type = None - - ptr = self.lib.lammps_extract_global(self.lmp, name) - if ptr: - if dtype == LAMMPS_STRING: - return ptr.decode('utf-8') - if veclen > 1: - result = [] - for i in range(0,veclen): - result.append(target_type(ptr[i])) - return result - else: return target_type(ptr[0]) - return None - - # ------------------------------------------------------------------------- - # extract per-atom info datatype - - def extract_atom_datatype(self, name): - """Retrieve per-atom property datatype from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_atom_datatype` - function of the C-library interface. Its documentation includes a - list of the supported keywords. - This function returns ``None`` if the keyword is not - recognized. Otherwise it will return an integer value that - corresponds to one of the :ref:`data type ` constants - defined in the :py:mod:`lammps` module. - - :param name: name of the property - :type name: string - :return: data type of per-atom property (see :ref:`py_datatype_constants`) - :rtype: int - """ - if name: name = name.encode() - else: return None - return self.lib.lammps_extract_atom_datatype(self.lmp, name) - - # ------------------------------------------------------------------------- - # extract per-atom info - - def extract_atom(self, name, dtype=LAMMPS_AUTODETECT): - """Retrieve per-atom properties from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_atom` - function of the C-library interface. Its documentation includes a - list of the supported keywords and their data types. - Since Python needs to know the data type to be able to interpret - the result, by default, this function will try to auto-detect the data type - by asking the library. You can also force a specific data type by setting ``dtype`` - to one of the :ref:`data type ` constants defined in the - :py:mod:`lammps` module. - This function returns ``None`` if either the keyword is not - recognized, or an invalid data type constant is used. - - .. note:: - - While the returned arrays of per-atom data are dimensioned - for the range [0:nmax] - as is the underlying storage - - the data is usually only valid for the range of [0:nlocal], - unless the property of interest is also updated for ghost - atoms. In some cases, this depends on a LAMMPS setting, see - for example :doc:`comm_modify vel yes `. - - :param name: name of the property - :type name: string - :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :return: requested data or ``None`` - :rtype: ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.POINTER(ctypes.c_int32)), - ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - or NoneType - """ - if dtype == LAMMPS_AUTODETECT: - dtype = self.extract_atom_datatype(name) - - if name: name = name.encode() - else: return None - - if dtype == LAMMPS_INT: - self.lib.lammps_extract_atom.restype = POINTER(c_int32) - elif dtype == LAMMPS_INT_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int32)) - elif dtype == LAMMPS_DOUBLE: - self.lib.lammps_extract_atom.restype = POINTER(c_double) - elif dtype == LAMMPS_DOUBLE_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double)) - elif dtype == LAMMPS_INT64: - self.lib.lammps_extract_atom.restype = POINTER(c_int64) - elif dtype == LAMMPS_INT64_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64)) - else: return None - - ptr = self.lib.lammps_extract_atom(self.lmp, name) - if ptr: return ptr - else: return None - - - # ------------------------------------------------------------------------- - - def extract_compute(self,cid,cstyle,ctype): - """Retrieve data from a LAMMPS compute - - This is a wrapper around the :cpp:func:`lammps_extract_compute` - function of the C-library interface. - This function returns ``None`` if either the compute id is not - recognized, or an invalid combination of :ref:`cstyle ` - and :ref:`ctype ` constants is used. The - names and functionality of the constants are the same as for - the corresponding C-library function. For requests to return - a scalar or a size, the value is returned, otherwise a pointer. - - :param cid: compute ID - :type cid: string - :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type cstyle: int - :param ctype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ctype: int - :return: requested data as scalar, pointer to 1d or 2d double array, or None - :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType - """ - if cid: cid = cid.encode() - else: return None - - if ctype == LMP_TYPE_SCALAR: - if cstyle == LMP_STYLE_GLOBAL: - self.lib.lammps_extract_compute.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - elif cstyle == LMP_STYLE_ATOM: - return None - elif cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - elif ctype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_compute.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr - - elif ctype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_compute.restype = POINTER(POINTER(c_double)) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr - - elif ctype == LMP_SIZE_COLS: - if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_ATOM or cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - elif ctype == LMP_SIZE_VECTOR or ctype == LMP_SIZE_ROWS: - if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - return None - - # ------------------------------------------------------------------------- - # extract fix info - # in case of global data, free memory for 1 double via lammps_free() - # double was allocated by library interface function - - def extract_fix(self,fid,fstyle,ftype,nrow=0,ncol=0): - """Retrieve data from a LAMMPS fix - - This is a wrapper around the :cpp:func:`lammps_extract_fix` - function of the C-library interface. - This function returns ``None`` if either the fix id is not - recognized, or an invalid combination of :ref:`fstyle ` - and :ref:`ftype ` constants is used. The - names and functionality of the constants are the same as for - the corresponding C-library function. For requests to return - a scalar or a size, the value is returned, also when accessing - global vectors or arrays, otherwise a pointer. - - :param fid: fix ID - :type fid: string - :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type fstyle: int - :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ftype: int - :param nrow: index of global vector element or row index of global array element - :type nrow: int - :param ncol: column index of global array element - :type ncol: int - :return: requested data or None - :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType - - """ - if fid: fid = fid.encode() - else: return None - - if fstyle == LMP_STYLE_GLOBAL: - if ftype in (LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): - self.lib.lammps_extract_fix.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - result = ptr[0] - self.lib.lammps_free(ptr) - return result - elif ftype in (LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): - self.lib.lammps_extract_fix.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - return ptr[0] - else: - return None - - elif fstyle == LMP_STYLE_ATOM: - if ftype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_fix.restype = POINTER(c_double) - elif ftype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) - elif ftype == LMP_SIZE_COLS: - self.lib.lammps_extract_fix.restype = POINTER(c_int) - else: - return None - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - if ftype == LMP_SIZE_COLS: - return ptr[0] - else: - return ptr - - elif fstyle == LMP_STYLE_LOCAL: - if ftype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_fix.restype = POINTER(c_double) - elif ftype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) - elif ftype in (LMP_TYPE_SCALAR, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): - self.lib.lammps_extract_fix.restype = POINTER(c_int) - else: - return None - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - if ftype in (LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): - return ptr - else: - return ptr[0] - else: - return None - - # ------------------------------------------------------------------------- - # extract variable info - # free memory for 1 double or 1 vector of doubles via lammps_free() - # for vector, must copy nlocal returned values to local c_double vector - # memory was allocated by library interface function - - def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): - """ Evaluate a LAMMPS variable and return its data - - This function is a wrapper around the function - :cpp:func:`lammps_extract_variable` of the C-library interface, - evaluates variable name and returns a copy of the computed data. - The memory temporarily allocated by the C-interface is deleted - after the data is copied to a Python variable or list. - The variable must be either an equal-style (or equivalent) - variable or an atom-style variable. The variable type has to - provided as ``vartype`` parameter which may be one of two constants: - ``LMP_VAR_EQUAL`` or ``LMP_VAR_ATOM``; it defaults to - equal-style variables. - The group parameter is only used for atom-style variables and - defaults to the group "all" if set to ``None``, which is the default. - - :param name: name of the variable to execute - :type name: string - :param group: name of group for atom-style variable - :type group: string, only for atom-style variables - :param vartype: type of variable, see :ref:`py_vartype_constants` - :type vartype: int - :return: the requested data - :rtype: c_double, (c_double), or NoneType - """ - if name: name = name.encode() - else: return None - if group: group = group.encode() - if vartype == LMP_VAR_EQUAL: - self.lib.lammps_extract_variable.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_variable(self.lmp,name,group) - if ptr: result = ptr[0] - else: return None - self.lib.lammps_free(ptr) - return result - elif vartype == LMP_VAR_ATOM: - nlocal = self.extract_global("nlocal") - result = (c_double*nlocal)() - self.lib.lammps_extract_variable.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_variable(self.lmp,name,group) - if ptr: - for i in range(nlocal): result[i] = ptr[i] - self.lib.lammps_free(ptr) - else: return None - return result - return None - - # ------------------------------------------------------------------------- - - def flush_buffers(self): - """Flush output buffers - - This is a wrapper around the :cpp:func:`lammps_flush_buffers` - function of the C-library interface. - """ - self.lib.lammps_flush_buffers(self.lmp) - - # ------------------------------------------------------------------------- - - def set_variable(self,name,value): - """Set a new value for a LAMMPS string style variable - - This is a wrapper around the :cpp:func:`lammps_set_variable` - function of the C-library interface. - - :param name: name of the variable - :type name: string - :param value: new variable value - :type value: any. will be converted to a string - :return: either 0 on success or -1 on failure - :rtype: int - """ - if name: name = name.encode() - else: return -1 - if value: value = str(value).encode() - else: return -1 - with ExceptionCheck(self): - return self.lib.lammps_set_variable(self.lmp,name,value) - - # ------------------------------------------------------------------------- - - # return vector of atom properties gathered across procs - # 3 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # dtype = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # returned data is a 1d vector - doc how it is ordered? - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def gather_atoms(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) - else: - return None - return data - - # ------------------------------------------------------------------------- - - def gather_atoms_concat(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_atoms_subset(self,name,dtype,count,ndata,ids): - if name: name = name.encode() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*ndata)*c_int)() - self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - elif dtype == 1: - data = ((count*ndata)*c_double)() - self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - else: - return None - return data - - # ------------------------------------------------------------------------- - - # scatter vector of atom properties across procs - # 2 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # assume data is of correct type and length, as created by gather_atoms() - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def scatter_atoms(self,name,dtype,count,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_atoms(self.lmp,name,dtype,count,data) - - # ------------------------------------------------------------------------- - - def scatter_atoms_subset(self,name,dtype,count,ndata,ids,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - - - # ------------------------------------------------------------------------- - - def gather_bonds(self): - """Retrieve global list of bonds - - This is a wrapper around the :cpp:func:`lammps_gather_bonds` - function of the C-library interface. - - This function returns a tuple with the number of bonds and a - flat list of ctypes integer values with the bond type, bond atom1, - bond atom2 for each bond. - - .. versionadded:: 28Jul2021 - - :return: a tuple with the number of bonds and a list of c_int or c_long - :rtype: (int, 3*nbonds*c_tagint) - """ - nbonds = self.extract_global("nbonds") - with ExceptionCheck(self): - data = ((3*nbonds)*self.c_tagint)() - self.lib.lammps_gather_bonds(self.lmp,data) - return nbonds,data - - # ------------------------------------------------------------------------- - - # return vector of atom/compute/fix properties gathered across procs - # 3 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # returned data is a 1d vector - doc how it is ordered? - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - def gather(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_concat(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_subset(self,name,dtype,count,ndata,ids): - if name: name = name.encode() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*ndata)*c_int)() - self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) - elif dtype == 1: - data = ((count*ndata)*c_double)() - self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) - else: - return None - return data - - # scatter vector of atom/compute/fix properties across procs - # 2 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # assume data is of correct type and length, as created by gather_atoms() - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def scatter(self,name,dtype,count,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter(self.lmp,name,dtype,count,data) - - def scatter_subset(self,name,dtype,count,ndata,ids,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_subset(self.lmp,name,dtype,count,ndata,ids,data) - - # ------------------------------------------------------------------------- - - def encode_image_flags(self,ix,iy,iz): - """ convert 3 integers with image flags for x-, y-, and z-direction - into a single integer like it is used internally in LAMMPS - - This method is a wrapper around the :cpp:func:`lammps_encode_image_flags` - function of library interface. - - :param ix: x-direction image flag - :type ix: int - :param iy: y-direction image flag - :type iy: int - :param iz: z-direction image flag - :type iz: int - :return: encoded image flags - :rtype: lammps.c_imageint - """ - return self.lib.lammps_encode_image_flags(ix,iy,iz) - - # ------------------------------------------------------------------------- - - def decode_image_flags(self,image): - """ Convert encoded image flag integer into list of three regular integers. - - This method is a wrapper around the :cpp:func:`lammps_decode_image_flags` - function of library interface. - - :param image: encoded image flags - :type image: lammps.c_imageint - :return: list of three image flags in x-, y-, and z- direction - :rtype: list of 3 int - """ - - flags = (c_int*3)() - self.lib.lammps_decode_image_flags(image,byref(flags)) - - return [int(i) for i in flags] - - # ------------------------------------------------------------------------- - - # create N atoms on all procs - # N = global number of atoms - # id = ID of each atom (optional, can be None) - # type = type of each atom (1 to Ntypes) (required) - # x = coords of each atom as (N,3) array (required) - # v = velocity of each atom as (N,3) array (optional, can be None) - # NOTE: how could we insure are passing correct type to LAMMPS - # e.g. for Python list or NumPy, etc - # ditto for gather_atoms() above - - def create_atoms(self,n,id,type,x,v=None,image=None,shrinkexceed=False): - """ - Create N atoms from list of coordinates and properties - - This function is a wrapper around the :cpp:func:`lammps_create_atoms` - function of the C-library interface, and the behavior is similar except - that the *v*, *image*, and *shrinkexceed* arguments are optional and - default to *None*, *None*, and *False*, respectively. With *None* being - equivalent to a ``NULL`` pointer in C. - - The lists of coordinates, types, atom IDs, velocities, image flags can - be provided in any format that may be converted into the required - internal data types. Also the list may contain more than *N* entries, - but not fewer. In the latter case, the function will return without - attempting to create atoms. You may use the :py:func:`encode_image_flags - ` method to properly combine three integers - with image flags into a single integer. - - :param n: number of atoms for which data is provided - :type n: int - :param id: list of atom IDs with at least n elements or None - :type id: list of lammps.tagint - :param type: list of atom types - :type type: list of int - :param x: list of coordinates for x-, y-, and z (flat list of 3n entries) - :type x: list of float - :param v: list of velocities for x-, y-, and z (flat list of 3n entries) or None (optional) - :type v: list of float - :param image: list of encoded image flags (optional) - :type image: list of lammps.imageint - :param shrinkexceed: whether to expand shrink-wrap boundaries if atoms are outside the box (optional) - :type shrinkexceed: bool - :return: number of atoms created. 0 if insufficient or invalid data - :rtype: int - """ - if id is not None: - id_lmp = (self.c_tagint*n)() - try: - id_lmp[:] = id[0:n] - except ValueError: - return 0 - else: - id_lmp = None - - type_lmp = (c_int*n)() - try: - type_lmp[:] = type[0:n] - except ValueError: - return 0 - - three_n = 3*n - x_lmp = (c_double*three_n)() - try: - x_lmp[:] = x[0:three_n] - except ValueError: - return 0 - - if v is not None: - v_lmp = (c_double*(three_n))() - try: - v_lmp[:] = v[0:three_n] - except ValueError: - return 0 - else: - v_lmp = None - - if image is not None: - img_lmp = (self.c_imageint*n)() - try: - img_lmp[:] = image[0:n] - except ValueError: - return 0 - else: - img_lmp = None - - if shrinkexceed: - se_lmp = 1 - else: - se_lmp = 0 - - self.lib.lammps_create_atoms.argtypes = [c_void_p, c_int, POINTER(self.c_tagint*n), - POINTER(c_int*n), POINTER(c_double*three_n), - POINTER(c_double*three_n), - POINTER(self.c_imageint*n), c_int] - with ExceptionCheck(self): - return self.lib.lammps_create_atoms(self.lmp, n, id_lmp, type_lmp, x_lmp, v_lmp, img_lmp, se_lmp) - - # ------------------------------------------------------------------------- - - @property - def has_mpi_support(self): - """ Report whether the LAMMPS shared library was compiled with a - real MPI library or in serial. - - This is a wrapper around the :cpp:func:`lammps_config_has_mpi_support` - function of the library interface. - - :return: False when compiled with MPI STUBS, otherwise True - :rtype: bool - """ - return self.lib.lammps_config_has_mpi_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def is_running(self): - """ Report whether being called from a function during a run or a minimization - - Various LAMMPS commands must not be called during an ongoing - run or minimization. This property allows to check for that. - This is a wrapper around the :cpp:func:`lammps_is_running` - function of the library interface. - - .. versionadded:: 9Oct2020 - - :return: True when called during a run otherwise false - :rtype: bool - """ - return self.lib.lammps_is_running(self.lmp) == 1 - - # ------------------------------------------------------------------------- - - def force_timeout(self): - """ Trigger an immediate timeout, i.e. a "soft stop" of a run. - - This function allows to cleanly stop an ongoing run or minimization - at the next loop iteration. - This is a wrapper around the :cpp:func:`lammps_force_timeout` - function of the library interface. - - .. versionadded:: 9Oct2020 - """ - self.lib.lammps_force_timeout(self.lmp) - - # ------------------------------------------------------------------------- - - @property - def has_exceptions(self): - """ Report whether the LAMMPS shared library was compiled with C++ - exceptions handling enabled - - This is a wrapper around the :cpp:func:`lammps_config_has_exceptions` - function of the library interface. - - :return: state of C++ exception support - :rtype: bool - """ - return self.lib.lammps_config_has_exceptions() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_gzip_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for reading and writing compressed files through ``gzip``. - - This is a wrapper around the :cpp:func:`lammps_config_has_gzip_support` - function of the library interface. - - :return: state of gzip support - :rtype: bool - """ - return self.lib.lammps_config_has_gzip_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_png_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for writing images in PNG format. - - This is a wrapper around the :cpp:func:`lammps_config_has_png_support` - function of the library interface. - - :return: state of PNG support - :rtype: bool - """ - return self.lib.lammps_config_has_png_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_jpeg_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for writing images in JPEG format. - - This is a wrapper around the :cpp:func:`lammps_config_has_jpeg_support` - function of the library interface. - - :return: state of JPEG support - :rtype: bool - """ - return self.lib.lammps_config_has_jpeg_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_ffmpeg_support(self): - """ State of support for writing movies with ``ffmpeg`` in the LAMMPS shared library - - This is a wrapper around the :cpp:func:`lammps_config_has_ffmpeg_support` - function of the library interface. - - :return: state of ffmpeg support - :rtype: bool - """ - return self.lib.lammps_config_has_ffmpeg_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def accelerator_config(self): - """ Return table with available accelerator configuration settings. - - This is a wrapper around the :cpp:func:`lammps_config_accelerator` - function of the library interface which loops over all known packages - and categories and returns enabled features as a nested dictionary - with all enabled settings as list of strings. - - :return: nested dictionary with all known enabled settings as list of strings - :rtype: dictionary - """ - - result = {} - for p in ['GPU', 'KOKKOS', 'INTEL', 'OPENMP']: - result[p] = {} - c = 'api' - result[p][c] = [] - for s in ['cuda', 'hip', 'phi', 'pthreads', 'opencl', 'openmp', 'serial']: - if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): - result[p][c].append(s) - c = 'precision' - result[p][c] = [] - for s in ['double', 'mixed', 'single']: - if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): - result[p][c].append(s) - return result - - # ------------------------------------------------------------------------- - - @property - def has_gpu_device(self): - """ Availability of GPU package compatible device - - This is a wrapper around the :cpp:func:`lammps_has_gpu_device` - function of the C library interface. - - :return: True if a GPU package compatible device is present, otherwise False - :rtype: bool - """ - return self.lib.lammps_has_gpu_device() != 0 - - # ------------------------------------------------------------------------- - - def get_gpu_device_info(self): - """Return a string with detailed information about any devices that are - usable by the GPU package. - - This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` - function of the C-library interface. - - :return: GPU device info string - :rtype: string - """ - - sb = create_string_buffer(8192) - self.lib.lammps_get_gpu_device_info(sb,8192) - return sb.value.decode() - - # ------------------------------------------------------------------------- - - @property - def installed_packages(self): - """ List of the names of enabled packages in the LAMMPS shared library - - This is a wrapper around the functions :cpp:func:`lammps_config_package_count` - and :cpp:func`lammps_config_package_name` of the library interface. - - :return - """ - if self._installed_packages is None: - self._installed_packages = [] - npackages = self.lib.lammps_config_package_count() - sb = create_string_buffer(100) - for idx in range(npackages): - self.lib.lammps_config_package_name(idx, sb, 100) - self._installed_packages.append(sb.value.decode()) - return self._installed_packages - - # ------------------------------------------------------------------------- - - def has_style(self, category, name): - """Returns whether a given style name is available in a given category - - This is a wrapper around the function :cpp:func:`lammps_has_style` - of the library interface. - - :param category: name of category - :type category: string - :param name: name of the style - :type name: string - - :return: true if style is available in given category - :rtype: bool - """ - return self.lib.lammps_has_style(self.lmp, category.encode(), name.encode()) != 0 - - # ------------------------------------------------------------------------- - - def available_styles(self, category): - """Returns a list of styles available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_style_count()` - and :cpp:func:`lammps_style_name()` of the library interface. - - :param category: name of category - :type category: string - - :return: list of style names in given category - :rtype: list - """ - if self._available_styles is None: - self._available_styles = {} - - if category not in self._available_styles: - self._available_styles[category] = [] - with ExceptionCheck(self): - nstyles = self.lib.lammps_style_count(self.lmp, category.encode()) - sb = create_string_buffer(100) - for idx in range(nstyles): - with ExceptionCheck(self): - self.lib.lammps_style_name(self.lmp, category.encode(), idx, sb, 100) - self._available_styles[category].append(sb.value.decode()) - return self._available_styles[category] - - # ------------------------------------------------------------------------- - - def has_id(self, category, name): - """Returns whether a given ID name is available in a given category - - This is a wrapper around the function :cpp:func:`lammps_has_id` - of the library interface. - - .. versionadded:: 9Oct2020 - - :param category: name of category - :type category: string - :param name: name of the ID - :type name: string - - :return: true if ID is available in given category - :rtype: bool - """ - return self.lib.lammps_has_id(self.lmp, category.encode(), name.encode()) != 0 - - # ------------------------------------------------------------------------- - - def available_ids(self, category): - """Returns a list of IDs available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_id_count()` - and :cpp:func:`lammps_id_name()` of the library interface. - - .. versionadded:: 9Oct2020 - - :param category: name of category - :type category: string - - :return: list of id names in given category - :rtype: list - """ - - categories = ['compute','dump','fix','group','molecule','region','variable'] - available_ids = [] - if category in categories: - num = self.lib.lammps_id_count(self.lmp, category.encode()) - sb = create_string_buffer(100) - for idx in range(num): - self.lib.lammps_id_name(self.lmp, category.encode(), idx, sb, 100) - available_ids.append(sb.value.decode()) - return available_ids - - # ------------------------------------------------------------------------- - - def available_plugins(self, category): - """Returns a list of plugins available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_plugin_count()` - and :cpp:func:`lammps_plugin_name()` of the library interface. - - .. versionadded:: 10Mar2021 - - :return: list of style/name pairs of loaded plugins - :rtype: list - """ - - available_plugins = [] - num = self.lib.lammps_plugin_count(self.lmp) - sty = create_string_buffer(100) - nam = create_string_buffer(100) - for idx in range(num): - self.lib.lammps_plugin_name(idx, sty, nam, 100) - available_plugins.append([sty.value.decode(), nam.value.decode()]) - return available_plugins - - # ------------------------------------------------------------------------- - - def set_fix_external_callback(self, fix_id, callback, caller=None): - """Set the callback function for a fix external instance with a given fix ID. - - Optionally also set a reference to the calling object. - - This is a wrapper around the :cpp:func:`lammps_set_fix_external_callback` function - of the C-library interface. However this is set up to call a Python function with - the following arguments. - - .. code-block: python - - def func(object, ntimestep, nlocal, tag, x, f): - - - object is the value of the "caller" argument - - ntimestep is the current timestep - - nlocal is the number of local atoms on the current MPI process - - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms - - x is a 2d NumPy array of doubles of the coordinates of the local atoms - - f is a 2d NumPy array of doubles of the forces on the local atoms that will be added - - .. versionchanged:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param callback: Python function that will be called from fix external - :type: function - :param caller: reference to some object passed to the callback function - :type: object, optional - """ - import numpy as np - - def callback_wrapper(caller, ntimestep, nlocal, tag_ptr, x_ptr, fext_ptr): - tag = self.numpy.iarray(self.c_tagint, tag_ptr, nlocal, 1) - x = self.numpy.darray(x_ptr, nlocal, 3) - f = self.numpy.darray(fext_ptr, nlocal, 3) - callback(caller, ntimestep, nlocal, tag, x, f) - - cFunc = self.FIX_EXTERNAL_CALLBACK_FUNC(callback_wrapper) - cCaller = caller - - self.callback[fix_id] = { 'function': cFunc, 'caller': caller } - with ExceptionCheck(self): - self.lib.lammps_set_fix_external_callback(self.lmp, fix_id.encode(), cFunc, cCaller) - - # ------------------------------------------------------------------------- - - def fix_external_get_force(self, fix_id): - """Get access to the array with per-atom forces of a fix external instance with a given fix ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :return: requested data - :rtype: ctypes.POINTER(ctypes.POINTER(ctypes.double)) - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_get_force(self.lmp, fix_id.encode()) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_global(self, fix_id, eng): - """Set the global energy contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eng: potential energy value to be added by fix external - :type: float - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_global(self, fix_id, virial): - """Set the global virial contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eng: list of 6 floating point numbers with the virial to be added by fix external - :type: float - """ - - cvirial = (6*c_double)(*virial) - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_peratom(self, fix_id, eatom): - """Set the per-atom energy contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: list of potential energy values for local atoms to be added by fix external - :type: float - """ - - nlocal = self.extract_setting('nlocal') - if len(eatom) < nlocal: - raise Exception('per-atom energy list length must be at least nlocal') - ceatom = (nlocal*c_double)(*eatom) - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_peratom(self, fix_id, vatom): - """Set the per-atom virial contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external - :type: float - """ - - # copy virial data to C compatible buffer - nlocal = self.extract_setting('nlocal') - if len(vatom) < nlocal: - raise Exception('per-atom virial first dimension must be at least nlocal') - if len(vatom[0]) != 6: - raise Exception('per-atom virial second dimension must be 6') - vbuf = (c_double * 6) - vptr = POINTER(c_double) - c_virial = (vptr * nlocal)() - for i in range(nlocal): - c_virial[i] = vbuf() - for j in range(6): - c_virial[i][j] = vatom[i][j] - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), c_virial) - - # ------------------------------------------------------------------------- - def fix_external_set_vector_length(self, fix_id, length): - """Set the vector length for a global vector stored with fix external for analysis - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param length: length of the global vector - :type: int - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_vector_length(self.lmp, fix_id.encode(), length) - - # ------------------------------------------------------------------------- - def fix_external_set_vector(self, fix_id, idx, val): - """Store a global vector value for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param idx: 1-based index of the value in the global vector - :type: int - :param val: value to be stored in the global vector - :type: float - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_vector(self.lmp, fix_id.encode(), idx, val) - - # ------------------------------------------------------------------------- - - def get_neighlist(self, idx): - """Returns an instance of :class:`NeighList` which wraps access to the neighbor list with the given index - - See :py:meth:`lammps.numpy.get_neighlist() ` if you want to use - NumPy arrays instead of ``c_int`` pointers. - - :param idx: index of neighbor list - :type idx: int - :return: an instance of :class:`NeighList` wrapping access to neighbor list data - :rtype: NeighList - """ - if idx < 0: - return None - return NeighList(self, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_size(self, idx): - """Return the number of elements in neighbor list with the given index - - :param idx: neighbor list index - :type idx: int - :return: number of elements in neighbor list with index idx - :rtype: int - """ - return self.lib.lammps_neighlist_num_elements(self.lmp, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_element_neighbors(self, idx, element): - """Return data of neighbor list entry - - :param element: neighbor list index - :type element: int - :param element: neighbor list element index - :type element: int - :return: tuple with atom local index, number of neighbors and array of neighbor local atom indices - :rtype: (int, int, POINTER(c_int)) - """ - c_iatom = c_int() - c_numneigh = c_int() - c_neighbors = POINTER(c_int)() - self.lib.lammps_neighlist_element_neighbors(self.lmp, idx, element, byref(c_iatom), byref(c_numneigh), byref(c_neighbors)) - return c_iatom.value, c_numneigh.value, c_neighbors - - # ------------------------------------------------------------------------- - - def find_pair_neighlist(self, style, exact=True, nsub=0, reqid=0): - """Find neighbor list index of pair style neighbor list - - Search for a neighbor list requested by a pair style instance that - matches "style". If exact is True, the pair style name must match - exactly. If exact is False, the pair style name is matched against - "style" as regular expression or sub-string. If the pair style is a - hybrid pair style, the style is instead matched against the hybrid - sub-styles. If the same pair style is used as sub-style multiple - types, you must set nsub to a value n > 0 which indicates the nth - instance of that sub-style to be used (same as for the pair_coeff - command). The default value of 0 will fail to match in that case. - - Once the pair style instance has been identified, it may have - requested multiple neighbor lists. Those are uniquely identified by - a request ID > 0 as set by the pair style. Otherwise the request - ID is 0. - - :param style: name of pair style that should be searched for - :type style: string - :param exact: controls whether style should match exactly or only must be contained in pair style name, defaults to True - :type exact: bool, optional - :param nsub: match nsub-th hybrid sub-style, defaults to 0 - :type nsub: int, optional - :param reqid: list request id, > 0 in case there are more than one, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - style = style.encode() - exact = int(exact) - idx = self.lib.lammps_find_pair_neighlist(self.lmp, style, exact, nsub, reqid) - return idx - - # ------------------------------------------------------------------------- - - def find_fix_neighlist(self, fixid, reqid=0): - """Find neighbor list index of fix neighbor list - - The fix instance requesting the neighbor list is uniquely identified - by the fix ID. In case the fix has requested multiple neighbor - lists, those are uniquely identified by a request ID > 0 as set by - the fix. Otherwise the request ID is 0 (the default). - - :param fixid: name of fix - :type fixid: string - :param reqid: id of neighbor list request, in case there are more than one request, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - fixid = fixid.encode() - idx = self.lib.lammps_find_fix_neighlist(self.lmp, fixid, reqid) - return idx - - # ------------------------------------------------------------------------- - - def find_compute_neighlist(self, computeid, reqid=0): - """Find neighbor list index of compute neighbor list - - The compute instance requesting the neighbor list is uniquely - identified by the compute ID. In case the compute has requested - multiple neighbor lists, those are uniquely identified by a request - ID > 0 as set by the compute. Otherwise the request ID is 0 (the - default). - - :param computeid: name of compute - :type computeid: string - :param reqid: index of neighbor list request, in case there are more than one request, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - computeid = computeid.encode() - idx = self.lib.lammps_find_compute_neighlist(self.lmp, computeid, reqid) - return idx diff --git a/python/lammps/data.py b/python/lammps/data.py deleted file mode 100644 index 731a8c514a..0000000000 --- a/python/lammps/data.py +++ /dev/null @@ -1,92 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# LAMMPS data structures -# Written by Richard Berger -################################################################################ - -class NeighList(object): - """This is a wrapper class that exposes the contents of a neighbor list. - - It can be used like a regular Python list. Each element is a tuple of: - - * the atom local index - * its number of neighbors - * and a pointer to an c_int array containing local atom indices of its - neighbors - - Internally it uses the lower-level LAMMPS C-library interface. - - :param lmp: reference to instance of :py:class:`lammps` - :type lmp: lammps - :param idx: neighbor list index - :type idx: int - """ - def __init__(self, lmp, idx): - self.lmp = lmp - self.idx = idx - - def __str__(self): - return "Neighbor List ({} atoms)".format(self.size) - - def __repr__(self): - return self.__str__() - - @property - def size(self): - """ - :return: number of elements in neighbor list - """ - return self.lmp.get_neighlist_size(self.idx) - - def get(self, element): - """ - Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list - - :return: tuple with atom local index, number of neighbors and ctypes pointer to neighbor's local atom indices - :rtype: (int, int, ctypes.POINTER(c_int)) - """ - iatom, numneigh, neighbors = self.lmp.get_neighlist_element_neighbors(self.idx, element) - return iatom, numneigh, neighbors - - # the methods below implement the iterator interface, so NeighList can be used like a regular Python list - - def __getitem__(self, element): - return self.get(element) - - def __len__(self): - return self.size - - def __iter__(self): - inum = self.size - - for ii in range(inum): - yield self.get(ii) - - def find(self, iatom): - """ - Find the neighbor list for a specific (local) atom iatom. - If there is no list for iatom, (-1, None) is returned. - - :return: tuple with number of neighbors and ctypes pointer to neighbor's local atom indices - :rtype: (int, ctypes.POINTER(c_int)) - """ - - inum = self.size - for ii in range(inum): - idx, numneigh, neighbors = self.get(ii) - if idx == iatom: - return numneigh, neighbors - - return -1, None diff --git a/python/lammps/formats.py b/python/lammps/formats.py deleted file mode 100644 index b7c267466f..0000000000 --- a/python/lammps/formats.py +++ /dev/null @@ -1,227 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# LAMMPS output formats -# Written by Richard Berger -# and Axel Kohlmeyer -################################################################################ - -import re - -has_yaml = False -try: - import yaml - has_yaml = True - try: - from yaml import CSafeLoader as Loader - except ImportError: - from yaml import SafeLoader as Loader -except ImportError: - # ignore here, raise an exception when trying to parse yaml instead - pass - -class LogFile: - """Reads LAMMPS log files and extracts the thermo information - - It supports the line, multi, and yaml thermo output styles. - - :param filename: path to log file - :type filename: str - - :ivar runs: List of LAMMPS runs in log file. Each run is a dictionary with - thermo fields as keys, storing the values over time - :ivar errors: List of error lines in log file - """ - - STYLE_DEFAULT = 0 - STYLE_MULTI = 1 - STYLE_YAML = 2 - - def __init__(self, filename): - alpha = re.compile(r'[a-df-zA-DF-Z]') # except e or E for floating-point numbers - kvpairs = re.compile(r'([a-zA-Z_0-9]+)\s+=\s*([0-9\.eE\-]+)') - style = LogFile.STYLE_DEFAULT - yamllog = "" - self.runs = [] - self.errors = [] - with open(filename, 'rt') as f: - in_thermo = False - in_data_section = False - for line in f: - if "ERROR" in line or "exited on signal" in line: - self.errors.append(line) - - elif re.match(r'^ *Step ', line): - in_thermo = True - in_data_section = True - keys = line.split() - current_run = {} - for k in keys: - current_run[k] = [] - - elif re.match(r'^(keywords:.*$|data:$|---$| - \[.*\]$)', line): - if not has_yaml: - raise Exception('Cannot process YAML format logs without the PyYAML Python module') - style = LogFile.STYLE_YAML - yamllog += line; - current_run = {} - - elif re.match(r'^\.\.\.$', line): - thermo = yaml.load(yamllog, Loader=Loader) - for k in thermo['keywords']: - current_run[k] = [] - for step in thermo['data']: - icol = 0 - for k in thermo['keywords']: - current_run[k].append(step[icol]) - icol += 1 - self.runs.append(current_run) - yamllog = "" - - elif re.match(r'^------* Step ', line): - if not in_thermo: - current_run = {'Step': [], 'CPU': []} - in_thermo = True - in_data_section = True - style = LogFile.STYLE_MULTI - str_step, str_cpu = line.strip('-\n').split('-----') - step = float(str_step.split()[1]) - cpu = float(str_cpu.split('=')[1].split()[0]) - current_run["Step"].append(step) - current_run["CPU"].append(cpu) - - elif line.startswith('Loop time of'): - in_thermo = False - if style != LogFile.STYLE_YAML: - self.runs.append(current_run) - - elif in_thermo and in_data_section: - if style == LogFile.STYLE_DEFAULT: - if alpha.search(line): - continue - for k, v in zip(keys, map(float, line.split())): - current_run[k].append(v) - - elif style == LogFile.STYLE_MULTI: - if '=' not in line: - in_data_section = False - continue - for k,v in kvpairs.findall(line): - if k not in current_run: - current_run[k] = [float(v)] - else: - current_run[k].append(float(v)) - -class AvgChunkFile: - """Reads files generated by fix ave/chunk - - :param filename: path to ave/chunk file - :type filename: str - - :ivar timesteps: List of timesteps stored in file - :ivar total_count: total count over time - :ivar chunks: List of chunks. Each chunk is a dictionary containing its ID, the coordinates, and the averaged quantities - """ - def __init__(self, filename): - with open(filename, 'rt') as f: - timestep = None - chunks_read = 0 - - self.timesteps = [] - self.total_count = [] - self.chunks = [] - - for lineno, line in enumerate(f): - if lineno == 0: - if not line.startswith("# Chunk-averaged data for fix"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - parts = line.split() - self.fix_name = parts[5] - self.group_name = parts[8] - continue - elif lineno == 1: - if not line.startswith("# Timestep Number-of-chunks Total-count"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - continue - elif lineno == 2: - if not line.startswith("#"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - columns = line.split()[1:] - ndim = line.count("Coord") - compress = 'OrigID' in line - if ndim > 0: - coord_start = columns.index("Coord1") - coord_end = columns.index("Coord%d" % ndim) - ncount_start = coord_end + 1 - data_start = ncount_start + 1 - else: - coord_start = None - coord_end = None - ncount_start = 2 - data_start = 3 - continue - - parts = line.split() - - if timestep is None: - timestep = int(parts[0]) - num_chunks = int(parts[1]) - total_count = float(parts[2]) - - self.timesteps.append(timestep) - self.total_count.append(total_count) - - for i in range(num_chunks): - self.chunks.append({ - 'coord' : [], - 'ncount' : [] - }) - elif chunks_read < num_chunks: - chunk = int(parts[0]) - ncount = float(parts[ncount_start]) - - if compress: - chunk_id = int(parts[1]) - else: - chunk_id = chunk - - current = self.chunks[chunk_id - 1] - current['id'] = chunk_id - current['ncount'].append(ncount) - - if ndim > 0: - coord = tuple(map(float, parts[coord_start:coord_end+1])) - current['coord'].append(coord) - - for i, data_column in list(enumerate(columns))[data_start:]: - value = float(parts[i]) - - if data_column in current: - current[data_column].append(value) - else: - current[data_column] = [value] - - chunks_read += 1 - assert chunk == chunks_read - else: - # do not support changing number of chunks - if not (num_chunks == int(parts[1])): - raise Exception("Currently, changing numbers of chunks are not supported.") - - timestep = int(parts[0]) - total_count = float(parts[2]) - chunks_read = 0 - - self.timesteps.append(timestep) - self.total_count.append(total_count) diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py deleted file mode 100644 index 57fe97d803..0000000000 --- a/python/lammps/mliap/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ - -# Check compatiblity of this build with the python shared library. -# If this fails, lammps will segfault because its library will -# try to improperly start up a new interpreter. -import sysconfig -import ctypes -library = sysconfig.get_config_vars('INSTSONAME')[0] -try: - pylib = ctypes.CDLL(library) -except OSError as e: - if pylib.endswith(".a"): - pylib.strip(".a") + ".so" - pylib = ctypes.CDLL(library) - else: - raise e -if not pylib.Py_IsInitialized(): - raise RuntimeError("This interpreter is not compatible with python-based mliap for LAMMPS.") -del sysconfig, ctypes, library, pylib - -from .loader import load_model, activate_mliappy diff --git a/python/lammps/mliap/loader.py b/python/lammps/mliap/loader.py deleted file mode 100644 index dff791bfc1..0000000000 --- a/python/lammps/mliap/loader.py +++ /dev/null @@ -1,52 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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: Nicholas Lubbers (LANL) -# ------------------------------------------------------------------------- - - -import sys -import importlib.util -import importlib.machinery - -def activate_mliappy(lmp): - try: - # Begin Importlib magic to find the embedded python module - # This is needed because the filename for liblammps does not - # match the spec for normal python modules, wherein - # file names match with PyInit function names. - # Also, python normally doesn't look for extensions besides '.so' - # We fix both of these problems by providing an explict - # path to the extension module 'mliap_model_python_couple' in - - path = lmp.lib._name - loader = importlib.machinery.ExtensionFileLoader('mliap_model_python_couple', path) - spec = importlib.util.spec_from_loader('mliap_model_python_couple', loader) - module = importlib.util.module_from_spec(spec) - sys.modules['mliap_model_python_couple'] = module - spec.loader.exec_module(module) - # End Importlib magic to find the embedded python module - - except Exception as ee: - raise ImportError("Could not load ML-IAP python coupling module.") from ee - -def load_model(model): - try: - import mliap_model_python_couple - except ImportError as ie: - raise ImportError("ML-IAP python module must be activated before loading\n" - "the pair style. Call lammps.mliap.activate_mliappy(lmp)." - ) from ie - mliap_model_python_couple.load_from_python(model) - diff --git a/python/lammps/mliap/pytorch.py b/python/lammps/mliap/pytorch.py deleted file mode 100644 index 9aa2da80f4..0000000000 --- a/python/lammps/mliap/pytorch.py +++ /dev/null @@ -1,326 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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: Nicholas Lubbers (LANL) -# ------------------------------------------------------------------------- - -import numpy as np -import torch - -def calc_n_params(model): - """ - Returns the sum of two decimal numbers in binary digits. - - Parameters: - model (torch.nn.Module): Network model that maps descriptors to a per atom attribute - - Returns: - n_params (int): Number of NN model parameters - """ - return sum(p.nelement() for p in model.parameters()) - -class TorchWrapper(torch.nn.Module): - """ - A class to wrap Modules to ensure lammps mliap compatability. - - ... - - Attributes - ---------- - model : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - device : torch.nn.Module (None) - Accelerator device - - dtype : torch.dtype (torch.float64) - Dtype to use on device - - n_params : torch.nn.Module (None) - Number of NN model parameters - - n_descriptors : int - Max number of per atom descriptors - - n_elements : int - Max number of elements - - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model to produce per atom energies and forces. - """ - - def __init__(self, model, n_descriptors, n_elements, n_params=None, device=None, dtype=torch.float64): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - model : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - n_descriptors : int - Max number of per atom descriptors - - n_elements : int - Max number of elements - - n_params : torch.nn.Module (None) - Number of NN model parameters - - device : torch.nn.Module (None) - Accelerator device - - dtype : torch.dtype (torch.float64) - Dtype to use on device - """ - - super().__init__() - - self.model = model - self.device = device - self.dtype = dtype - - # Put model on device and convert to dtype - self.to(self.dtype) - self.to(self.device) - - if n_params is None: - n_params = calc_n_params(model) - - self.n_params = n_params - self.n_descriptors = n_descriptors - self.n_elements = n_elements - - def forward(self, elems, descriptors, beta, energy): - """ - Takes element types and descriptors calculated via lammps and - calculates the per atom energies and forces. - - Parameters - ---------- - elems : numpy.array - Per atom element types - - descriptors : numpy.array - Per atom descriptors - - beta : numpy.array - Expired beta array to be filled with new betas - - energy : numpy.array - Expired per atom energy array to be filled with new per atom energy - (Note: This is a pointer to the lammps per atom energies) - - - Returns - ------- - None - """ - - descriptors = torch.from_numpy(descriptors).to(dtype=self.dtype, device=self.device).requires_grad_(True) - elems = torch.from_numpy(elems).to(dtype=torch.long, device=self.device) - 1 - - with torch.autograd.enable_grad(): - - energy_nn = self.model(descriptors, elems) - if energy_nn.ndim > 1: - energy_nn = energy_nn.flatten() - - beta_nn = torch.autograd.grad(energy_nn.sum(), descriptors)[0] - - beta[:] = beta_nn.detach().cpu().numpy().astype(np.float64) - energy[:] = energy_nn.detach().cpu().numpy().astype(np.float64) - - -class IgnoreElems(torch.nn.Module): - """ - A class to represent a NN model agnostic of element typing. - - ... - - Attributes - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model - """ - - def __init__(self, subnet): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - """ - - super().__init__() - self.subnet = subnet - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnet(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - return self.subnet(descriptors) - - -class UnpackElems(torch.nn.Module): - """ - A class to represent a NN model pseudo-agnostic of element typing for - systems with multiple element typings. - - ... - - Attributes - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - n_types : int - Number of atom types used in training the NN model. - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - """ - - def __init__(self, subnet, n_types): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - """ - super().__init__() - self.subnet = subnet - self.n_types = n_types - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnet(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - unpacked_descriptors = torch.zeros(elems.shape[0], self.n_types, descriptors.shape[1], dtype=torch.float64) - for i, ind in enumerate(elems): - unpacked_descriptors[i, ind, :] = descriptors[i] - return self.subnet(torch.reshape(unpacked_descriptors, (elems.shape[0], -1)), elems) - - -class ElemwiseModels(torch.nn.Module): - """ - A class to represent a NN model dependent on element typing. - - ... - - Attributes - ---------- - subnets : list of torch.nn.Modules - Per element type network models that maps per element type - descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - """ - - def __init__(self, subnets, n_types): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnets : list of torch.nn.Modules - Per element type network models that maps per element - type descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - """ - - super().__init__() - self.subnets = subnets - self.n_types = n_types - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnets(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - per_atom_attributes = torch.zeros(elems.size[0]) - given_elems, elem_indices = torch.unique(elems, return_inverse=True) - for i, elem in enumerate(given_elems): - per_atom_attribute[elem_indices == i] = self.subnets[elem](descriptors[elem_indices == i]) - return per_atom_attributes diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py deleted file mode 100644 index ce0cb35e47..0000000000 --- a/python/lammps/numpy_wrapper.py +++ /dev/null @@ -1,483 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# NumPy additions -# Written by Richard Berger -################################################################################ - -import warnings -from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast - - -from .constants import * # lgtm [py/polluting-import] -from .data import NeighList - - -class numpy_wrapper: - """lammps API NumPy Wrapper - - This is a wrapper class that provides additional methods on top of an - existing :py:class:`lammps` instance. The methods transform raw ctypes - pointers into NumPy arrays, which give direct access to the - original data while protecting against out-of-bounds accesses. - - There is no need to explicitly instantiate this class. Each instance - of :py:class:`lammps` has a :py:attr:`numpy ` property - that returns an instance. - - :param lmp: instance of the :py:class:`lammps` class - :type lmp: lammps - """ - def __init__(self, lmp): - self.lmp = lmp - - # ------------------------------------------------------------------------- - - def _ctype_to_numpy_int(self, ctype_int): - import numpy as np - if ctype_int == c_int32: - return np.int32 - elif ctype_int == c_int64: - return np.int64 - return np.intc - - # ------------------------------------------------------------------------- - - def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT): - """Retrieve per-atom properties from LAMMPS as NumPy arrays - - This is a wrapper around the :py:meth:`lammps.extract_atom()` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - .. note:: - - While the returned arrays of per-atom data are dimensioned - for the range [0:nmax] - as is the underlying storage - - the data is usually only valid for the range of [0:nlocal], - unless the property of interest is also updated for ghost - atoms. In some cases, this depends on a LAMMPS setting, see - for example :doc:`comm_modify vel yes `. - - :param name: name of the property - :type name: string - :param dtype: type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :param nelem: number of elements in array - :type nelem: int, optional - :param dim: dimension of each element - :type dim: int, optional - :return: requested data as NumPy array with direct access to C data or None - :rtype: numpy.array or NoneType - """ - if dtype == LAMMPS_AUTODETECT: - dtype = self.lmp.extract_atom_datatype(name) - - if nelem == LAMMPS_AUTODETECT: - if name == "mass": - nelem = self.lmp.extract_global("ntypes") + 1 - else: - nelem = self.lmp.extract_global("nlocal") - if dim == LAMMPS_AUTODETECT: - if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D): - # TODO add other fields - if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"): - dim = 3 - elif name == "smd_data_9": - dim = 9 - elif name == "smd_stress": - dim = 6 - else: - dim = 2 - else: - dim = 1 - - raw_ptr = self.lmp.extract_atom(name, dtype) - - if dtype in (LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D): - return self.darray(raw_ptr, nelem, dim) - elif dtype in (LAMMPS_INT, LAMMPS_INT_2D): - return self.iarray(c_int32, raw_ptr, nelem, dim) - elif dtype in (LAMMPS_INT64, LAMMPS_INT64_2D): - return self.iarray(c_int64, raw_ptr, nelem, dim) - return raw_ptr - - # ------------------------------------------------------------------------- - - def extract_atom_iarray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if name in ['id', 'molecule']: - c_int_type = self.lmp.c_tagint - elif name in ['image']: - c_int_type = self.lmp.c_imageint - else: - c_int_type = c_int - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D) - - return self.iarray(c_int_type, raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - - def extract_atom_darray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D) - - return self.darray(raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - - def extract_compute(self, cid, cstyle, ctype): - """Retrieve data from a LAMMPS compute - - This is a wrapper around the - :py:meth:`lammps.extract_compute() ` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param cid: compute ID - :type cid: string - :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type cstyle: int - :param ctype: type of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ctype: int - :return: requested data either as float, as NumPy array with direct access to C data, or None - :rtype: float, numpy.array, or NoneType - """ - value = self.lmp.extract_compute(cid, cstyle, ctype) - - if cstyle == LMP_STYLE_GLOBAL: - if ctype == LMP_TYPE_VECTOR: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_VECTOR) - return self.darray(value, nrows) - elif ctype == LMP_TYPE_ARRAY: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - return self.darray(value, nrows, ncols) - elif cstyle == LMP_STYLE_LOCAL: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - if ncols == 0: - return self.darray(value, nrows) - else: - return self.darray(value, nrows, ncols) - elif cstyle == LMP_STYLE_ATOM: - if ctype == LMP_TYPE_VECTOR: - nlocal = self.lmp.extract_global("nlocal") - return self.darray(value, nlocal) - elif ctype == LMP_TYPE_ARRAY: - nlocal = self.lmp.extract_global("nlocal") - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - return self.darray(value, nlocal, ncols) - return value - - # ------------------------------------------------------------------------- - - def extract_fix(self, fid, fstyle, ftype, nrow=0, ncol=0): - """Retrieve data from a LAMMPS fix - - This is a wrapper around the :py:meth:`lammps.extract_fix() ` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param fid: fix ID - :type fid: string - :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type fstyle: int - :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ftype: int - :param nrow: index of global vector element or row index of global array element - :type nrow: int - :param ncol: column index of global array element - :type ncol: int - :return: requested data - :rtype: integer or double value, pointer to 1d or 2d double array or None - - """ - value = self.lmp.extract_fix(fid, fstyle, ftype, nrow, ncol) - if fstyle == LMP_STYLE_ATOM: - if ftype == LMP_TYPE_VECTOR: - nlocal = self.lmp.extract_global("nlocal") - return self.darray(value, nlocal) - elif ftype == LMP_TYPE_ARRAY: - nlocal = self.lmp.extract_global("nlocal") - ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) - return self.darray(value, nlocal, ncols) - elif fstyle == LMP_STYLE_LOCAL: - if ftype == LMP_TYPE_VECTOR: - nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) - return self.darray(value, nrows) - elif ftype == LMP_TYPE_ARRAY: - nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) - ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) - return self.darray(value, nrows, ncols) - return value - - # ------------------------------------------------------------------------- - - def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): - """ Evaluate a LAMMPS variable and return its data - - This function is a wrapper around the function - :py:meth:`lammps.extract_variable() ` - method. It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param name: name of the variable to execute - :type name: string - :param group: name of group for atom-style variable (ignored for equal-style variables) - :type group: string - :param vartype: type of variable, see :ref:`py_vartype_constants` - :type vartype: int - :return: the requested data or None - :rtype: c_double, numpy.array, or NoneType - """ - import numpy as np - value = self.lmp.extract_variable(name, group, vartype) - if vartype == LMP_VAR_ATOM: - return np.ctypeslib.as_array(value) - return value - - # ------------------------------------------------------------------------- - - def gather_bonds(self): - """Retrieve global list of bonds as NumPy array - - This is a wrapper around :py:meth:`lammps.gather_bonds() ` - It behaves the same as the original method, but returns a NumPy array instead - of a ``ctypes`` list. - - .. versionadded:: 28Jul2021 - - :return: the requested data as a 2d-integer numpy array - :rtype: numpy.array(nbonds,3) - """ - import numpy as np - nbonds, value = self.lmp.gather_bonds() - return np.ctypeslib.as_array(value).reshape(nbonds,3) - - # ------------------------------------------------------------------------- - - def fix_external_get_force(self, fix_id): - """Get access to the array with per-atom forces of a fix external instance with a given fix ID. - - This function is a wrapper around the - :py:meth:`lammps.fix_external_get_force() ` - method. It behaves the same as the original method, but returns a NumPy array instead - of a ``ctypes`` pointer. - - .. versionchanged:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :return: requested data - :rtype: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - value = self.lmp.fix_external_get_force(fix_id) - return self.darray(value,nlocal,3) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_peratom(self, fix_id, eatom): - """Set the per-atom energy contribution for a fix external instance with the given ID. - - This function is an alternative to - :py:meth:`lammps.fix_external_set_energy_peratom() ` - method. It behaves the same as the original method, but accepts a NumPy array - instead of a list as argument. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: per-atom potential energy - :type: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - if len(eatom) < nlocal: - raise Exception('per-atom energy dimension must be at least nlocal') - - c_double_p = POINTER(c_double) - value = eatom.astype(np.double) - return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), - value.ctypes.data_as(c_double_p)) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_peratom(self, fix_id, vatom): - """Set the per-atom virial contribution for a fix external instance with the given ID. - - This function is an alternative to - :py:meth:`lammps.fix_external_set_virial_peratom() ` - method. It behaves the same as the original method, but accepts a NumPy array - instead of a list as argument. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: per-atom potential energy - :type: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - if len(vatom) < nlocal: - raise Exception('per-atom virial first dimension must be at least nlocal') - if len(vatom[0]) != 6: - raise Exception('per-atom virial second dimension must be 6') - - c_double_pp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C') - - # recast numpy array to be compatible with library interface - value = (vatom.__array_interface__['data'][0] - + np.arange(vatom.shape[0])*vatom.strides[0]).astype(np.uintp) - - # change prototype to our custom type - self.lmp.lib.lammps_fix_external_set_virial_peratom.argtypes = [ c_void_p, c_char_p, c_double_pp ] - - self.lmp.lib.lammps_fix_external_set_virial_peratom(self.lmp.lmp, fix_id.encode(), value) - - # ------------------------------------------------------------------------- - - def get_neighlist(self, idx): - """Returns an instance of :class:`NumPyNeighList` which wraps access to the neighbor list with the given index - - :param idx: index of neighbor list - :type idx: int - :return: an instance of :class:`NumPyNeighList` wrapping access to neighbor list data - :rtype: NumPyNeighList - """ - if idx < 0: - return None - return NumPyNeighList(self.lmp, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_element_neighbors(self, idx, element): - """Return data of neighbor list entry - - This function is a wrapper around the function - :py:meth:`lammps.get_neighlist_element_neighbors() ` - method. It behaves the same as the original method, but returns a NumPy array containing the neighbors - instead of a ``ctypes`` pointer. - - :param element: neighbor list index - :type element: int - :param element: neighbor list element index - :type element: int - :return: tuple with atom local index and numpy array of neighbor local atom indices - :rtype: (int, numpy.array) - """ - iatom, numneigh, c_neighbors = self.lmp.get_neighlist_element_neighbors(idx, element) - neighbors = self.iarray(c_int, c_neighbors, numneigh, 1) - return iatom, neighbors - - # ------------------------------------------------------------------------- - - def iarray(self, c_int_type, raw_ptr, nelem, dim=1): - if raw_ptr is None: - return None - - import numpy as np - np_int_type = self._ctype_to_numpy_int(c_int_type) - - if dim == 1: - ptr = cast(raw_ptr, POINTER(c_int_type * nelem)) - else: - ptr = cast(raw_ptr[0], POINTER(c_int_type * nelem * dim)) - - a = np.frombuffer(ptr.contents, dtype=np_int_type) - - if dim > 1: - a.shape = (nelem, dim) - else: - a.shape = (nelem) - return a - - # ------------------------------------------------------------------------- - - def darray(self, raw_ptr, nelem, dim=1): - if raw_ptr is None: - return None - - import numpy as np - - if dim == 1: - ptr = cast(raw_ptr, POINTER(c_double * nelem)) - else: - ptr = cast(raw_ptr[0], POINTER(c_double * nelem * dim)) - - a = np.frombuffer(ptr.contents) - - if dim > 1: - a.shape = (nelem, dim) - else: - a.shape = (nelem) - return a - -# ------------------------------------------------------------------------- - -class NumPyNeighList(NeighList): - """This is a wrapper class that exposes the contents of a neighbor list. - - It can be used like a regular Python list. Each element is a tuple of: - - * the atom local index - * a NumPy array containing the local atom indices of its neighbors - - Internally it uses the lower-level LAMMPS C-library interface. - - :param lmp: reference to instance of :py:class:`lammps` - :type lmp: lammps - :param idx: neighbor list index - :type idx: int - """ - def __init__(self, lmp, idx): - super(NumPyNeighList, self).__init__(lmp, idx) - - def get(self, element): - """ - Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list - - :return: tuple with atom local index, numpy array of neighbor local atom indices - :rtype: (int, numpy.array) - """ - iatom, neighbors = self.lmp.numpy.get_neighlist_element_neighbors(self.idx, element) - return iatom, neighbors - - def find(self, iatom): - """ - Find the neighbor list for a specific (local) atom iatom. - If there is no list for iatom, None is returned. - - :return: numpy array of neighbor local atom indices - :rtype: numpy.array or None - """ - inum = self.size - for ii in range(inum): - idx, neighbors = self.get(ii) - if idx == iatom: - return neighbors - return None diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py deleted file mode 100644 index 1fe1f2452b..0000000000 --- a/python/lammps/pylammps.py +++ /dev/null @@ -1,990 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# Alternative Python Wrapper -# Written by Richard Berger -################################################################################ - -# for python2/3 compatibility - -from __future__ import print_function - -import io -import os -import re -import sys -import tempfile -from collections import namedtuple - -from .core import lammps - -# ------------------------------------------------------------------------- - -class OutputCapture(object): - """ Utility class to capture LAMMPS library output """ - def __init__(self): - self.stdout_fd = 1 - self.captured_output = "" - - def __enter__(self): - self.tmpfile = tempfile.TemporaryFile(mode='w+b') - - sys.stdout.flush() - - # make copy of original stdout - self.stdout_orig = os.dup(self.stdout_fd) - - # replace stdout and redirect to temp file - os.dup2(self.tmpfile.fileno(), self.stdout_fd) - return self - - def __exit__(self, exc_type, exc_value, traceback): - os.dup2(self.stdout_orig, self.stdout_fd) - os.close(self.stdout_orig) - self.tmpfile.close() - - @property - def output(self): - sys.stdout.flush() - self.tmpfile.flush() - self.tmpfile.seek(0, io.SEEK_SET) - self.captured_output = self.tmpfile.read().decode('utf-8') - return self.captured_output - -# ------------------------------------------------------------------------- - -class Variable(object): - def __init__(self, pylammps_instance, name, style, definition): - self._pylmp = pylammps_instance - self.name = name - self.style = style - self.definition = definition.split() - - @property - def value(self): - if self.style == 'atom': - return list(self._pylmp.lmp.extract_variable(self.name, "all", 1)) - else: - value = self._pylmp.lmp_print('"${%s}"' % self.name).strip() - try: - return float(value) - except ValueError: - return value - -# ------------------------------------------------------------------------- - -class AtomList(object): - """ - A dynamic list of atoms that returns either an :py:class:`Atom` or - :py:class:`Atom2D` instance for each atom. Instances are only allocated - when accessed. - - :ivar natoms: total number of atoms - :ivar dimensions: number of dimensions in system - """ - def __init__(self, pylammps_instance): - self._pylmp = pylammps_instance - self.natoms = self._pylmp.system.natoms - self.dimensions = self._pylmp.system.dimensions - self._loaded = {} - - def __getitem__(self, index): - """ - Return Atom with given local index - - :param index: Local index of atom - :type index: int - :rtype: Atom or Atom2D - """ - if index not in self._loaded: - if self.dimensions == 2: - atom = Atom2D(self._pylmp, index) - else: - atom = Atom(self._pylmp, index) - self._loaded[index] = atom - return self._loaded[index] - - def __len__(self): - return self.natoms - - -# ------------------------------------------------------------------------- - -class Atom(object): - """ - A wrapper class then represents a single atom inside of LAMMPS - - It provides access to properties of the atom and allows you to change some of them. - """ - def __init__(self, pylammps_instance, index): - self._pylmp = pylammps_instance - self.index = index - - def __dir__(self): - return [k for k in super().__dir__() if not k.startswith('_')] - - def get(self, name, index): - prop = self._pylmp.lmp.numpy.extract_atom(name) - if prop is not None: - return prop[index] - return None - - @property - def id(self): - """ - Return the atom ID - - :type: int - """ - return self.get("id", self.index) - - @property - def type(self): - """ - Return the atom type - - :type: int - """ - return self.get("type", self.index) - - @property - def mol(self): - """ - Return the atom molecule index - - :type: int - """ - return self.get("mol", self.index) - - @property - def mass(self): - """ - Return the atom mass - - :type: float - """ - return self.get("mass", self.index) - - @property - def radius(self): - """ - Return the particle radius - - :type: float - """ - return self.get("radius", self.index) - - @property - def position(self): - """ - :getter: Return position of atom - :setter: Set position of atom - :type: numpy.array (float, float, float) - """ - return self.get("x", self.index) - - @position.setter - def position(self, value): - current = self.position - current[:] = value - - @property - def velocity(self): - """ - :getter: Return velocity of atom - :setter: Set velocity of atom - :type: numpy.array (float, float, float) - """ - return self.get("v", self.index) - - @velocity.setter - def velocity(self, value): - current = self.velocity - current[:] = value - - @property - def force(self): - """ - Return the total force acting on the atom - - :type: numpy.array (float, float, float) - """ - return self.get("f", self.index) - - @force.setter - def force(self, value): - current = self.force - current[:] = value - - @property - def torque(self): - """ - Return the total torque acting on the atom - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @force.setter - def torque(self, value): - current = self.torque - current[:] = value - - @property - def omega(self): - """ - Return the rotational velocity of the particle - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @omega.setter - def omega(self, value): - current = self.torque - current[:] = value - - @property - def torque(self): - """ - Return the total torque acting on the particle - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @torque.setter - def torque(self, value): - current = self.torque - current[:] = value - - @property - def angular_momentum(self): - """ - Return the angular momentum of the particle - - :type: numpy.array (float, float, float) - """ - return self.get("angmom", self.index) - - @angular_momentum.setter - def angular_momentum(self, value): - current = self.angular_momentum - current[:] = value - - @property - def charge(self): - """ - Return the atom charge - - :type: float - """ - return self.get("q", self.index) - -# ------------------------------------------------------------------------- - -class Atom2D(Atom): - """ - A wrapper class then represents a single 2D atom inside of LAMMPS - - Inherits all properties from the :py:class:`Atom` class, but returns 2D versions - of position, velocity, and force. - - It provides access to properties of the atom and allows you to change some of them. - """ - def __init__(self, pylammps_instance, index): - super(Atom2D, self).__init__(pylammps_instance, index) - - @property - def position(self): - """Access to coordinates of an atom - - :getter: Return position of atom - :setter: Set position of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).position[0:2] - - @position.setter - def position(self, value): - current = self.position - current[:] = value - - @property - def velocity(self): - """Access to velocity of an atom - :getter: Return velocity of atom - :setter: Set velocity of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).velocity[0:2] - - @velocity.setter - def velocity(self, value): - current = self.velocity - current[:] = value - - @property - def force(self): - """Access to force of an atom - :getter: Return force of atom - :setter: Set force of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).force[0:2] - - @force.setter - def force(self, value): - current = self.force - current[:] = value - -# ------------------------------------------------------------------------- - -class variable_set: - def __init__(self, name, variable_dict): - self._name = name - array_pattern = re.compile(r"(?P.+)\[(?P[0-9]+)\]") - - for key, value in variable_dict.items(): - m = array_pattern.match(key) - if m: - g = m.groupdict() - varname = g['arr'] - idx = int(g['index']) - if varname not in self.__dict__: - self.__dict__[varname] = {} - self.__dict__[varname][idx] = value - else: - self.__dict__[key] = value - - def __str__(self): - return "{}({})".format(self._name, ','.join(["{}={}".format(k, self.__dict__[k]) for k in self.__dict__.keys() if not k.startswith('_')])) - - def __dir__(self): - return [k for k in self.__dict__.keys() if not k.startswith('_')] - - def __repr__(self): - return self.__str__() - -# ------------------------------------------------------------------------- - -def get_thermo_data(output): - """ traverse output of runs and extract thermo data columns """ - if isinstance(output, str): - lines = output.splitlines() - else: - lines = output - - runs = [] - columns = [] - in_run = False - current_run = {} - - for line in lines: - if line.startswith("Per MPI rank memory allocation"): - in_run = True - elif in_run and len(columns) == 0: - # first line after memory usage are column names - columns = line.split() - - current_run = {} - - for col in columns: - current_run[col] = [] - - elif line.startswith("Loop time of "): - in_run = False - columns = [] - thermo_data = variable_set('ThermoData', current_run) - r = {'thermo' : thermo_data } - runs.append(namedtuple('Run', list(r.keys()))(*list(r.values()))) - elif in_run and len(columns) > 0: - items = line.split() - # Convert thermo output and store it. - # It must have the same number of columns and - # all of them must be convertible to floats. - # Otherwise we ignore the line - if len(items) == len(columns): - try: - values = [float(x) for x in items] - for i, col in enumerate(columns): - current_run[col].append(values[i]) - except ValueError: - # cannot convert. must be a non-thermo output. ignore. - pass - - return runs - -# ------------------------------------------------------------------------- -# ------------------------------------------------------------------------- - -class PyLammps(object): - """ - This is a Python wrapper class around the lower-level - :py:class:`lammps` class, exposing a more Python-like, - object-oriented interface for prototyping system inside of IPython and - Jupyter notebooks. - - It either creates its own instance of :py:class:`lammps` or can be - initialized with an existing instance. The arguments are the same of the - lower-level interface. The original interface can still be accessed via - :py:attr:`PyLammps.lmp`. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - :param verbose: print all LAMMPS output to stdout - :type verbose: bool - - :ivar lmp: instance of original LAMMPS Python interface - :vartype lmp: :py:class:`lammps` - - :ivar runs: list of completed runs, each storing the thermo output - :vartype run: list - """ - - def __init__(self, name="", cmdargs=None, ptr=None, comm=None, verbose=False): - self.has_echo = False - self.verbose = verbose - - if cmdargs: - if '-echo' in cmdargs: - idx = cmdargs.index('-echo') - # ensures that echo line is ignored during output capture - self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both') - - if ptr: - if isinstance(ptr,PyLammps): - self.lmp = ptr.lmp - elif isinstance(ptr,lammps): - self.lmp = ptr - else: - self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) - else: - self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm) - print("LAMMPS output is captured by PyLammps wrapper") - self._cmd_history = [] - self._enable_cmd_history = False - self.runs = [] - - def __enter__(self): - return self - - def __exit__(self, ex_type, ex_value, ex_traceback): - self.close() - - def __del__(self): - if self.lmp: self.lmp.close() - self.lmp = None - - def close(self): - """Explicitly delete a LAMMPS instance - - This is a wrapper around the :py:meth:`lammps.close` of the Python interface. - """ - if self.lmp: self.lmp.close() - self.lmp = None - - def version(self): - """Return a numerical representation of the LAMMPS version in use. - - This is a wrapper around the :py:meth:`lammps.version` function of the Python interface. - - :return: version number - :rtype: int - """ - return self.lmp.version() - - def file(self, file): - """Read LAMMPS commands from a file. - - This is a wrapper around the :py:meth:`lammps.file` function of the Python interface. - - :param path: Name of the file/path with LAMMPS commands - :type path: string - """ - self.lmp.file(file) - - @property - def enable_cmd_history(self): - """ - :getter: Return whether command history is saved - :setter: Set if command history should be saved - :type: bool - """ - return self._enable_cmd_history - - @enable_cmd_history.setter - def enable_cmd_history(self, value): - """ - :getter: Return whether command history is saved - :setter: Set if command history should be saved - :type: bool - """ - self._enable_cmd_history = (value == True) - - def write_script(self, filepath): - """ - Write LAMMPS script file containing all commands executed up until now - - :param filepath: path to script file that should be written - :type filepath: string - """ - with open(filepath, "w") as f: - for cmd in self._cmd_history: - print(cmd, file=f) - - def clear_cmd_history(self): - """ - Clear LAMMPS command history up to this point - """ - self._cmd_history = [] - - def command(self, cmd): - """ - Execute LAMMPS command - - If :py:attr:`PyLammps.enable_cmd_history` is set to ``True``, commands executed - will be recorded. The entire command history can be written to a file using - :py:meth:`PyLammps.write_script()`. To clear the command history, use - :py:meth:`PyLammps.clear_cmd_history()`. - - :param cmd: command string that should be executed - :type: cmd: string - """ - self.lmp.command(cmd) - - if self.enable_cmd_history: - self._cmd_history.append(cmd) - - def run(self, *args, **kwargs): - """ - Execute LAMMPS run command with given arguments - - All thermo output during the run is captured and saved as new entry in - :py:attr:`PyLammps.runs`. The latest run can be retrieved by - :py:attr:`PyLammps.last_run`. - """ - output = self.__getattr__('run')(*args, **kwargs) - self.runs += get_thermo_data(output) - return output - - @property - def last_run(self): - """ - Return data produced of last completed run command - - :getter: Returns an object containing information about the last run command - :type: dict - """ - if len(self.runs) > 0: - return self.runs[-1] - return None - - @property - def atoms(self): - """ - All atoms of this LAMMPS instance - - :getter: Returns a list of atoms currently in the system - :type: AtomList - """ - return AtomList(self) - - @property - def system(self): - """ - The system state of this LAMMPS instance - - :getter: Returns an object with properties storing the current system state - :type: namedtuple - """ - output = self.lmp_info("system") - output = output[output.index("System information:")+1:] - d = self._parse_info_system(output) - return namedtuple('System', d.keys())(*d.values()) - - @property - def communication(self): - """ - The communication state of this LAMMPS instance - - :getter: Returns an object with properties storing the current communication state - :type: namedtuple - """ - output = self.lmp_info("communication") - output = output[output.index("Communication information:")+1:] - d = self._parse_info_communication(output) - return namedtuple('Communication', d.keys())(*d.values()) - - @property - def computes(self): - """ - The list of active computes of this LAMMPS instance - - :getter: Returns a list of computes that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("computes") - output = output[output.index("Compute information:")+1:] - return self._parse_element_list(output) - - @property - def dumps(self): - """ - The list of active dumps of this LAMMPS instance - - :getter: Returns a list of dumps that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("dumps") - output = output[output.index("Dump information:")+1:] - return self._parse_element_list(output) - - @property - def fixes(self): - """ - The list of active fixes of this LAMMPS instance - - :getter: Returns a list of fixes that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("fixes") - output = output[output.index("Fix information:")+1:] - return self._parse_element_list(output) - - @property - def groups(self): - """ - The list of active atom groups of this LAMMPS instance - - :getter: Returns a list of atom groups that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("groups") - output = output[output.index("Group information:")+1:] - return self._parse_groups(output) - - @property - def variables(self): - """ - Returns a dictionary of all variables defined in the current LAMMPS instance - - :getter: Returns a dictionary of all variables that are defined in this LAMMPS instance - :type: dict - """ - output = self.lmp_info("variables") - output = output[output.index("Variable information:")+1:] - variables = {} - for v in self._parse_element_list(output): - variables[v['name']] = Variable(self, v['name'], v['style'], v['def']) - return variables - - def eval(self, expr): - """ - Evaluate expression - - :param expr: the expression string that should be evaluated inside of LAMMPS - :type expr: string - - :return: the value of the evaluated expression - :rtype: float if numeric, string otherwise - """ - value = self.lmp_print('"$(%s)"' % expr).strip() - try: - return float(value) - except ValueError: - return value - - def _split_values(self, line): - return [x.strip() for x in line.split(',')] - - def _get_pair(self, value): - return [x.strip() for x in value.split('=')] - - def _parse_info_system(self, output): - system = {} - - for line in output: - if line.startswith("Units"): - system['units'] = self._get_pair(line)[1] - elif line.startswith("Atom style"): - system['atom_style'] = self._get_pair(line)[1] - elif line.startswith("Atom map"): - system['atom_map'] = self._get_pair(line)[1] - elif line.startswith("Atoms"): - parts = self._split_values(line) - system['natoms'] = int(self._get_pair(parts[0])[1]) - system['ntypes'] = int(self._get_pair(parts[1])[1]) - system['style'] = self._get_pair(parts[2])[1] - elif line.startswith("Kspace style"): - system['kspace_style'] = self._get_pair(line)[1] - elif line.startswith("Dimensions"): - system['dimensions'] = int(self._get_pair(line)[1]) - elif line.startswith("Orthogonal box"): - system['orthogonal_box'] = [float(x) for x in self._get_pair(line)[1].split('x')] - elif line.startswith("Boundaries"): - system['boundaries'] = self._get_pair(line)[1] - elif line.startswith("xlo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("ylo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("zlo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("Molecule type"): - system['molecule_type'] = self._get_pair(line)[1] - elif line.startswith("Bonds"): - parts = self._split_values(line) - system['nbonds'] = int(self._get_pair(parts[0])[1]) - system['nbondtypes'] = int(self._get_pair(parts[1])[1]) - system['bond_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Angles"): - parts = self._split_values(line) - system['nangles'] = int(self._get_pair(parts[0])[1]) - system['nangletypes'] = int(self._get_pair(parts[1])[1]) - system['angle_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Dihedrals"): - parts = self._split_values(line) - system['ndihedrals'] = int(self._get_pair(parts[0])[1]) - system['ndihedraltypes'] = int(self._get_pair(parts[1])[1]) - system['dihedral_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Impropers"): - parts = self._split_values(line) - system['nimpropers'] = int(self._get_pair(parts[0])[1]) - system['nimpropertypes'] = int(self._get_pair(parts[1])[1]) - system['improper_style'] = self._get_pair(parts[2])[1] - - return system - - def _parse_info_communication(self, output): - comm = {} - - for line in output: - if line.startswith("MPI library"): - comm['mpi_version'] = line.split(':')[1].strip() - elif line.startswith("Comm style"): - parts = self._split_values(line) - comm['comm_style'] = self._get_pair(parts[0])[1] - comm['comm_layout'] = self._get_pair(parts[1])[1] - elif line.startswith("Processor grid"): - comm['proc_grid'] = [int(x) for x in self._get_pair(line)[1].split('x')] - elif line.startswith("Communicate velocities for ghost atoms"): - comm['ghost_velocity'] = (self._get_pair(line)[1] == "yes") - elif line.startswith("Nprocs"): - parts = self._split_values(line) - comm['nprocs'] = int(self._get_pair(parts[0])[1]) - comm['nthreads'] = int(self._get_pair(parts[1])[1]) - return comm - - def _parse_element_list(self, output): - elements = [] - - for line in output: - if not line or (":" not in line): continue - element_info = self._split_values(line.split(':')[1].strip()) - element = {'name': element_info[0]} - for key, value in [self._get_pair(x) for x in element_info[1:]]: - element[key] = value - elements.append(element) - return elements - - def _parse_groups(self, output): - groups = [] - group_pattern = re.compile(r"(?P.+) \((?P.+)\)") - - for line in output: - m = group_pattern.match(line.split(':')[1].strip()) - group = {'name': m.group('name'), 'type': m.group('type')} - groups.append(group) - return groups - - def lmp_print(self, s): - """ needed for Python2 compatibility, since print is a reserved keyword """ - return self.__getattr__("print")(s) - - def __dir__(self): - return sorted(set(['angle_coeff', 'angle_style', 'atom_modify', 'atom_style', 'atom_style', - 'bond_coeff', 'bond_style', 'boundary', 'change_box', 'communicate', 'compute', - 'create_atoms', 'create_box', 'delete_atoms', 'delete_bonds', 'dielectric', - 'dihedral_coeff', 'dihedral_style', 'dimension', 'dump', 'fix', 'fix_modify', - 'group', 'improper_coeff', 'improper_style', 'include', 'kspace_modify', - 'kspace_style', 'lattice', 'mass', 'minimize', 'min_style', 'neighbor', - 'neigh_modify', 'newton', 'nthreads', 'pair_coeff', 'pair_modify', - 'pair_style', 'processors', 'read', 'read_data', 'read_restart', 'region', - 'replicate', 'reset_timestep', 'restart', 'run', 'run_style', 'thermo', - 'thermo_modify', 'thermo_style', 'timestep', 'undump', 'unfix', 'units', - 'variable', 'velocity', 'write_restart'] + self.lmp.available_styles("command"))) - - def lmp_info(self, s): - # skip anything before and after Info-Info-Info - # also skip timestamp line - output = self.__getattr__("info")(s) - indices = [index for index, line in enumerate(output) if line.startswith("Info-Info-Info-Info")] - start = indices[0] - end = indices[1] - return [line for line in output[start+2:end] if line] - - def __getattr__(self, name): - """ - This method is where the Python 'magic' happens. If a method is not - defined by the class PyLammps, it assumes it is a LAMMPS command. It takes - all the arguments, concatinates them to a single string, and executes it using - :py:meth:`lammps.PyLammps.command()`. - - :param verbose: Print output of command - :type verbose: bool - :return: line or list of lines of output, None if no output - :rtype: list or string - """ - def handler(*args, **kwargs): - cmd_args = [name] + [str(x) for x in args] - self.lmp.flush_buffers() - - with OutputCapture() as capture: - cmd = ' '.join(cmd_args) - self.command(cmd) - self.lmp.flush_buffers() - output = capture.output - - comm = self.lmp.get_mpi_comm() - if comm: - output = self.lmp.comm.bcast(output, root=0) - - if self.verbose or ('verbose' in kwargs and kwargs['verbose']): - print(output, end = '') - - lines = output.splitlines() - - if self.has_echo: - lines = lines[1:] - - if len(lines) > 1: - return lines - elif len(lines) == 1: - return lines[0] - return None - - return handler - - -class IPyLammps(PyLammps): - """ - IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps interface - - It either creates its own instance of :py:class:`lammps` or can be - initialized with an existing instance. The arguments are the same of the - lower-level interface. The original interface can still be accessed via - :py:attr:`PyLammps.lmp`. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - """ - - def __init__(self,name="",cmdargs=None,ptr=None,comm=None): - super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) - - def image(self, filename="snapshot.png", group="all", color="type", diameter="type", - size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"): - """ Generate image using write_dump command and display it - - See :doc:`dump image ` for more information. - - :param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG - :type filename: string - :param group: the group of atoms write_image should use - :type group: string - :param color: name of property used to determine color - :type color: string - :param diameter: name of property used to determine atom diameter - :type diameter: string - :param size: dimensions of image - :type size: tuple (width, height) - :param view: view parameters - :type view: tuple (theta, phi) - :param center: center parameters - :type center: tuple (flag, center_x, center_y, center_z) - :param up: vector pointing to up direction - :type up: tuple (up_x, up_y, up_z) - :param zoom: zoom factor - :type zoom: float - :param background_color: background color of scene - :type background_color: string - - :return: Image instance used to display image in notebook - :rtype: :py:class:`IPython.core.display.Image` - """ - cmd_args = [group, "image", filename, color, diameter] - - if size is not None: - width = size[0] - height = size[1] - cmd_args += ["size", width, height] - - if view is not None: - theta = view[0] - phi = view[1] - cmd_args += ["view", theta, phi] - - if center is not None: - flag = center[0] - Cx = center[1] - Cy = center[2] - Cz = center[3] - cmd_args += ["center", flag, Cx, Cy, Cz] - - if up is not None: - Ux = up[0] - Uy = up[1] - Uz = up[2] - cmd_args += ["up", Ux, Uy, Uz] - - if zoom is not None: - cmd_args += ["zoom", zoom] - - cmd_args.append("modify backcolor " + background_color) - - self.write_dump(*cmd_args) - from IPython.core.display import Image - return Image(filename) - - def video(self, filename): - """ - Load video from file - - Can be used to visualize videos from :doc:`dump movie `. - - :param filename: Path to video file - :type filename: string - :return: HTML Video Tag used by notebook to embed a video - :rtype: :py:class:`IPython.display.HTML` - """ - from IPython.display import HTML - return HTML("") From 8f431c5904f1324d981aa149ff3e3896e8b862c7 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:05:03 -0600 Subject: [PATCH 65/75] Add python/lammps directory back --- python/lammps/__init__.py | 49 + python/lammps/constants.py | 48 + python/lammps/core.py | 2097 +++++++++++++++++++++++++++++++ python/lammps/data.py | 92 ++ python/lammps/formats.py | 227 ++++ python/lammps/mliap/__init__.py | 20 + python/lammps/mliap/loader.py | 52 + python/lammps/mliap/pytorch.py | 326 +++++ python/lammps/numpy_wrapper.py | 483 +++++++ python/lammps/pylammps.py | 990 +++++++++++++++ 10 files changed, 4384 insertions(+) create mode 100644 python/lammps/__init__.py create mode 100644 python/lammps/constants.py create mode 100644 python/lammps/core.py create mode 100644 python/lammps/data.py create mode 100644 python/lammps/formats.py create mode 100644 python/lammps/mliap/__init__.py create mode 100644 python/lammps/mliap/loader.py create mode 100644 python/lammps/mliap/pytorch.py create mode 100644 python/lammps/numpy_wrapper.py create mode 100644 python/lammps/pylammps.py diff --git a/python/lammps/__init__.py b/python/lammps/__init__.py new file mode 100644 index 0000000000..fc35e45225 --- /dev/null +++ b/python/lammps/__init__.py @@ -0,0 +1,49 @@ +""" +LAMMPS module global members: + +.. data:: __version__ + + Numerical representation of the LAMMPS version this + module was taken from. Has the same format as the + result of :py:func:`lammps.version`. +""" + +from .constants import * # lgtm [py/polluting-import] +from .core import * # lgtm [py/polluting-import] +from .data import * # lgtm [py/polluting-import] +from .pylammps import * # lgtm [py/polluting-import] + +# convert installed module string version to numeric version +def get_version_number(): + import time + from os.path import join + from sys import version_info + + # must report 0 when inside LAMMPS source tree + if __file__.find(join('python', 'lammps', '__init__.py')) > 0: + return 0 + + vstring = None + if version_info.major == 3 and version_info.minor >= 8: + from importlib.metadata import version, PackageNotFoundError + try: + vstring = version('lammps') + except PackageNotFoundError: + # nothing to do, ignore + pass + + else: + from pkg_resources import get_distribution, DistributionNotFound + try: + vstring = get_distribution('lammps').version + except DistributionNotFound: + # nothing to do, ignore + pass + + if not vstring: + return 0 + + t = time.strptime(vstring, "%Y.%m.%d") + return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday + +__version__ = get_version_number() diff --git a/python/lammps/constants.py b/python/lammps/constants.py new file mode 100644 index 0000000000..a50d58b28f --- /dev/null +++ b/python/lammps/constants.py @@ -0,0 +1,48 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +# various symbolic constants to be used +# in certain calls to select data formats +LAMMPS_AUTODETECT = None +LAMMPS_INT = 0 +LAMMPS_INT_2D = 1 +LAMMPS_DOUBLE = 2 +LAMMPS_DOUBLE_2D = 3 +LAMMPS_INT64 = 4 +LAMMPS_INT64_2D = 5 +LAMMPS_STRING = 6 + +# these must be kept in sync with the enums in library.h +LMP_STYLE_GLOBAL = 0 +LMP_STYLE_ATOM = 1 +LMP_STYLE_LOCAL = 2 + +LMP_TYPE_SCALAR = 0 +LMP_TYPE_VECTOR = 1 +LMP_TYPE_ARRAY = 2 +LMP_SIZE_VECTOR = 3 +LMP_SIZE_ROWS = 4 +LMP_SIZE_COLS = 5 + +LMP_VAR_EQUAL = 0 +LMP_VAR_ATOM = 1 + +# ------------------------------------------------------------------------- + +def get_ctypes_int(size): + from ctypes import c_int, c_int32, c_int64 + if size == 4: + return c_int32 + elif size == 8: + return c_int64 + return c_int diff --git a/python/lammps/core.py b/python/lammps/core.py new file mode 100644 index 0000000000..930a40a4b0 --- /dev/null +++ b/python/lammps/core.py @@ -0,0 +1,2097 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- +# Python wrapper for the LAMMPS library via ctypes + +# for python2/3 compatibility + +from __future__ import print_function + +import os +import sys +from ctypes import * # lgtm [py/polluting-import] +from os.path import dirname,abspath,join +from inspect import getsourcefile + +from .constants import * # lgtm [py/polluting-import] +from .data import * # lgtm [py/polluting-import] + +# ------------------------------------------------------------------------- + +class MPIAbortException(Exception): + def __init__(self, message): + self.message = message + + def __str__(self): + return repr(self.message) + +# ------------------------------------------------------------------------- + +class ExceptionCheck: + """Utility class to rethrow LAMMPS C++ exceptions as Python exceptions""" + def __init__(self, lmp): + self.lmp = lmp + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + if self.lmp.has_exceptions and self.lmp.lib.lammps_has_error(self.lmp.lmp): + raise self.lmp._lammps_exception + +# ------------------------------------------------------------------------- + +class lammps(object): + """Create an instance of the LAMMPS Python class. + + .. _mpi4py_docs: https://mpi4py.readthedocs.io/ + + This is a Python wrapper class that exposes the LAMMPS C-library + interface to Python. It either requires that LAMMPS has been compiled + as shared library which is then dynamically loaded via the ctypes + Python module or that this module called from a Python function that + is called from a Python interpreter embedded into a LAMMPS executable, + for example through the :doc:`python invoke ` command. + When the class is instantiated it calls the :cpp:func:`lammps_open` + function of the LAMMPS C-library interface, which in + turn will create an instance of the :cpp:class:`LAMMPS ` + C++ class. The handle to this C++ class is stored internally + and automatically passed to the calls to the C library interface. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + """ + + # ------------------------------------------------------------------------- + # create an instance of LAMMPS + + def __init__(self,name='',cmdargs=None,ptr=None,comm=None): + self.comm = comm + self.opened = 0 + + # determine module file location + + modpath = dirname(abspath(getsourcefile(lambda:0))) + # for windows installers the shared library is in a different folder + winpath = abspath(os.path.join(modpath,'..','..','bin')) + # allow override for running tests on Windows + if (os.environ.get("LAMMPSDLLPATH")): + winpath = os.environ.get("LAMMPSDLLPATH") + self.lib = None + self.lmp = None + + # if a pointer to a LAMMPS object is handed in + # when being called from a Python interpreter + # embedded into a LAMMPS executable, all library + # symbols should already be available so we do not + # load a shared object. + + try: + if ptr is not None: self.lib = CDLL("",RTLD_GLOBAL) + except OSError: + self.lib = None + + # load liblammps.so unless name is given + # if name = "g++", load liblammps_g++.so + # try loading the LAMMPS shared object from the location + # of the lammps package with an absolute path, + # so that LD_LIBRARY_PATH does not need to be set for regular install + # fall back to loading with a relative path, + # typically requires LD_LIBRARY_PATH to be set appropriately + # guess shared library extension based on OS, if not inferred from actual file + + if any([f.startswith('liblammps') and f.endswith('.dylib') + for f in os.listdir(modpath)]): + lib_ext = ".dylib" + elif any([f.startswith('liblammps') and f.endswith('.dll') + for f in os.listdir(modpath)]): + lib_ext = ".dll" + elif os.path.exists(winpath) and any([f.startswith('liblammps') and f.endswith('.dll') + for f in os.listdir(winpath)]): + lib_ext = ".dll" + modpath = winpath + elif any([f.startswith('liblammps') and f.endswith('.so') + for f in os.listdir(modpath)]): + lib_ext = ".so" + else: + import platform + if platform.system() == "Darwin": + lib_ext = ".dylib" + elif platform.system() == "Windows": + lib_ext = ".dll" + else: + lib_ext = ".so" + + if not self.lib: + if name: + libpath = join(modpath,"liblammps_%s" % name + lib_ext) + else: + libpath = join(modpath,"liblammps" + lib_ext) + if not os.path.isfile(libpath): + if name: + libpath = "liblammps_%s" % name + lib_ext + else: + libpath = "liblammps" + lib_ext + self.lib = CDLL(libpath,RTLD_GLOBAL) + + # declare all argument and return types for all library methods here. + # exceptions are where the arguments depend on certain conditions and + # then are defined where the functions are used. + self.lib.lammps_extract_setting.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_setting.restype = c_int + + # set default types + # needed in later declarations + self.c_bigint = get_ctypes_int(self.extract_setting("bigint")) + self.c_tagint = get_ctypes_int(self.extract_setting("tagint")) + self.c_imageint = get_ctypes_int(self.extract_setting("imageint")) + + self.lib.lammps_open.restype = c_void_p + self.lib.lammps_open_no_mpi.restype = c_void_p + self.lib.lammps_close.argtypes = [c_void_p] + self.lib.lammps_flush_buffers.argtypes = [c_void_p] + self.lib.lammps_free.argtypes = [c_void_p] + + self.lib.lammps_file.argtypes = [c_void_p, c_char_p] + self.lib.lammps_file.restype = None + + self.lib.lammps_command.argtypes = [c_void_p, c_char_p] + self.lib.lammps_command.restype = c_char_p + self.lib.lammps_commands_list.restype = None + self.lib.lammps_commands_string.argtypes = [c_void_p, c_char_p] + self.lib.lammps_commands_string.restype = None + + self.lib.lammps_get_natoms.argtypes = [c_void_p] + self.lib.lammps_get_natoms.restype = c_double + self.lib.lammps_extract_box.argtypes = \ + [c_void_p,POINTER(c_double),POINTER(c_double), + POINTER(c_double),POINTER(c_double),POINTER(c_double), + POINTER(c_int),POINTER(c_int)] + self.lib.lammps_extract_box.restype = None + + self.lib.lammps_reset_box.argtypes = \ + [c_void_p,POINTER(c_double),POINTER(c_double),c_double,c_double,c_double] + self.lib.lammps_reset_box.restype = None + + self.lib.lammps_gather_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_atoms.restype = None + + self.lib.lammps_gather_atoms_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_atoms_concat.restype = None + + self.lib.lammps_gather_atoms_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_gather_atoms_subset.restype = None + + self.lib.lammps_scatter_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_scatter_atoms.restype = None + + self.lib.lammps_scatter_atoms_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_scatter_atoms_subset.restype = None + + self.lib.lammps_gather_bonds.argtypes = [c_void_p,c_void_p] + self.lib.lammps_gather_bonds.restype = None + + self.lib.lammps_gather.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather.restype = None + + self.lib.lammps_gather_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_concat.restype = None + + self.lib.lammps_gather_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_gather_subset.restype = None + + self.lib.lammps_scatter.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_scatter.restype = None + + self.lib.lammps_scatter_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_scatter_subset.restype = None + + + self.lib.lammps_find_pair_neighlist.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int] + self.lib.lammps_find_pair_neighlist.restype = c_int + + self.lib.lammps_find_fix_neighlist.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_find_fix_neighlist.restype = c_int + + self.lib.lammps_find_compute_neighlist.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_find_compute_neighlist.restype = c_int + + self.lib.lammps_neighlist_num_elements.argtypes = [c_void_p, c_int] + self.lib.lammps_neighlist_num_elements.restype = c_int + + self.lib.lammps_neighlist_element_neighbors.argtypes = \ + [c_void_p, c_int, c_int, POINTER(c_int), POINTER(c_int), POINTER(POINTER(c_int))] + self.lib.lammps_neighlist_element_neighbors.restype = None + + self.lib.lammps_is_running.argtypes = [c_void_p] + self.lib.lammps_is_running.restype = c_int + + self.lib.lammps_force_timeout.argtypes = [c_void_p] + + self.lib.lammps_has_error.argtypes = [c_void_p] + self.lib.lammps_has_error.restype = c_int + + self.lib.lammps_get_last_error_message.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_get_last_error_message.restype = c_int + + self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_global_datatype.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_global_datatype.restype = c_int + self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int] + + self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p] + self.lib.lammps_get_thermo.restype = c_double + + self.lib.lammps_encode_image_flags.restype = self.c_imageint + + self.lib.lammps_config_package_name.argtypes = [c_int, c_char_p, c_int] + self.lib.lammps_config_accelerator.argtypes = [c_char_p, c_char_p, c_char_p] + + self.lib.lammps_set_variable.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_has_style.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_style_count.argtypes = [c_void_p, c_char_p] + + self.lib.lammps_style_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] + + self.lib.lammps_has_id.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_id_count.argtypes = [c_void_p, c_char_p] + + self.lib.lammps_id_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] + + self.lib.lammps_plugin_count.argtypes = [ ] + self.lib.lammps_plugin_name.argtypes = [c_int, c_char_p, c_char_p, c_int] + + self.lib.lammps_version.argtypes = [c_void_p] + + self.lib.lammps_get_os_info.argtypes = [c_char_p, c_int] + self.lib.lammps_get_gpu_device_info.argtypes = [c_char_p, c_int] + + self.lib.lammps_get_mpi_comm.argtypes = [c_void_p] + + self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)] + + self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_atom_datatype.restype = c_int + + self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] + + self.lib.lammps_extract_variable.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_fix_external_get_force.argtypes = [c_void_p, c_char_p] + self.lib.lammps_fix_external_get_force.restype = POINTER(POINTER(c_double)) + + self.lib.lammps_fix_external_set_energy_global.argtypes = [c_void_p, c_char_p, c_double] + self.lib.lammps_fix_external_set_virial_global.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] + + self.lib.lammps_fix_external_set_vector_length.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_fix_external_set_vector.argtypes = [c_void_p, c_char_p, c_int, c_double] + + # detect if Python is using a version of mpi4py that can pass communicators + # only needed if LAMMPS has been compiled with MPI support. + self.has_mpi4py = False + if self.has_mpi_support: + try: + from mpi4py import __version__ as mpi4py_version + # tested to work with mpi4py versions 2 and 3 + self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3'] + except ImportError: + # ignore failing import + pass + + # if no ptr provided, create an instance of LAMMPS + # we can pass an MPI communicator from mpi4py v2.0.0 and later + # no_mpi call lets LAMMPS use MPI_COMM_WORLD + # cargs = array of C strings from args + # if ptr, then are embedding Python in LAMMPS input script + # ptr is the desired instance of LAMMPS + # just convert it to ctypes ptr and store in self.lmp + + if ptr is None: + + # with mpi4py v2+, we can pass MPI communicators to LAMMPS + # need to adjust for type of MPI communicator object + # allow for int (like MPICH) or void* (like OpenMPI) + if self.has_mpi_support and self.has_mpi4py: + from mpi4py import MPI + self.MPI = MPI + + if comm is not None: + if not self.has_mpi_support: + raise Exception('LAMMPS not compiled with real MPI library') + if not self.has_mpi4py: + raise Exception('Python mpi4py version is not 2 or 3') + if self.MPI._sizeof(self.MPI.Comm) == sizeof(c_int): + MPI_Comm = c_int + else: + MPI_Comm = c_void_p + + # Detect whether LAMMPS and mpi4py definitely use different MPI libs + if sizeof(MPI_Comm) != self.lib.lammps_config_has_mpi_support(): + raise Exception('Inconsistent MPI library in LAMMPS and mpi4py') + + narg = 0 + cargs = None + if cmdargs is not None: + cmdargs.insert(0,"lammps") + narg = len(cmdargs) + for i in range(narg): + if type(cmdargs[i]) is str: + cmdargs[i] = cmdargs[i].encode() + cargs = (c_char_p*narg)(*cmdargs) + self.lib.lammps_open.argtypes = [c_int, c_char_p*narg, MPI_Comm, c_void_p] + else: + self.lib.lammps_open.argtypes = [c_int, c_char_p, MPI_Comm, c_void_p] + + self.opened = 1 + comm_ptr = self.MPI._addressof(comm) + comm_val = MPI_Comm.from_address(comm_ptr) + self.lmp = c_void_p(self.lib.lammps_open(narg,cargs,comm_val,None)) + + else: + if self.has_mpi4py and self.has_mpi_support: + self.comm = self.MPI.COMM_WORLD + self.opened = 1 + if cmdargs is not None: + cmdargs.insert(0,"lammps") + narg = len(cmdargs) + for i in range(narg): + if type(cmdargs[i]) is str: + cmdargs[i] = cmdargs[i].encode() + cargs = (c_char_p*narg)(*cmdargs) + self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p*narg, c_void_p] + self.lmp = c_void_p(self.lib.lammps_open_no_mpi(narg,cargs,None)) + else: + self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p, c_void_p] + self.lmp = c_void_p(self.lib.lammps_open_no_mpi(0,None,None)) + + else: + # magic to convert ptr to ctypes ptr + if sys.version_info >= (3, 0): + # Python 3 (uses PyCapsule API) + pythonapi.PyCapsule_GetPointer.restype = c_void_p + pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] + self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None)) + else: + # Python 2 (uses PyCObject API) + pythonapi.PyCObject_AsVoidPtr.restype = c_void_p + pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] + self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) + + # check if library initilialization failed + if not self.lmp: + raise(RuntimeError("Failed to initialize LAMMPS object")) + + # optional numpy support (lazy loading) + self._numpy = None + + self._installed_packages = None + self._available_styles = None + + # check if liblammps version matches the installed python module version + # but not for in-place usage, i.e. when the version is 0 + import lammps + if lammps.__version__ > 0 and lammps.__version__ != self.lib.lammps_version(self.lmp): + raise(AttributeError("LAMMPS Python module installed for LAMMPS version %d, but shared library is version %d" \ + % (lammps.__version__, self.lib.lammps_version(self.lmp)))) + + # add way to insert Python callback for fix external + self.callback = {} + self.FIX_EXTERNAL_CALLBACK_FUNC = CFUNCTYPE(None, py_object, self.c_bigint, c_int, POINTER(self.c_tagint), POINTER(POINTER(c_double)), POINTER(POINTER(c_double))) + self.lib.lammps_set_fix_external_callback.argtypes = [c_void_p, c_char_p, self.FIX_EXTERNAL_CALLBACK_FUNC, py_object] + self.lib.lammps_set_fix_external_callback.restype = None + + # ------------------------------------------------------------------------- + # shut-down LAMMPS instance + + def __del__(self): + self.close() + + # ------------------------------------------------------------------------- + # context manager implementation + + def __enter__(self): + return self + + def __exit__(self, ex_type, ex_value, ex_traceback): + self.close() + + # ------------------------------------------------------------------------- + + @property + def numpy(self): + """ Return object to access numpy versions of API + + It provides alternative implementations of API functions that + return numpy arrays instead of ctypes pointers. If numpy is not installed, + accessing this property will lead to an ImportError. + + :return: instance of numpy wrapper object + :rtype: numpy_wrapper + """ + if not self._numpy: + from .numpy_wrapper import numpy_wrapper + self._numpy = numpy_wrapper(self) + return self._numpy + + # ------------------------------------------------------------------------- + + def close(self): + """Explicitly delete a LAMMPS instance through the C-library interface. + + This is a wrapper around the :cpp:func:`lammps_close` function of the C-library interface. + """ + if self.lmp and self.opened: + self.lib.lammps_close(self.lmp) + self.lmp = None + self.opened = 0 + + # ------------------------------------------------------------------------- + + 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`. + + You cannot create or use any LAMMPS instances after this function is called + unless LAMMPS was compiled without MPI and without Kokkos support. + """ + self.close() + self.lib.lammps_kokkos_finalize() + self.lib.lammps_mpi_finalize() + + # ------------------------------------------------------------------------- + + def version(self): + """Return a numerical representation of the LAMMPS version in use. + + This is a wrapper around the :cpp:func:`lammps_version` function of the C-library interface. + + :return: version number + :rtype: int + """ + return self.lib.lammps_version(self.lmp) + + # ------------------------------------------------------------------------- + + def get_os_info(self): + """Return a string with information about the OS and compiler runtime + + This is a wrapper around the :cpp:func:`lammps_get_os_info` function of the C-library interface. + + :return: OS info string + :rtype: string + """ + + sb = create_string_buffer(512) + self.lib.lammps_get_os_info(sb,512) + return sb.value.decode() + + # ------------------------------------------------------------------------- + + def get_mpi_comm(self): + """Get the MPI communicator in use by the current LAMMPS instance + + This is a wrapper around the :cpp:func:`lammps_get_mpi_comm` function + of the C-library interface. It will return ``None`` if either the + LAMMPS library was compiled without MPI support or the mpi4py + Python module is not available. + + :return: MPI communicator + :rtype: MPI_Comm + """ + + if self.has_mpi4py and self.has_mpi_support: + from mpi4py import MPI + f_comm = self.lib.lammps_get_mpi_comm(self.lmp) + c_comm = MPI.Comm.f2py(f_comm) + return c_comm + else: + return None + + # ------------------------------------------------------------------------- + + @property + def _lammps_exception(self): + sb = create_string_buffer(100) + error_type = self.lib.lammps_get_last_error_message(self.lmp, sb, 100) + error_msg = sb.value.decode().strip() + + if error_type == 2: + return MPIAbortException(error_msg) + return Exception(error_msg) + + # ------------------------------------------------------------------------- + + def file(self, path): + """Read LAMMPS commands from a file. + + This is a wrapper around the :cpp:func:`lammps_file` function of the C-library interface. + It will open the file with the name/path `file` and process the LAMMPS commands line by line until + the end. The function will return when the end of the file is reached. + + :param path: Name of the file/path with LAMMPS commands + :type path: string + """ + if path: path = path.encode() + else: return + + with ExceptionCheck(self): + self.lib.lammps_file(self.lmp, path) + + # ------------------------------------------------------------------------- + + def command(self,cmd): + """Process a single LAMMPS input command from a string. + + This is a wrapper around the :cpp:func:`lammps_command` + function of the C-library interface. + + :param cmd: a single lammps command + :type cmd: string + """ + if cmd: cmd = cmd.encode() + else: return + + with ExceptionCheck(self): + self.lib.lammps_command(self.lmp,cmd) + + # ------------------------------------------------------------------------- + + def commands_list(self,cmdlist): + """Process multiple LAMMPS input commands from a list of strings. + + This is a wrapper around the + :cpp:func:`lammps_commands_list` function of + the C-library interface. + + :param cmdlist: a single lammps command + :type cmdlist: list of strings + """ + cmds = [x.encode() for x in cmdlist if type(x) is str] + narg = len(cmdlist) + args = (c_char_p * narg)(*cmds) + self.lib.lammps_commands_list.argtypes = [c_void_p, c_int, c_char_p * narg] + + with ExceptionCheck(self): + self.lib.lammps_commands_list(self.lmp,narg,args) + + # ------------------------------------------------------------------------- + + def commands_string(self,multicmd): + """Process a block of LAMMPS input commands from a string. + + This is a wrapper around the + :cpp:func:`lammps_commands_string` + function of the C-library interface. + + :param multicmd: text block of lammps commands + :type multicmd: string + """ + if type(multicmd) is str: multicmd = multicmd.encode() + + with ExceptionCheck(self): + self.lib.lammps_commands_string(self.lmp,c_char_p(multicmd)) + + # ------------------------------------------------------------------------- + + def get_natoms(self): + """Get the total number of atoms in the LAMMPS instance. + + Will be precise up to 53-bit signed integer due to the + underlying :cpp:func:`lammps_get_natoms` function returning a double. + + :return: number of atoms + :rtype: int + """ + return int(self.lib.lammps_get_natoms(self.lmp)) + + # ------------------------------------------------------------------------- + + def extract_box(self): + """Extract simulation box parameters + + This is a wrapper around the :cpp:func:`lammps_extract_box` function + of the C-library interface. Unlike in the C function, the result is + returned as a list. + + :return: list of the extracted data: boxlo, boxhi, xy, yz, xz, periodicity, box_change + :rtype: [ 3*double, 3*double, double, double, 3*int, int] + """ + boxlo = (3*c_double)() + boxhi = (3*c_double)() + xy = c_double() + yz = c_double() + xz = c_double() + periodicity = (3*c_int)() + box_change = c_int() + + with ExceptionCheck(self): + self.lib.lammps_extract_box(self.lmp,boxlo,boxhi, + byref(xy),byref(yz),byref(xz), + periodicity,byref(box_change)) + + boxlo = boxlo[:3] + boxhi = boxhi[:3] + xy = xy.value + yz = yz.value + xz = xz.value + periodicity = periodicity[:3] + box_change = box_change.value + + return boxlo,boxhi,xy,yz,xz,periodicity,box_change + + # ------------------------------------------------------------------------- + + def reset_box(self,boxlo,boxhi,xy,yz,xz): + """Reset simulation box parameters + + This is a wrapper around the :cpp:func:`lammps_reset_box` function + of the C-library interface. + + :param boxlo: new lower box boundaries + :type boxlo: list of 3 floating point numbers + :param boxhi: new upper box boundaries + :type boxhi: list of 3 floating point numbers + :param xy: xy tilt factor + :type xy: float + :param yz: yz tilt factor + :type yz: float + :param xz: xz tilt factor + :type xz: float + """ + cboxlo = (3*c_double)(*boxlo) + cboxhi = (3*c_double)(*boxhi) + with ExceptionCheck(self): + self.lib.lammps_reset_box(self.lmp,cboxlo,cboxhi,xy,yz,xz) + + # ------------------------------------------------------------------------- + + def get_thermo(self,name): + """Get current value of a thermo keyword + + This is a wrapper around the :cpp:func:`lammps_get_thermo` + function of the C-library interface. + + :param name: name of thermo keyword + :type name: string + :return: value of thermo keyword + :rtype: double or None + """ + if name: name = name.encode() + else: return None + + with ExceptionCheck(self): + return self.lib.lammps_get_thermo(self.lmp,name) + + # ------------------------------------------------------------------------- + + def extract_setting(self, name): + """Query LAMMPS about global settings that can be expressed as an integer. + + This is a wrapper around the :cpp:func:`lammps_extract_setting` + function of the C-library interface. Its documentation includes + a list of the supported keywords. + + :param name: name of the setting + :type name: string + :return: value of the setting + :rtype: int + """ + if name: name = name.encode() + else: return None + return int(self.lib.lammps_extract_setting(self.lmp,name)) + + # ------------------------------------------------------------------------- + # extract global info datatype + + def extract_global_datatype(self, name): + """Retrieve global property datatype from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_global_datatype` + function of the C-library interface. Its documentation includes a + list of the supported keywords. + This function returns ``None`` if the keyword is not + recognized. Otherwise it will return a positive integer value that + corresponds to one of the :ref:`data type ` + constants define in the :py:mod:`lammps` module. + + :param name: name of the property + :type name: string + :return: data type of global property, see :ref:`py_datatype_constants` + :rtype: int + """ + if name: name = name.encode() + else: return None + return self.lib.lammps_extract_global_datatype(self.lmp, name) + + # ------------------------------------------------------------------------- + # extract global info + + def extract_global(self, name, dtype=LAMMPS_AUTODETECT): + """Query LAMMPS about global settings of different types. + + This is a wrapper around the :cpp:func:`lammps_extract_global` function + of the C-library interface. Since there are no pointers in Python, this + method will - unlike the C function - return the value or a list of + values. The :cpp:func:`lammps_extract_global` documentation includes a + list of the supported keywords and their data types. + Since Python needs to know the data type to be able to interpret + the result, by default, this function will try to auto-detect the data type + by asking the library. You can also force a specific data type. For that + purpose the :py:mod:`lammps` module contains :ref:`data type ` + constants. This function returns ``None`` if either the keyword is not recognized, + or an invalid data type constant is used. + + :param name: name of the property + :type name: string + :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :return: value of the property or list of values or None + :rtype: int, float, list, or NoneType + """ + + if dtype == LAMMPS_AUTODETECT: + dtype = self.extract_global_datatype(name) + + # set length of vector for items that are not a scalar + vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3, + 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } + if name in vec_dict: + veclen = vec_dict[name] + elif name == 'respa_dt': + veclen = self.extract_global('respa_levels',LAMMPS_INT) + else: + veclen = 1 + + if name: name = name.encode() + else: return None + + if dtype == LAMMPS_INT: + self.lib.lammps_extract_global.restype = POINTER(c_int32) + target_type = int + elif dtype == LAMMPS_INT64: + self.lib.lammps_extract_global.restype = POINTER(c_int64) + target_type = int + elif dtype == LAMMPS_DOUBLE: + self.lib.lammps_extract_global.restype = POINTER(c_double) + target_type = float + elif dtype == LAMMPS_STRING: + self.lib.lammps_extract_global.restype = c_char_p + target_type = str + else: + target_type = None + + ptr = self.lib.lammps_extract_global(self.lmp, name) + if ptr: + if dtype == LAMMPS_STRING: + return ptr.decode('utf-8') + if veclen > 1: + result = [] + for i in range(0,veclen): + result.append(target_type(ptr[i])) + return result + else: return target_type(ptr[0]) + return None + + # ------------------------------------------------------------------------- + # extract per-atom info datatype + + def extract_atom_datatype(self, name): + """Retrieve per-atom property datatype from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_atom_datatype` + function of the C-library interface. Its documentation includes a + list of the supported keywords. + This function returns ``None`` if the keyword is not + recognized. Otherwise it will return an integer value that + corresponds to one of the :ref:`data type ` constants + defined in the :py:mod:`lammps` module. + + :param name: name of the property + :type name: string + :return: data type of per-atom property (see :ref:`py_datatype_constants`) + :rtype: int + """ + if name: name = name.encode() + else: return None + return self.lib.lammps_extract_atom_datatype(self.lmp, name) + + # ------------------------------------------------------------------------- + # extract per-atom info + + def extract_atom(self, name, dtype=LAMMPS_AUTODETECT): + """Retrieve per-atom properties from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_atom` + function of the C-library interface. Its documentation includes a + list of the supported keywords and their data types. + Since Python needs to know the data type to be able to interpret + the result, by default, this function will try to auto-detect the data type + by asking the library. You can also force a specific data type by setting ``dtype`` + to one of the :ref:`data type ` constants defined in the + :py:mod:`lammps` module. + This function returns ``None`` if either the keyword is not + recognized, or an invalid data type constant is used. + + .. note:: + + While the returned arrays of per-atom data are dimensioned + for the range [0:nmax] - as is the underlying storage - + the data is usually only valid for the range of [0:nlocal], + unless the property of interest is also updated for ghost + atoms. In some cases, this depends on a LAMMPS setting, see + for example :doc:`comm_modify vel yes `. + + :param name: name of the property + :type name: string + :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :return: requested data or ``None`` + :rtype: ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.POINTER(ctypes.c_int32)), + ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), + ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + or NoneType + """ + if dtype == LAMMPS_AUTODETECT: + dtype = self.extract_atom_datatype(name) + + if name: name = name.encode() + else: return None + + if dtype == LAMMPS_INT: + self.lib.lammps_extract_atom.restype = POINTER(c_int32) + elif dtype == LAMMPS_INT_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int32)) + elif dtype == LAMMPS_DOUBLE: + self.lib.lammps_extract_atom.restype = POINTER(c_double) + elif dtype == LAMMPS_DOUBLE_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double)) + elif dtype == LAMMPS_INT64: + self.lib.lammps_extract_atom.restype = POINTER(c_int64) + elif dtype == LAMMPS_INT64_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64)) + else: return None + + ptr = self.lib.lammps_extract_atom(self.lmp, name) + if ptr: return ptr + else: return None + + + # ------------------------------------------------------------------------- + + def extract_compute(self,cid,cstyle,ctype): + """Retrieve data from a LAMMPS compute + + This is a wrapper around the :cpp:func:`lammps_extract_compute` + function of the C-library interface. + This function returns ``None`` if either the compute id is not + recognized, or an invalid combination of :ref:`cstyle ` + and :ref:`ctype ` constants is used. The + names and functionality of the constants are the same as for + the corresponding C-library function. For requests to return + a scalar or a size, the value is returned, otherwise a pointer. + + :param cid: compute ID + :type cid: string + :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type cstyle: int + :param ctype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ctype: int + :return: requested data as scalar, pointer to 1d or 2d double array, or None + :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType + """ + if cid: cid = cid.encode() + else: return None + + if ctype == LMP_TYPE_SCALAR: + if cstyle == LMP_STYLE_GLOBAL: + self.lib.lammps_extract_compute.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + elif cstyle == LMP_STYLE_ATOM: + return None + elif cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + elif ctype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_compute.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr + + elif ctype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_compute.restype = POINTER(POINTER(c_double)) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr + + elif ctype == LMP_SIZE_COLS: + if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_ATOM or cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + elif ctype == LMP_SIZE_VECTOR or ctype == LMP_SIZE_ROWS: + if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + return None + + # ------------------------------------------------------------------------- + # extract fix info + # in case of global data, free memory for 1 double via lammps_free() + # double was allocated by library interface function + + def extract_fix(self,fid,fstyle,ftype,nrow=0,ncol=0): + """Retrieve data from a LAMMPS fix + + This is a wrapper around the :cpp:func:`lammps_extract_fix` + function of the C-library interface. + This function returns ``None`` if either the fix id is not + recognized, or an invalid combination of :ref:`fstyle ` + and :ref:`ftype ` constants is used. The + names and functionality of the constants are the same as for + the corresponding C-library function. For requests to return + a scalar or a size, the value is returned, also when accessing + global vectors or arrays, otherwise a pointer. + + :param fid: fix ID + :type fid: string + :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type fstyle: int + :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ftype: int + :param nrow: index of global vector element or row index of global array element + :type nrow: int + :param ncol: column index of global array element + :type ncol: int + :return: requested data or None + :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType + + """ + if fid: fid = fid.encode() + else: return None + + if fstyle == LMP_STYLE_GLOBAL: + if ftype in (LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): + self.lib.lammps_extract_fix.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + result = ptr[0] + self.lib.lammps_free(ptr) + return result + elif ftype in (LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): + self.lib.lammps_extract_fix.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + return ptr[0] + else: + return None + + elif fstyle == LMP_STYLE_ATOM: + if ftype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_fix.restype = POINTER(c_double) + elif ftype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) + elif ftype == LMP_SIZE_COLS: + self.lib.lammps_extract_fix.restype = POINTER(c_int) + else: + return None + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + if ftype == LMP_SIZE_COLS: + return ptr[0] + else: + return ptr + + elif fstyle == LMP_STYLE_LOCAL: + if ftype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_fix.restype = POINTER(c_double) + elif ftype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) + elif ftype in (LMP_TYPE_SCALAR, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): + self.lib.lammps_extract_fix.restype = POINTER(c_int) + else: + return None + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + if ftype in (LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): + return ptr + else: + return ptr[0] + else: + return None + + # ------------------------------------------------------------------------- + # extract variable info + # free memory for 1 double or 1 vector of doubles via lammps_free() + # for vector, must copy nlocal returned values to local c_double vector + # memory was allocated by library interface function + + def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): + """ Evaluate a LAMMPS variable and return its data + + This function is a wrapper around the function + :cpp:func:`lammps_extract_variable` of the C-library interface, + evaluates variable name and returns a copy of the computed data. + The memory temporarily allocated by the C-interface is deleted + after the data is copied to a Python variable or list. + The variable must be either an equal-style (or equivalent) + variable or an atom-style variable. The variable type has to + provided as ``vartype`` parameter which may be one of two constants: + ``LMP_VAR_EQUAL`` or ``LMP_VAR_ATOM``; it defaults to + equal-style variables. + The group parameter is only used for atom-style variables and + defaults to the group "all" if set to ``None``, which is the default. + + :param name: name of the variable to execute + :type name: string + :param group: name of group for atom-style variable + :type group: string, only for atom-style variables + :param vartype: type of variable, see :ref:`py_vartype_constants` + :type vartype: int + :return: the requested data + :rtype: c_double, (c_double), or NoneType + """ + if name: name = name.encode() + else: return None + if group: group = group.encode() + if vartype == LMP_VAR_EQUAL: + self.lib.lammps_extract_variable.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_variable(self.lmp,name,group) + if ptr: result = ptr[0] + else: return None + self.lib.lammps_free(ptr) + return result + elif vartype == LMP_VAR_ATOM: + nlocal = self.extract_global("nlocal") + result = (c_double*nlocal)() + self.lib.lammps_extract_variable.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_variable(self.lmp,name,group) + if ptr: + for i in range(nlocal): result[i] = ptr[i] + self.lib.lammps_free(ptr) + else: return None + return result + return None + + # ------------------------------------------------------------------------- + + def flush_buffers(self): + """Flush output buffers + + This is a wrapper around the :cpp:func:`lammps_flush_buffers` + function of the C-library interface. + """ + self.lib.lammps_flush_buffers(self.lmp) + + # ------------------------------------------------------------------------- + + def set_variable(self,name,value): + """Set a new value for a LAMMPS string style variable + + This is a wrapper around the :cpp:func:`lammps_set_variable` + function of the C-library interface. + + :param name: name of the variable + :type name: string + :param value: new variable value + :type value: any. will be converted to a string + :return: either 0 on success or -1 on failure + :rtype: int + """ + if name: name = name.encode() + else: return -1 + if value: value = str(value).encode() + else: return -1 + with ExceptionCheck(self): + return self.lib.lammps_set_variable(self.lmp,name,value) + + # ------------------------------------------------------------------------- + + # return vector of atom properties gathered across procs + # 3 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # dtype = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # returned data is a 1d vector - doc how it is ordered? + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def gather_atoms(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) + else: + return None + return data + + # ------------------------------------------------------------------------- + + def gather_atoms_concat(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_atoms_subset(self,name,dtype,count,ndata,ids): + if name: name = name.encode() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*ndata)*c_int)() + self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + elif dtype == 1: + data = ((count*ndata)*c_double)() + self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + else: + return None + return data + + # ------------------------------------------------------------------------- + + # scatter vector of atom properties across procs + # 2 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # assume data is of correct type and length, as created by gather_atoms() + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def scatter_atoms(self,name,dtype,count,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_atoms(self.lmp,name,dtype,count,data) + + # ------------------------------------------------------------------------- + + def scatter_atoms_subset(self,name,dtype,count,ndata,ids,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + + + # ------------------------------------------------------------------------- + + def gather_bonds(self): + """Retrieve global list of bonds + + This is a wrapper around the :cpp:func:`lammps_gather_bonds` + function of the C-library interface. + + This function returns a tuple with the number of bonds and a + flat list of ctypes integer values with the bond type, bond atom1, + bond atom2 for each bond. + + .. versionadded:: 28Jul2021 + + :return: a tuple with the number of bonds and a list of c_int or c_long + :rtype: (int, 3*nbonds*c_tagint) + """ + nbonds = self.extract_global("nbonds") + with ExceptionCheck(self): + data = ((3*nbonds)*self.c_tagint)() + self.lib.lammps_gather_bonds(self.lmp,data) + return nbonds,data + + # ------------------------------------------------------------------------- + + # return vector of atom/compute/fix properties gathered across procs + # 3 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # returned data is a 1d vector - doc how it is ordered? + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + def gather(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_concat(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_subset(self,name,dtype,count,ndata,ids): + if name: name = name.encode() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*ndata)*c_int)() + self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) + elif dtype == 1: + data = ((count*ndata)*c_double)() + self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) + else: + return None + return data + + # scatter vector of atom/compute/fix properties across procs + # 2 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # assume data is of correct type and length, as created by gather_atoms() + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def scatter(self,name,dtype,count,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter(self.lmp,name,dtype,count,data) + + def scatter_subset(self,name,dtype,count,ndata,ids,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_subset(self.lmp,name,dtype,count,ndata,ids,data) + + # ------------------------------------------------------------------------- + + def encode_image_flags(self,ix,iy,iz): + """ convert 3 integers with image flags for x-, y-, and z-direction + into a single integer like it is used internally in LAMMPS + + This method is a wrapper around the :cpp:func:`lammps_encode_image_flags` + function of library interface. + + :param ix: x-direction image flag + :type ix: int + :param iy: y-direction image flag + :type iy: int + :param iz: z-direction image flag + :type iz: int + :return: encoded image flags + :rtype: lammps.c_imageint + """ + return self.lib.lammps_encode_image_flags(ix,iy,iz) + + # ------------------------------------------------------------------------- + + def decode_image_flags(self,image): + """ Convert encoded image flag integer into list of three regular integers. + + This method is a wrapper around the :cpp:func:`lammps_decode_image_flags` + function of library interface. + + :param image: encoded image flags + :type image: lammps.c_imageint + :return: list of three image flags in x-, y-, and z- direction + :rtype: list of 3 int + """ + + flags = (c_int*3)() + self.lib.lammps_decode_image_flags(image,byref(flags)) + + return [int(i) for i in flags] + + # ------------------------------------------------------------------------- + + # create N atoms on all procs + # N = global number of atoms + # id = ID of each atom (optional, can be None) + # type = type of each atom (1 to Ntypes) (required) + # x = coords of each atom as (N,3) array (required) + # v = velocity of each atom as (N,3) array (optional, can be None) + # NOTE: how could we insure are passing correct type to LAMMPS + # e.g. for Python list or NumPy, etc + # ditto for gather_atoms() above + + def create_atoms(self,n,id,type,x,v=None,image=None,shrinkexceed=False): + """ + Create N atoms from list of coordinates and properties + + This function is a wrapper around the :cpp:func:`lammps_create_atoms` + function of the C-library interface, and the behavior is similar except + that the *v*, *image*, and *shrinkexceed* arguments are optional and + default to *None*, *None*, and *False*, respectively. With *None* being + equivalent to a ``NULL`` pointer in C. + + The lists of coordinates, types, atom IDs, velocities, image flags can + be provided in any format that may be converted into the required + internal data types. Also the list may contain more than *N* entries, + but not fewer. In the latter case, the function will return without + attempting to create atoms. You may use the :py:func:`encode_image_flags + ` method to properly combine three integers + with image flags into a single integer. + + :param n: number of atoms for which data is provided + :type n: int + :param id: list of atom IDs with at least n elements or None + :type id: list of lammps.tagint + :param type: list of atom types + :type type: list of int + :param x: list of coordinates for x-, y-, and z (flat list of 3n entries) + :type x: list of float + :param v: list of velocities for x-, y-, and z (flat list of 3n entries) or None (optional) + :type v: list of float + :param image: list of encoded image flags (optional) + :type image: list of lammps.imageint + :param shrinkexceed: whether to expand shrink-wrap boundaries if atoms are outside the box (optional) + :type shrinkexceed: bool + :return: number of atoms created. 0 if insufficient or invalid data + :rtype: int + """ + if id is not None: + id_lmp = (self.c_tagint*n)() + try: + id_lmp[:] = id[0:n] + except ValueError: + return 0 + else: + id_lmp = None + + type_lmp = (c_int*n)() + try: + type_lmp[:] = type[0:n] + except ValueError: + return 0 + + three_n = 3*n + x_lmp = (c_double*three_n)() + try: + x_lmp[:] = x[0:three_n] + except ValueError: + return 0 + + if v is not None: + v_lmp = (c_double*(three_n))() + try: + v_lmp[:] = v[0:three_n] + except ValueError: + return 0 + else: + v_lmp = None + + if image is not None: + img_lmp = (self.c_imageint*n)() + try: + img_lmp[:] = image[0:n] + except ValueError: + return 0 + else: + img_lmp = None + + if shrinkexceed: + se_lmp = 1 + else: + se_lmp = 0 + + self.lib.lammps_create_atoms.argtypes = [c_void_p, c_int, POINTER(self.c_tagint*n), + POINTER(c_int*n), POINTER(c_double*three_n), + POINTER(c_double*three_n), + POINTER(self.c_imageint*n), c_int] + with ExceptionCheck(self): + return self.lib.lammps_create_atoms(self.lmp, n, id_lmp, type_lmp, x_lmp, v_lmp, img_lmp, se_lmp) + + # ------------------------------------------------------------------------- + + @property + def has_mpi_support(self): + """ Report whether the LAMMPS shared library was compiled with a + real MPI library or in serial. + + This is a wrapper around the :cpp:func:`lammps_config_has_mpi_support` + function of the library interface. + + :return: False when compiled with MPI STUBS, otherwise True + :rtype: bool + """ + return self.lib.lammps_config_has_mpi_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def is_running(self): + """ Report whether being called from a function during a run or a minimization + + Various LAMMPS commands must not be called during an ongoing + run or minimization. This property allows to check for that. + This is a wrapper around the :cpp:func:`lammps_is_running` + function of the library interface. + + .. versionadded:: 9Oct2020 + + :return: True when called during a run otherwise false + :rtype: bool + """ + return self.lib.lammps_is_running(self.lmp) == 1 + + # ------------------------------------------------------------------------- + + def force_timeout(self): + """ Trigger an immediate timeout, i.e. a "soft stop" of a run. + + This function allows to cleanly stop an ongoing run or minimization + at the next loop iteration. + This is a wrapper around the :cpp:func:`lammps_force_timeout` + function of the library interface. + + .. versionadded:: 9Oct2020 + """ + self.lib.lammps_force_timeout(self.lmp) + + # ------------------------------------------------------------------------- + + @property + def has_exceptions(self): + """ Report whether the LAMMPS shared library was compiled with C++ + exceptions handling enabled + + This is a wrapper around the :cpp:func:`lammps_config_has_exceptions` + function of the library interface. + + :return: state of C++ exception support + :rtype: bool + """ + return self.lib.lammps_config_has_exceptions() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_gzip_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for reading and writing compressed files through ``gzip``. + + This is a wrapper around the :cpp:func:`lammps_config_has_gzip_support` + function of the library interface. + + :return: state of gzip support + :rtype: bool + """ + return self.lib.lammps_config_has_gzip_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_png_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for writing images in PNG format. + + This is a wrapper around the :cpp:func:`lammps_config_has_png_support` + function of the library interface. + + :return: state of PNG support + :rtype: bool + """ + return self.lib.lammps_config_has_png_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_jpeg_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for writing images in JPEG format. + + This is a wrapper around the :cpp:func:`lammps_config_has_jpeg_support` + function of the library interface. + + :return: state of JPEG support + :rtype: bool + """ + return self.lib.lammps_config_has_jpeg_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_ffmpeg_support(self): + """ State of support for writing movies with ``ffmpeg`` in the LAMMPS shared library + + This is a wrapper around the :cpp:func:`lammps_config_has_ffmpeg_support` + function of the library interface. + + :return: state of ffmpeg support + :rtype: bool + """ + return self.lib.lammps_config_has_ffmpeg_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def accelerator_config(self): + """ Return table with available accelerator configuration settings. + + This is a wrapper around the :cpp:func:`lammps_config_accelerator` + function of the library interface which loops over all known packages + and categories and returns enabled features as a nested dictionary + with all enabled settings as list of strings. + + :return: nested dictionary with all known enabled settings as list of strings + :rtype: dictionary + """ + + result = {} + for p in ['GPU', 'KOKKOS', 'INTEL', 'OPENMP']: + result[p] = {} + c = 'api' + result[p][c] = [] + for s in ['cuda', 'hip', 'phi', 'pthreads', 'opencl', 'openmp', 'serial']: + if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): + result[p][c].append(s) + c = 'precision' + result[p][c] = [] + for s in ['double', 'mixed', 'single']: + if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): + result[p][c].append(s) + return result + + # ------------------------------------------------------------------------- + + @property + def has_gpu_device(self): + """ Availability of GPU package compatible device + + This is a wrapper around the :cpp:func:`lammps_has_gpu_device` + function of the C library interface. + + :return: True if a GPU package compatible device is present, otherwise False + :rtype: bool + """ + return self.lib.lammps_has_gpu_device() != 0 + + # ------------------------------------------------------------------------- + + def get_gpu_device_info(self): + """Return a string with detailed information about any devices that are + usable by the GPU package. + + This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` + function of the C-library interface. + + :return: GPU device info string + :rtype: string + """ + + sb = create_string_buffer(8192) + self.lib.lammps_get_gpu_device_info(sb,8192) + return sb.value.decode() + + # ------------------------------------------------------------------------- + + @property + def installed_packages(self): + """ List of the names of enabled packages in the LAMMPS shared library + + This is a wrapper around the functions :cpp:func:`lammps_config_package_count` + and :cpp:func`lammps_config_package_name` of the library interface. + + :return + """ + if self._installed_packages is None: + self._installed_packages = [] + npackages = self.lib.lammps_config_package_count() + sb = create_string_buffer(100) + for idx in range(npackages): + self.lib.lammps_config_package_name(idx, sb, 100) + self._installed_packages.append(sb.value.decode()) + return self._installed_packages + + # ------------------------------------------------------------------------- + + def has_style(self, category, name): + """Returns whether a given style name is available in a given category + + This is a wrapper around the function :cpp:func:`lammps_has_style` + of the library interface. + + :param category: name of category + :type category: string + :param name: name of the style + :type name: string + + :return: true if style is available in given category + :rtype: bool + """ + return self.lib.lammps_has_style(self.lmp, category.encode(), name.encode()) != 0 + + # ------------------------------------------------------------------------- + + def available_styles(self, category): + """Returns a list of styles available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_style_count()` + and :cpp:func:`lammps_style_name()` of the library interface. + + :param category: name of category + :type category: string + + :return: list of style names in given category + :rtype: list + """ + if self._available_styles is None: + self._available_styles = {} + + if category not in self._available_styles: + self._available_styles[category] = [] + with ExceptionCheck(self): + nstyles = self.lib.lammps_style_count(self.lmp, category.encode()) + sb = create_string_buffer(100) + for idx in range(nstyles): + with ExceptionCheck(self): + self.lib.lammps_style_name(self.lmp, category.encode(), idx, sb, 100) + self._available_styles[category].append(sb.value.decode()) + return self._available_styles[category] + + # ------------------------------------------------------------------------- + + def has_id(self, category, name): + """Returns whether a given ID name is available in a given category + + This is a wrapper around the function :cpp:func:`lammps_has_id` + of the library interface. + + .. versionadded:: 9Oct2020 + + :param category: name of category + :type category: string + :param name: name of the ID + :type name: string + + :return: true if ID is available in given category + :rtype: bool + """ + return self.lib.lammps_has_id(self.lmp, category.encode(), name.encode()) != 0 + + # ------------------------------------------------------------------------- + + def available_ids(self, category): + """Returns a list of IDs available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_id_count()` + and :cpp:func:`lammps_id_name()` of the library interface. + + .. versionadded:: 9Oct2020 + + :param category: name of category + :type category: string + + :return: list of id names in given category + :rtype: list + """ + + categories = ['compute','dump','fix','group','molecule','region','variable'] + available_ids = [] + if category in categories: + num = self.lib.lammps_id_count(self.lmp, category.encode()) + sb = create_string_buffer(100) + for idx in range(num): + self.lib.lammps_id_name(self.lmp, category.encode(), idx, sb, 100) + available_ids.append(sb.value.decode()) + return available_ids + + # ------------------------------------------------------------------------- + + def available_plugins(self, category): + """Returns a list of plugins available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_plugin_count()` + and :cpp:func:`lammps_plugin_name()` of the library interface. + + .. versionadded:: 10Mar2021 + + :return: list of style/name pairs of loaded plugins + :rtype: list + """ + + available_plugins = [] + num = self.lib.lammps_plugin_count(self.lmp) + sty = create_string_buffer(100) + nam = create_string_buffer(100) + for idx in range(num): + self.lib.lammps_plugin_name(idx, sty, nam, 100) + available_plugins.append([sty.value.decode(), nam.value.decode()]) + return available_plugins + + # ------------------------------------------------------------------------- + + def set_fix_external_callback(self, fix_id, callback, caller=None): + """Set the callback function for a fix external instance with a given fix ID. + + Optionally also set a reference to the calling object. + + This is a wrapper around the :cpp:func:`lammps_set_fix_external_callback` function + of the C-library interface. However this is set up to call a Python function with + the following arguments. + + .. code-block: python + + def func(object, ntimestep, nlocal, tag, x, f): + + - object is the value of the "caller" argument + - ntimestep is the current timestep + - nlocal is the number of local atoms on the current MPI process + - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms + - x is a 2d NumPy array of doubles of the coordinates of the local atoms + - f is a 2d NumPy array of doubles of the forces on the local atoms that will be added + + .. versionchanged:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param callback: Python function that will be called from fix external + :type: function + :param caller: reference to some object passed to the callback function + :type: object, optional + """ + import numpy as np + + def callback_wrapper(caller, ntimestep, nlocal, tag_ptr, x_ptr, fext_ptr): + tag = self.numpy.iarray(self.c_tagint, tag_ptr, nlocal, 1) + x = self.numpy.darray(x_ptr, nlocal, 3) + f = self.numpy.darray(fext_ptr, nlocal, 3) + callback(caller, ntimestep, nlocal, tag, x, f) + + cFunc = self.FIX_EXTERNAL_CALLBACK_FUNC(callback_wrapper) + cCaller = caller + + self.callback[fix_id] = { 'function': cFunc, 'caller': caller } + with ExceptionCheck(self): + self.lib.lammps_set_fix_external_callback(self.lmp, fix_id.encode(), cFunc, cCaller) + + # ------------------------------------------------------------------------- + + def fix_external_get_force(self, fix_id): + """Get access to the array with per-atom forces of a fix external instance with a given fix ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: ctypes.POINTER(ctypes.POINTER(ctypes.double)) + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_get_force(self.lmp, fix_id.encode()) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_global(self, fix_id, eng): + """Set the global energy contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: potential energy value to be added by fix external + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_global(self, fix_id, virial): + """Set the global virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: list of 6 floating point numbers with the virial to be added by fix external + :type: float + """ + + cvirial = (6*c_double)(*virial) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the per-atom energy contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: list of potential energy values for local atoms to be added by fix external + :type: float + """ + + nlocal = self.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy list length must be at least nlocal') + ceatom = (nlocal*c_double)(*eatom) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external + :type: float + """ + + # copy virial data to C compatible buffer + nlocal = self.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') + vbuf = (c_double * 6) + vptr = POINTER(c_double) + c_virial = (vptr * nlocal)() + for i in range(nlocal): + c_virial[i] = vbuf() + for j in range(6): + c_virial[i][j] = vatom[i][j] + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), c_virial) + + # ------------------------------------------------------------------------- + def fix_external_set_vector_length(self, fix_id, length): + """Set the vector length for a global vector stored with fix external for analysis + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param length: length of the global vector + :type: int + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector_length(self.lmp, fix_id.encode(), length) + + # ------------------------------------------------------------------------- + def fix_external_set_vector(self, fix_id, idx, val): + """Store a global vector value for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param idx: 1-based index of the value in the global vector + :type: int + :param val: value to be stored in the global vector + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector(self.lmp, fix_id.encode(), idx, val) + + # ------------------------------------------------------------------------- + + def get_neighlist(self, idx): + """Returns an instance of :class:`NeighList` which wraps access to the neighbor list with the given index + + See :py:meth:`lammps.numpy.get_neighlist() ` if you want to use + NumPy arrays instead of ``c_int`` pointers. + + :param idx: index of neighbor list + :type idx: int + :return: an instance of :class:`NeighList` wrapping access to neighbor list data + :rtype: NeighList + """ + if idx < 0: + return None + return NeighList(self, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_size(self, idx): + """Return the number of elements in neighbor list with the given index + + :param idx: neighbor list index + :type idx: int + :return: number of elements in neighbor list with index idx + :rtype: int + """ + return self.lib.lammps_neighlist_num_elements(self.lmp, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_element_neighbors(self, idx, element): + """Return data of neighbor list entry + + :param element: neighbor list index + :type element: int + :param element: neighbor list element index + :type element: int + :return: tuple with atom local index, number of neighbors and array of neighbor local atom indices + :rtype: (int, int, POINTER(c_int)) + """ + c_iatom = c_int() + c_numneigh = c_int() + c_neighbors = POINTER(c_int)() + self.lib.lammps_neighlist_element_neighbors(self.lmp, idx, element, byref(c_iatom), byref(c_numneigh), byref(c_neighbors)) + return c_iatom.value, c_numneigh.value, c_neighbors + + # ------------------------------------------------------------------------- + + def find_pair_neighlist(self, style, exact=True, nsub=0, reqid=0): + """Find neighbor list index of pair style neighbor list + + Search for a neighbor list requested by a pair style instance that + matches "style". If exact is True, the pair style name must match + exactly. If exact is False, the pair style name is matched against + "style" as regular expression or sub-string. If the pair style is a + hybrid pair style, the style is instead matched against the hybrid + sub-styles. If the same pair style is used as sub-style multiple + types, you must set nsub to a value n > 0 which indicates the nth + instance of that sub-style to be used (same as for the pair_coeff + command). The default value of 0 will fail to match in that case. + + Once the pair style instance has been identified, it may have + requested multiple neighbor lists. Those are uniquely identified by + a request ID > 0 as set by the pair style. Otherwise the request + ID is 0. + + :param style: name of pair style that should be searched for + :type style: string + :param exact: controls whether style should match exactly or only must be contained in pair style name, defaults to True + :type exact: bool, optional + :param nsub: match nsub-th hybrid sub-style, defaults to 0 + :type nsub: int, optional + :param reqid: list request id, > 0 in case there are more than one, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + style = style.encode() + exact = int(exact) + idx = self.lib.lammps_find_pair_neighlist(self.lmp, style, exact, nsub, reqid) + return idx + + # ------------------------------------------------------------------------- + + def find_fix_neighlist(self, fixid, reqid=0): + """Find neighbor list index of fix neighbor list + + The fix instance requesting the neighbor list is uniquely identified + by the fix ID. In case the fix has requested multiple neighbor + lists, those are uniquely identified by a request ID > 0 as set by + the fix. Otherwise the request ID is 0 (the default). + + :param fixid: name of fix + :type fixid: string + :param reqid: id of neighbor list request, in case there are more than one request, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + fixid = fixid.encode() + idx = self.lib.lammps_find_fix_neighlist(self.lmp, fixid, reqid) + return idx + + # ------------------------------------------------------------------------- + + def find_compute_neighlist(self, computeid, reqid=0): + """Find neighbor list index of compute neighbor list + + The compute instance requesting the neighbor list is uniquely + identified by the compute ID. In case the compute has requested + multiple neighbor lists, those are uniquely identified by a request + ID > 0 as set by the compute. Otherwise the request ID is 0 (the + default). + + :param computeid: name of compute + :type computeid: string + :param reqid: index of neighbor list request, in case there are more than one request, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + computeid = computeid.encode() + idx = self.lib.lammps_find_compute_neighlist(self.lmp, computeid, reqid) + return idx diff --git a/python/lammps/data.py b/python/lammps/data.py new file mode 100644 index 0000000000..731a8c514a --- /dev/null +++ b/python/lammps/data.py @@ -0,0 +1,92 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# LAMMPS data structures +# Written by Richard Berger +################################################################################ + +class NeighList(object): + """This is a wrapper class that exposes the contents of a neighbor list. + + It can be used like a regular Python list. Each element is a tuple of: + + * the atom local index + * its number of neighbors + * and a pointer to an c_int array containing local atom indices of its + neighbors + + Internally it uses the lower-level LAMMPS C-library interface. + + :param lmp: reference to instance of :py:class:`lammps` + :type lmp: lammps + :param idx: neighbor list index + :type idx: int + """ + def __init__(self, lmp, idx): + self.lmp = lmp + self.idx = idx + + def __str__(self): + return "Neighbor List ({} atoms)".format(self.size) + + def __repr__(self): + return self.__str__() + + @property + def size(self): + """ + :return: number of elements in neighbor list + """ + return self.lmp.get_neighlist_size(self.idx) + + def get(self, element): + """ + Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list + + :return: tuple with atom local index, number of neighbors and ctypes pointer to neighbor's local atom indices + :rtype: (int, int, ctypes.POINTER(c_int)) + """ + iatom, numneigh, neighbors = self.lmp.get_neighlist_element_neighbors(self.idx, element) + return iatom, numneigh, neighbors + + # the methods below implement the iterator interface, so NeighList can be used like a regular Python list + + def __getitem__(self, element): + return self.get(element) + + def __len__(self): + return self.size + + def __iter__(self): + inum = self.size + + for ii in range(inum): + yield self.get(ii) + + def find(self, iatom): + """ + Find the neighbor list for a specific (local) atom iatom. + If there is no list for iatom, (-1, None) is returned. + + :return: tuple with number of neighbors and ctypes pointer to neighbor's local atom indices + :rtype: (int, ctypes.POINTER(c_int)) + """ + + inum = self.size + for ii in range(inum): + idx, numneigh, neighbors = self.get(ii) + if idx == iatom: + return numneigh, neighbors + + return -1, None diff --git a/python/lammps/formats.py b/python/lammps/formats.py new file mode 100644 index 0000000000..b7c267466f --- /dev/null +++ b/python/lammps/formats.py @@ -0,0 +1,227 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# LAMMPS output formats +# Written by Richard Berger +# and Axel Kohlmeyer +################################################################################ + +import re + +has_yaml = False +try: + import yaml + has_yaml = True + try: + from yaml import CSafeLoader as Loader + except ImportError: + from yaml import SafeLoader as Loader +except ImportError: + # ignore here, raise an exception when trying to parse yaml instead + pass + +class LogFile: + """Reads LAMMPS log files and extracts the thermo information + + It supports the line, multi, and yaml thermo output styles. + + :param filename: path to log file + :type filename: str + + :ivar runs: List of LAMMPS runs in log file. Each run is a dictionary with + thermo fields as keys, storing the values over time + :ivar errors: List of error lines in log file + """ + + STYLE_DEFAULT = 0 + STYLE_MULTI = 1 + STYLE_YAML = 2 + + def __init__(self, filename): + alpha = re.compile(r'[a-df-zA-DF-Z]') # except e or E for floating-point numbers + kvpairs = re.compile(r'([a-zA-Z_0-9]+)\s+=\s*([0-9\.eE\-]+)') + style = LogFile.STYLE_DEFAULT + yamllog = "" + self.runs = [] + self.errors = [] + with open(filename, 'rt') as f: + in_thermo = False + in_data_section = False + for line in f: + if "ERROR" in line or "exited on signal" in line: + self.errors.append(line) + + elif re.match(r'^ *Step ', line): + in_thermo = True + in_data_section = True + keys = line.split() + current_run = {} + for k in keys: + current_run[k] = [] + + elif re.match(r'^(keywords:.*$|data:$|---$| - \[.*\]$)', line): + if not has_yaml: + raise Exception('Cannot process YAML format logs without the PyYAML Python module') + style = LogFile.STYLE_YAML + yamllog += line; + current_run = {} + + elif re.match(r'^\.\.\.$', line): + thermo = yaml.load(yamllog, Loader=Loader) + for k in thermo['keywords']: + current_run[k] = [] + for step in thermo['data']: + icol = 0 + for k in thermo['keywords']: + current_run[k].append(step[icol]) + icol += 1 + self.runs.append(current_run) + yamllog = "" + + elif re.match(r'^------* Step ', line): + if not in_thermo: + current_run = {'Step': [], 'CPU': []} + in_thermo = True + in_data_section = True + style = LogFile.STYLE_MULTI + str_step, str_cpu = line.strip('-\n').split('-----') + step = float(str_step.split()[1]) + cpu = float(str_cpu.split('=')[1].split()[0]) + current_run["Step"].append(step) + current_run["CPU"].append(cpu) + + elif line.startswith('Loop time of'): + in_thermo = False + if style != LogFile.STYLE_YAML: + self.runs.append(current_run) + + elif in_thermo and in_data_section: + if style == LogFile.STYLE_DEFAULT: + if alpha.search(line): + continue + for k, v in zip(keys, map(float, line.split())): + current_run[k].append(v) + + elif style == LogFile.STYLE_MULTI: + if '=' not in line: + in_data_section = False + continue + for k,v in kvpairs.findall(line): + if k not in current_run: + current_run[k] = [float(v)] + else: + current_run[k].append(float(v)) + +class AvgChunkFile: + """Reads files generated by fix ave/chunk + + :param filename: path to ave/chunk file + :type filename: str + + :ivar timesteps: List of timesteps stored in file + :ivar total_count: total count over time + :ivar chunks: List of chunks. Each chunk is a dictionary containing its ID, the coordinates, and the averaged quantities + """ + def __init__(self, filename): + with open(filename, 'rt') as f: + timestep = None + chunks_read = 0 + + self.timesteps = [] + self.total_count = [] + self.chunks = [] + + for lineno, line in enumerate(f): + if lineno == 0: + if not line.startswith("# Chunk-averaged data for fix"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + parts = line.split() + self.fix_name = parts[5] + self.group_name = parts[8] + continue + elif lineno == 1: + if not line.startswith("# Timestep Number-of-chunks Total-count"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + continue + elif lineno == 2: + if not line.startswith("#"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + columns = line.split()[1:] + ndim = line.count("Coord") + compress = 'OrigID' in line + if ndim > 0: + coord_start = columns.index("Coord1") + coord_end = columns.index("Coord%d" % ndim) + ncount_start = coord_end + 1 + data_start = ncount_start + 1 + else: + coord_start = None + coord_end = None + ncount_start = 2 + data_start = 3 + continue + + parts = line.split() + + if timestep is None: + timestep = int(parts[0]) + num_chunks = int(parts[1]) + total_count = float(parts[2]) + + self.timesteps.append(timestep) + self.total_count.append(total_count) + + for i in range(num_chunks): + self.chunks.append({ + 'coord' : [], + 'ncount' : [] + }) + elif chunks_read < num_chunks: + chunk = int(parts[0]) + ncount = float(parts[ncount_start]) + + if compress: + chunk_id = int(parts[1]) + else: + chunk_id = chunk + + current = self.chunks[chunk_id - 1] + current['id'] = chunk_id + current['ncount'].append(ncount) + + if ndim > 0: + coord = tuple(map(float, parts[coord_start:coord_end+1])) + current['coord'].append(coord) + + for i, data_column in list(enumerate(columns))[data_start:]: + value = float(parts[i]) + + if data_column in current: + current[data_column].append(value) + else: + current[data_column] = [value] + + chunks_read += 1 + assert chunk == chunks_read + else: + # do not support changing number of chunks + if not (num_chunks == int(parts[1])): + raise Exception("Currently, changing numbers of chunks are not supported.") + + timestep = int(parts[0]) + total_count = float(parts[2]) + chunks_read = 0 + + self.timesteps.append(timestep) + self.total_count.append(total_count) diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py new file mode 100644 index 0000000000..57fe97d803 --- /dev/null +++ b/python/lammps/mliap/__init__.py @@ -0,0 +1,20 @@ + +# Check compatiblity of this build with the python shared library. +# If this fails, lammps will segfault because its library will +# try to improperly start up a new interpreter. +import sysconfig +import ctypes +library = sysconfig.get_config_vars('INSTSONAME')[0] +try: + pylib = ctypes.CDLL(library) +except OSError as e: + if pylib.endswith(".a"): + pylib.strip(".a") + ".so" + pylib = ctypes.CDLL(library) + else: + raise e +if not pylib.Py_IsInitialized(): + raise RuntimeError("This interpreter is not compatible with python-based mliap for LAMMPS.") +del sysconfig, ctypes, library, pylib + +from .loader import load_model, activate_mliappy diff --git a/python/lammps/mliap/loader.py b/python/lammps/mliap/loader.py new file mode 100644 index 0000000000..dff791bfc1 --- /dev/null +++ b/python/lammps/mliap/loader.py @@ -0,0 +1,52 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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: Nicholas Lubbers (LANL) +# ------------------------------------------------------------------------- + + +import sys +import importlib.util +import importlib.machinery + +def activate_mliappy(lmp): + try: + # Begin Importlib magic to find the embedded python module + # This is needed because the filename for liblammps does not + # match the spec for normal python modules, wherein + # file names match with PyInit function names. + # Also, python normally doesn't look for extensions besides '.so' + # We fix both of these problems by providing an explict + # path to the extension module 'mliap_model_python_couple' in + + path = lmp.lib._name + loader = importlib.machinery.ExtensionFileLoader('mliap_model_python_couple', path) + spec = importlib.util.spec_from_loader('mliap_model_python_couple', loader) + module = importlib.util.module_from_spec(spec) + sys.modules['mliap_model_python_couple'] = module + spec.loader.exec_module(module) + # End Importlib magic to find the embedded python module + + except Exception as ee: + raise ImportError("Could not load ML-IAP python coupling module.") from ee + +def load_model(model): + try: + import mliap_model_python_couple + except ImportError as ie: + raise ImportError("ML-IAP python module must be activated before loading\n" + "the pair style. Call lammps.mliap.activate_mliappy(lmp)." + ) from ie + mliap_model_python_couple.load_from_python(model) + diff --git a/python/lammps/mliap/pytorch.py b/python/lammps/mliap/pytorch.py new file mode 100644 index 0000000000..9aa2da80f4 --- /dev/null +++ b/python/lammps/mliap/pytorch.py @@ -0,0 +1,326 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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: Nicholas Lubbers (LANL) +# ------------------------------------------------------------------------- + +import numpy as np +import torch + +def calc_n_params(model): + """ + Returns the sum of two decimal numbers in binary digits. + + Parameters: + model (torch.nn.Module): Network model that maps descriptors to a per atom attribute + + Returns: + n_params (int): Number of NN model parameters + """ + return sum(p.nelement() for p in model.parameters()) + +class TorchWrapper(torch.nn.Module): + """ + A class to wrap Modules to ensure lammps mliap compatability. + + ... + + Attributes + ---------- + model : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + device : torch.nn.Module (None) + Accelerator device + + dtype : torch.dtype (torch.float64) + Dtype to use on device + + n_params : torch.nn.Module (None) + Number of NN model parameters + + n_descriptors : int + Max number of per atom descriptors + + n_elements : int + Max number of elements + + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model to produce per atom energies and forces. + """ + + def __init__(self, model, n_descriptors, n_elements, n_params=None, device=None, dtype=torch.float64): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + model : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + n_descriptors : int + Max number of per atom descriptors + + n_elements : int + Max number of elements + + n_params : torch.nn.Module (None) + Number of NN model parameters + + device : torch.nn.Module (None) + Accelerator device + + dtype : torch.dtype (torch.float64) + Dtype to use on device + """ + + super().__init__() + + self.model = model + self.device = device + self.dtype = dtype + + # Put model on device and convert to dtype + self.to(self.dtype) + self.to(self.device) + + if n_params is None: + n_params = calc_n_params(model) + + self.n_params = n_params + self.n_descriptors = n_descriptors + self.n_elements = n_elements + + def forward(self, elems, descriptors, beta, energy): + """ + Takes element types and descriptors calculated via lammps and + calculates the per atom energies and forces. + + Parameters + ---------- + elems : numpy.array + Per atom element types + + descriptors : numpy.array + Per atom descriptors + + beta : numpy.array + Expired beta array to be filled with new betas + + energy : numpy.array + Expired per atom energy array to be filled with new per atom energy + (Note: This is a pointer to the lammps per atom energies) + + + Returns + ------- + None + """ + + descriptors = torch.from_numpy(descriptors).to(dtype=self.dtype, device=self.device).requires_grad_(True) + elems = torch.from_numpy(elems).to(dtype=torch.long, device=self.device) - 1 + + with torch.autograd.enable_grad(): + + energy_nn = self.model(descriptors, elems) + if energy_nn.ndim > 1: + energy_nn = energy_nn.flatten() + + beta_nn = torch.autograd.grad(energy_nn.sum(), descriptors)[0] + + beta[:] = beta_nn.detach().cpu().numpy().astype(np.float64) + energy[:] = energy_nn.detach().cpu().numpy().astype(np.float64) + + +class IgnoreElems(torch.nn.Module): + """ + A class to represent a NN model agnostic of element typing. + + ... + + Attributes + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model + """ + + def __init__(self, subnet): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + """ + + super().__init__() + self.subnet = subnet + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnet(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + return self.subnet(descriptors) + + +class UnpackElems(torch.nn.Module): + """ + A class to represent a NN model pseudo-agnostic of element typing for + systems with multiple element typings. + + ... + + Attributes + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + n_types : int + Number of atom types used in training the NN model. + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + """ + + def __init__(self, subnet, n_types): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + """ + super().__init__() + self.subnet = subnet + self.n_types = n_types + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnet(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + unpacked_descriptors = torch.zeros(elems.shape[0], self.n_types, descriptors.shape[1], dtype=torch.float64) + for i, ind in enumerate(elems): + unpacked_descriptors[i, ind, :] = descriptors[i] + return self.subnet(torch.reshape(unpacked_descriptors, (elems.shape[0], -1)), elems) + + +class ElemwiseModels(torch.nn.Module): + """ + A class to represent a NN model dependent on element typing. + + ... + + Attributes + ---------- + subnets : list of torch.nn.Modules + Per element type network models that maps per element type + descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + """ + + def __init__(self, subnets, n_types): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnets : list of torch.nn.Modules + Per element type network models that maps per element + type descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + """ + + super().__init__() + self.subnets = subnets + self.n_types = n_types + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnets(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + per_atom_attributes = torch.zeros(elems.size[0]) + given_elems, elem_indices = torch.unique(elems, return_inverse=True) + for i, elem in enumerate(given_elems): + per_atom_attribute[elem_indices == i] = self.subnets[elem](descriptors[elem_indices == i]) + return per_atom_attributes diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py new file mode 100644 index 0000000000..ce0cb35e47 --- /dev/null +++ b/python/lammps/numpy_wrapper.py @@ -0,0 +1,483 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# NumPy additions +# Written by Richard Berger +################################################################################ + +import warnings +from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast + + +from .constants import * # lgtm [py/polluting-import] +from .data import NeighList + + +class numpy_wrapper: + """lammps API NumPy Wrapper + + This is a wrapper class that provides additional methods on top of an + existing :py:class:`lammps` instance. The methods transform raw ctypes + pointers into NumPy arrays, which give direct access to the + original data while protecting against out-of-bounds accesses. + + There is no need to explicitly instantiate this class. Each instance + of :py:class:`lammps` has a :py:attr:`numpy ` property + that returns an instance. + + :param lmp: instance of the :py:class:`lammps` class + :type lmp: lammps + """ + def __init__(self, lmp): + self.lmp = lmp + + # ------------------------------------------------------------------------- + + def _ctype_to_numpy_int(self, ctype_int): + import numpy as np + if ctype_int == c_int32: + return np.int32 + elif ctype_int == c_int64: + return np.int64 + return np.intc + + # ------------------------------------------------------------------------- + + def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT): + """Retrieve per-atom properties from LAMMPS as NumPy arrays + + This is a wrapper around the :py:meth:`lammps.extract_atom()` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + .. note:: + + While the returned arrays of per-atom data are dimensioned + for the range [0:nmax] - as is the underlying storage - + the data is usually only valid for the range of [0:nlocal], + unless the property of interest is also updated for ghost + atoms. In some cases, this depends on a LAMMPS setting, see + for example :doc:`comm_modify vel yes `. + + :param name: name of the property + :type name: string + :param dtype: type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :param nelem: number of elements in array + :type nelem: int, optional + :param dim: dimension of each element + :type dim: int, optional + :return: requested data as NumPy array with direct access to C data or None + :rtype: numpy.array or NoneType + """ + if dtype == LAMMPS_AUTODETECT: + dtype = self.lmp.extract_atom_datatype(name) + + if nelem == LAMMPS_AUTODETECT: + if name == "mass": + nelem = self.lmp.extract_global("ntypes") + 1 + else: + nelem = self.lmp.extract_global("nlocal") + if dim == LAMMPS_AUTODETECT: + if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D): + # TODO add other fields + if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"): + dim = 3 + elif name == "smd_data_9": + dim = 9 + elif name == "smd_stress": + dim = 6 + else: + dim = 2 + else: + dim = 1 + + raw_ptr = self.lmp.extract_atom(name, dtype) + + if dtype in (LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D): + return self.darray(raw_ptr, nelem, dim) + elif dtype in (LAMMPS_INT, LAMMPS_INT_2D): + return self.iarray(c_int32, raw_ptr, nelem, dim) + elif dtype in (LAMMPS_INT64, LAMMPS_INT64_2D): + return self.iarray(c_int64, raw_ptr, nelem, dim) + return raw_ptr + + # ------------------------------------------------------------------------- + + def extract_atom_iarray(self, name, nelem, dim=1): + warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) + + if name in ['id', 'molecule']: + c_int_type = self.lmp.c_tagint + elif name in ['image']: + c_int_type = self.lmp.c_imageint + else: + c_int_type = c_int + + if dim == 1: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) + else: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D) + + return self.iarray(c_int_type, raw_ptr, nelem, dim) + + # ------------------------------------------------------------------------- + + def extract_atom_darray(self, name, nelem, dim=1): + warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) + + if dim == 1: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) + else: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D) + + return self.darray(raw_ptr, nelem, dim) + + # ------------------------------------------------------------------------- + + def extract_compute(self, cid, cstyle, ctype): + """Retrieve data from a LAMMPS compute + + This is a wrapper around the + :py:meth:`lammps.extract_compute() ` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param cid: compute ID + :type cid: string + :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type cstyle: int + :param ctype: type of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ctype: int + :return: requested data either as float, as NumPy array with direct access to C data, or None + :rtype: float, numpy.array, or NoneType + """ + value = self.lmp.extract_compute(cid, cstyle, ctype) + + if cstyle == LMP_STYLE_GLOBAL: + if ctype == LMP_TYPE_VECTOR: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_VECTOR) + return self.darray(value, nrows) + elif ctype == LMP_TYPE_ARRAY: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + return self.darray(value, nrows, ncols) + elif cstyle == LMP_STYLE_LOCAL: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + if ncols == 0: + return self.darray(value, nrows) + else: + return self.darray(value, nrows, ncols) + elif cstyle == LMP_STYLE_ATOM: + if ctype == LMP_TYPE_VECTOR: + nlocal = self.lmp.extract_global("nlocal") + return self.darray(value, nlocal) + elif ctype == LMP_TYPE_ARRAY: + nlocal = self.lmp.extract_global("nlocal") + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + return self.darray(value, nlocal, ncols) + return value + + # ------------------------------------------------------------------------- + + def extract_fix(self, fid, fstyle, ftype, nrow=0, ncol=0): + """Retrieve data from a LAMMPS fix + + This is a wrapper around the :py:meth:`lammps.extract_fix() ` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param fid: fix ID + :type fid: string + :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type fstyle: int + :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ftype: int + :param nrow: index of global vector element or row index of global array element + :type nrow: int + :param ncol: column index of global array element + :type ncol: int + :return: requested data + :rtype: integer or double value, pointer to 1d or 2d double array or None + + """ + value = self.lmp.extract_fix(fid, fstyle, ftype, nrow, ncol) + if fstyle == LMP_STYLE_ATOM: + if ftype == LMP_TYPE_VECTOR: + nlocal = self.lmp.extract_global("nlocal") + return self.darray(value, nlocal) + elif ftype == LMP_TYPE_ARRAY: + nlocal = self.lmp.extract_global("nlocal") + ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) + return self.darray(value, nlocal, ncols) + elif fstyle == LMP_STYLE_LOCAL: + if ftype == LMP_TYPE_VECTOR: + nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) + return self.darray(value, nrows) + elif ftype == LMP_TYPE_ARRAY: + nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) + ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) + return self.darray(value, nrows, ncols) + return value + + # ------------------------------------------------------------------------- + + def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): + """ Evaluate a LAMMPS variable and return its data + + This function is a wrapper around the function + :py:meth:`lammps.extract_variable() ` + method. It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param name: name of the variable to execute + :type name: string + :param group: name of group for atom-style variable (ignored for equal-style variables) + :type group: string + :param vartype: type of variable, see :ref:`py_vartype_constants` + :type vartype: int + :return: the requested data or None + :rtype: c_double, numpy.array, or NoneType + """ + import numpy as np + value = self.lmp.extract_variable(name, group, vartype) + if vartype == LMP_VAR_ATOM: + return np.ctypeslib.as_array(value) + return value + + # ------------------------------------------------------------------------- + + def gather_bonds(self): + """Retrieve global list of bonds as NumPy array + + This is a wrapper around :py:meth:`lammps.gather_bonds() ` + It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` list. + + .. versionadded:: 28Jul2021 + + :return: the requested data as a 2d-integer numpy array + :rtype: numpy.array(nbonds,3) + """ + import numpy as np + nbonds, value = self.lmp.gather_bonds() + return np.ctypeslib.as_array(value).reshape(nbonds,3) + + # ------------------------------------------------------------------------- + + def fix_external_get_force(self, fix_id): + """Get access to the array with per-atom forces of a fix external instance with a given fix ID. + + This function is a wrapper around the + :py:meth:`lammps.fix_external_get_force() ` + method. It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` pointer. + + .. versionchanged:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + value = self.lmp.fix_external_get_force(fix_id) + return self.darray(value,nlocal,3) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the per-atom energy contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_energy_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy dimension must be at least nlocal') + + c_double_p = POINTER(c_double) + value = eatom.astype(np.double) + return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), + value.ctypes.data_as(c_double_p)) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_virial_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') + + c_double_pp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C') + + # recast numpy array to be compatible with library interface + value = (vatom.__array_interface__['data'][0] + + np.arange(vatom.shape[0])*vatom.strides[0]).astype(np.uintp) + + # change prototype to our custom type + self.lmp.lib.lammps_fix_external_set_virial_peratom.argtypes = [ c_void_p, c_char_p, c_double_pp ] + + self.lmp.lib.lammps_fix_external_set_virial_peratom(self.lmp.lmp, fix_id.encode(), value) + + # ------------------------------------------------------------------------- + + def get_neighlist(self, idx): + """Returns an instance of :class:`NumPyNeighList` which wraps access to the neighbor list with the given index + + :param idx: index of neighbor list + :type idx: int + :return: an instance of :class:`NumPyNeighList` wrapping access to neighbor list data + :rtype: NumPyNeighList + """ + if idx < 0: + return None + return NumPyNeighList(self.lmp, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_element_neighbors(self, idx, element): + """Return data of neighbor list entry + + This function is a wrapper around the function + :py:meth:`lammps.get_neighlist_element_neighbors() ` + method. It behaves the same as the original method, but returns a NumPy array containing the neighbors + instead of a ``ctypes`` pointer. + + :param element: neighbor list index + :type element: int + :param element: neighbor list element index + :type element: int + :return: tuple with atom local index and numpy array of neighbor local atom indices + :rtype: (int, numpy.array) + """ + iatom, numneigh, c_neighbors = self.lmp.get_neighlist_element_neighbors(idx, element) + neighbors = self.iarray(c_int, c_neighbors, numneigh, 1) + return iatom, neighbors + + # ------------------------------------------------------------------------- + + def iarray(self, c_int_type, raw_ptr, nelem, dim=1): + if raw_ptr is None: + return None + + import numpy as np + np_int_type = self._ctype_to_numpy_int(c_int_type) + + if dim == 1: + ptr = cast(raw_ptr, POINTER(c_int_type * nelem)) + else: + ptr = cast(raw_ptr[0], POINTER(c_int_type * nelem * dim)) + + a = np.frombuffer(ptr.contents, dtype=np_int_type) + + if dim > 1: + a.shape = (nelem, dim) + else: + a.shape = (nelem) + return a + + # ------------------------------------------------------------------------- + + def darray(self, raw_ptr, nelem, dim=1): + if raw_ptr is None: + return None + + import numpy as np + + if dim == 1: + ptr = cast(raw_ptr, POINTER(c_double * nelem)) + else: + ptr = cast(raw_ptr[0], POINTER(c_double * nelem * dim)) + + a = np.frombuffer(ptr.contents) + + if dim > 1: + a.shape = (nelem, dim) + else: + a.shape = (nelem) + return a + +# ------------------------------------------------------------------------- + +class NumPyNeighList(NeighList): + """This is a wrapper class that exposes the contents of a neighbor list. + + It can be used like a regular Python list. Each element is a tuple of: + + * the atom local index + * a NumPy array containing the local atom indices of its neighbors + + Internally it uses the lower-level LAMMPS C-library interface. + + :param lmp: reference to instance of :py:class:`lammps` + :type lmp: lammps + :param idx: neighbor list index + :type idx: int + """ + def __init__(self, lmp, idx): + super(NumPyNeighList, self).__init__(lmp, idx) + + def get(self, element): + """ + Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list + + :return: tuple with atom local index, numpy array of neighbor local atom indices + :rtype: (int, numpy.array) + """ + iatom, neighbors = self.lmp.numpy.get_neighlist_element_neighbors(self.idx, element) + return iatom, neighbors + + def find(self, iatom): + """ + Find the neighbor list for a specific (local) atom iatom. + If there is no list for iatom, None is returned. + + :return: numpy array of neighbor local atom indices + :rtype: numpy.array or None + """ + inum = self.size + for ii in range(inum): + idx, neighbors = self.get(ii) + if idx == iatom: + return neighbors + return None diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py new file mode 100644 index 0000000000..1fe1f2452b --- /dev/null +++ b/python/lammps/pylammps.py @@ -0,0 +1,990 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# Alternative Python Wrapper +# Written by Richard Berger +################################################################################ + +# for python2/3 compatibility + +from __future__ import print_function + +import io +import os +import re +import sys +import tempfile +from collections import namedtuple + +from .core import lammps + +# ------------------------------------------------------------------------- + +class OutputCapture(object): + """ Utility class to capture LAMMPS library output """ + def __init__(self): + self.stdout_fd = 1 + self.captured_output = "" + + def __enter__(self): + self.tmpfile = tempfile.TemporaryFile(mode='w+b') + + sys.stdout.flush() + + # make copy of original stdout + self.stdout_orig = os.dup(self.stdout_fd) + + # replace stdout and redirect to temp file + os.dup2(self.tmpfile.fileno(), self.stdout_fd) + return self + + def __exit__(self, exc_type, exc_value, traceback): + os.dup2(self.stdout_orig, self.stdout_fd) + os.close(self.stdout_orig) + self.tmpfile.close() + + @property + def output(self): + sys.stdout.flush() + self.tmpfile.flush() + self.tmpfile.seek(0, io.SEEK_SET) + self.captured_output = self.tmpfile.read().decode('utf-8') + return self.captured_output + +# ------------------------------------------------------------------------- + +class Variable(object): + def __init__(self, pylammps_instance, name, style, definition): + self._pylmp = pylammps_instance + self.name = name + self.style = style + self.definition = definition.split() + + @property + def value(self): + if self.style == 'atom': + return list(self._pylmp.lmp.extract_variable(self.name, "all", 1)) + else: + value = self._pylmp.lmp_print('"${%s}"' % self.name).strip() + try: + return float(value) + except ValueError: + return value + +# ------------------------------------------------------------------------- + +class AtomList(object): + """ + A dynamic list of atoms that returns either an :py:class:`Atom` or + :py:class:`Atom2D` instance for each atom. Instances are only allocated + when accessed. + + :ivar natoms: total number of atoms + :ivar dimensions: number of dimensions in system + """ + def __init__(self, pylammps_instance): + self._pylmp = pylammps_instance + self.natoms = self._pylmp.system.natoms + self.dimensions = self._pylmp.system.dimensions + self._loaded = {} + + def __getitem__(self, index): + """ + Return Atom with given local index + + :param index: Local index of atom + :type index: int + :rtype: Atom or Atom2D + """ + if index not in self._loaded: + if self.dimensions == 2: + atom = Atom2D(self._pylmp, index) + else: + atom = Atom(self._pylmp, index) + self._loaded[index] = atom + return self._loaded[index] + + def __len__(self): + return self.natoms + + +# ------------------------------------------------------------------------- + +class Atom(object): + """ + A wrapper class then represents a single atom inside of LAMMPS + + It provides access to properties of the atom and allows you to change some of them. + """ + def __init__(self, pylammps_instance, index): + self._pylmp = pylammps_instance + self.index = index + + def __dir__(self): + return [k for k in super().__dir__() if not k.startswith('_')] + + def get(self, name, index): + prop = self._pylmp.lmp.numpy.extract_atom(name) + if prop is not None: + return prop[index] + return None + + @property + def id(self): + """ + Return the atom ID + + :type: int + """ + return self.get("id", self.index) + + @property + def type(self): + """ + Return the atom type + + :type: int + """ + return self.get("type", self.index) + + @property + def mol(self): + """ + Return the atom molecule index + + :type: int + """ + return self.get("mol", self.index) + + @property + def mass(self): + """ + Return the atom mass + + :type: float + """ + return self.get("mass", self.index) + + @property + def radius(self): + """ + Return the particle radius + + :type: float + """ + return self.get("radius", self.index) + + @property + def position(self): + """ + :getter: Return position of atom + :setter: Set position of atom + :type: numpy.array (float, float, float) + """ + return self.get("x", self.index) + + @position.setter + def position(self, value): + current = self.position + current[:] = value + + @property + def velocity(self): + """ + :getter: Return velocity of atom + :setter: Set velocity of atom + :type: numpy.array (float, float, float) + """ + return self.get("v", self.index) + + @velocity.setter + def velocity(self, value): + current = self.velocity + current[:] = value + + @property + def force(self): + """ + Return the total force acting on the atom + + :type: numpy.array (float, float, float) + """ + return self.get("f", self.index) + + @force.setter + def force(self, value): + current = self.force + current[:] = value + + @property + def torque(self): + """ + Return the total torque acting on the atom + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @force.setter + def torque(self, value): + current = self.torque + current[:] = value + + @property + def omega(self): + """ + Return the rotational velocity of the particle + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @omega.setter + def omega(self, value): + current = self.torque + current[:] = value + + @property + def torque(self): + """ + Return the total torque acting on the particle + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @torque.setter + def torque(self, value): + current = self.torque + current[:] = value + + @property + def angular_momentum(self): + """ + Return the angular momentum of the particle + + :type: numpy.array (float, float, float) + """ + return self.get("angmom", self.index) + + @angular_momentum.setter + def angular_momentum(self, value): + current = self.angular_momentum + current[:] = value + + @property + def charge(self): + """ + Return the atom charge + + :type: float + """ + return self.get("q", self.index) + +# ------------------------------------------------------------------------- + +class Atom2D(Atom): + """ + A wrapper class then represents a single 2D atom inside of LAMMPS + + Inherits all properties from the :py:class:`Atom` class, but returns 2D versions + of position, velocity, and force. + + It provides access to properties of the atom and allows you to change some of them. + """ + def __init__(self, pylammps_instance, index): + super(Atom2D, self).__init__(pylammps_instance, index) + + @property + def position(self): + """Access to coordinates of an atom + + :getter: Return position of atom + :setter: Set position of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).position[0:2] + + @position.setter + def position(self, value): + current = self.position + current[:] = value + + @property + def velocity(self): + """Access to velocity of an atom + :getter: Return velocity of atom + :setter: Set velocity of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).velocity[0:2] + + @velocity.setter + def velocity(self, value): + current = self.velocity + current[:] = value + + @property + def force(self): + """Access to force of an atom + :getter: Return force of atom + :setter: Set force of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).force[0:2] + + @force.setter + def force(self, value): + current = self.force + current[:] = value + +# ------------------------------------------------------------------------- + +class variable_set: + def __init__(self, name, variable_dict): + self._name = name + array_pattern = re.compile(r"(?P.+)\[(?P[0-9]+)\]") + + for key, value in variable_dict.items(): + m = array_pattern.match(key) + if m: + g = m.groupdict() + varname = g['arr'] + idx = int(g['index']) + if varname not in self.__dict__: + self.__dict__[varname] = {} + self.__dict__[varname][idx] = value + else: + self.__dict__[key] = value + + def __str__(self): + return "{}({})".format(self._name, ','.join(["{}={}".format(k, self.__dict__[k]) for k in self.__dict__.keys() if not k.startswith('_')])) + + def __dir__(self): + return [k for k in self.__dict__.keys() if not k.startswith('_')] + + def __repr__(self): + return self.__str__() + +# ------------------------------------------------------------------------- + +def get_thermo_data(output): + """ traverse output of runs and extract thermo data columns """ + if isinstance(output, str): + lines = output.splitlines() + else: + lines = output + + runs = [] + columns = [] + in_run = False + current_run = {} + + for line in lines: + if line.startswith("Per MPI rank memory allocation"): + in_run = True + elif in_run and len(columns) == 0: + # first line after memory usage are column names + columns = line.split() + + current_run = {} + + for col in columns: + current_run[col] = [] + + elif line.startswith("Loop time of "): + in_run = False + columns = [] + thermo_data = variable_set('ThermoData', current_run) + r = {'thermo' : thermo_data } + runs.append(namedtuple('Run', list(r.keys()))(*list(r.values()))) + elif in_run and len(columns) > 0: + items = line.split() + # Convert thermo output and store it. + # It must have the same number of columns and + # all of them must be convertible to floats. + # Otherwise we ignore the line + if len(items) == len(columns): + try: + values = [float(x) for x in items] + for i, col in enumerate(columns): + current_run[col].append(values[i]) + except ValueError: + # cannot convert. must be a non-thermo output. ignore. + pass + + return runs + +# ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- + +class PyLammps(object): + """ + This is a Python wrapper class around the lower-level + :py:class:`lammps` class, exposing a more Python-like, + object-oriented interface for prototyping system inside of IPython and + Jupyter notebooks. + + It either creates its own instance of :py:class:`lammps` or can be + initialized with an existing instance. The arguments are the same of the + lower-level interface. The original interface can still be accessed via + :py:attr:`PyLammps.lmp`. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + :param verbose: print all LAMMPS output to stdout + :type verbose: bool + + :ivar lmp: instance of original LAMMPS Python interface + :vartype lmp: :py:class:`lammps` + + :ivar runs: list of completed runs, each storing the thermo output + :vartype run: list + """ + + def __init__(self, name="", cmdargs=None, ptr=None, comm=None, verbose=False): + self.has_echo = False + self.verbose = verbose + + if cmdargs: + if '-echo' in cmdargs: + idx = cmdargs.index('-echo') + # ensures that echo line is ignored during output capture + self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both') + + if ptr: + if isinstance(ptr,PyLammps): + self.lmp = ptr.lmp + elif isinstance(ptr,lammps): + self.lmp = ptr + else: + self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) + else: + self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm) + print("LAMMPS output is captured by PyLammps wrapper") + self._cmd_history = [] + self._enable_cmd_history = False + self.runs = [] + + def __enter__(self): + return self + + def __exit__(self, ex_type, ex_value, ex_traceback): + self.close() + + def __del__(self): + if self.lmp: self.lmp.close() + self.lmp = None + + def close(self): + """Explicitly delete a LAMMPS instance + + This is a wrapper around the :py:meth:`lammps.close` of the Python interface. + """ + if self.lmp: self.lmp.close() + self.lmp = None + + def version(self): + """Return a numerical representation of the LAMMPS version in use. + + This is a wrapper around the :py:meth:`lammps.version` function of the Python interface. + + :return: version number + :rtype: int + """ + return self.lmp.version() + + def file(self, file): + """Read LAMMPS commands from a file. + + This is a wrapper around the :py:meth:`lammps.file` function of the Python interface. + + :param path: Name of the file/path with LAMMPS commands + :type path: string + """ + self.lmp.file(file) + + @property + def enable_cmd_history(self): + """ + :getter: Return whether command history is saved + :setter: Set if command history should be saved + :type: bool + """ + return self._enable_cmd_history + + @enable_cmd_history.setter + def enable_cmd_history(self, value): + """ + :getter: Return whether command history is saved + :setter: Set if command history should be saved + :type: bool + """ + self._enable_cmd_history = (value == True) + + def write_script(self, filepath): + """ + Write LAMMPS script file containing all commands executed up until now + + :param filepath: path to script file that should be written + :type filepath: string + """ + with open(filepath, "w") as f: + for cmd in self._cmd_history: + print(cmd, file=f) + + def clear_cmd_history(self): + """ + Clear LAMMPS command history up to this point + """ + self._cmd_history = [] + + def command(self, cmd): + """ + Execute LAMMPS command + + If :py:attr:`PyLammps.enable_cmd_history` is set to ``True``, commands executed + will be recorded. The entire command history can be written to a file using + :py:meth:`PyLammps.write_script()`. To clear the command history, use + :py:meth:`PyLammps.clear_cmd_history()`. + + :param cmd: command string that should be executed + :type: cmd: string + """ + self.lmp.command(cmd) + + if self.enable_cmd_history: + self._cmd_history.append(cmd) + + def run(self, *args, **kwargs): + """ + Execute LAMMPS run command with given arguments + + All thermo output during the run is captured and saved as new entry in + :py:attr:`PyLammps.runs`. The latest run can be retrieved by + :py:attr:`PyLammps.last_run`. + """ + output = self.__getattr__('run')(*args, **kwargs) + self.runs += get_thermo_data(output) + return output + + @property + def last_run(self): + """ + Return data produced of last completed run command + + :getter: Returns an object containing information about the last run command + :type: dict + """ + if len(self.runs) > 0: + return self.runs[-1] + return None + + @property + def atoms(self): + """ + All atoms of this LAMMPS instance + + :getter: Returns a list of atoms currently in the system + :type: AtomList + """ + return AtomList(self) + + @property + def system(self): + """ + The system state of this LAMMPS instance + + :getter: Returns an object with properties storing the current system state + :type: namedtuple + """ + output = self.lmp_info("system") + output = output[output.index("System information:")+1:] + d = self._parse_info_system(output) + return namedtuple('System', d.keys())(*d.values()) + + @property + def communication(self): + """ + The communication state of this LAMMPS instance + + :getter: Returns an object with properties storing the current communication state + :type: namedtuple + """ + output = self.lmp_info("communication") + output = output[output.index("Communication information:")+1:] + d = self._parse_info_communication(output) + return namedtuple('Communication', d.keys())(*d.values()) + + @property + def computes(self): + """ + The list of active computes of this LAMMPS instance + + :getter: Returns a list of computes that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("computes") + output = output[output.index("Compute information:")+1:] + return self._parse_element_list(output) + + @property + def dumps(self): + """ + The list of active dumps of this LAMMPS instance + + :getter: Returns a list of dumps that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("dumps") + output = output[output.index("Dump information:")+1:] + return self._parse_element_list(output) + + @property + def fixes(self): + """ + The list of active fixes of this LAMMPS instance + + :getter: Returns a list of fixes that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("fixes") + output = output[output.index("Fix information:")+1:] + return self._parse_element_list(output) + + @property + def groups(self): + """ + The list of active atom groups of this LAMMPS instance + + :getter: Returns a list of atom groups that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("groups") + output = output[output.index("Group information:")+1:] + return self._parse_groups(output) + + @property + def variables(self): + """ + Returns a dictionary of all variables defined in the current LAMMPS instance + + :getter: Returns a dictionary of all variables that are defined in this LAMMPS instance + :type: dict + """ + output = self.lmp_info("variables") + output = output[output.index("Variable information:")+1:] + variables = {} + for v in self._parse_element_list(output): + variables[v['name']] = Variable(self, v['name'], v['style'], v['def']) + return variables + + def eval(self, expr): + """ + Evaluate expression + + :param expr: the expression string that should be evaluated inside of LAMMPS + :type expr: string + + :return: the value of the evaluated expression + :rtype: float if numeric, string otherwise + """ + value = self.lmp_print('"$(%s)"' % expr).strip() + try: + return float(value) + except ValueError: + return value + + def _split_values(self, line): + return [x.strip() for x in line.split(',')] + + def _get_pair(self, value): + return [x.strip() for x in value.split('=')] + + def _parse_info_system(self, output): + system = {} + + for line in output: + if line.startswith("Units"): + system['units'] = self._get_pair(line)[1] + elif line.startswith("Atom style"): + system['atom_style'] = self._get_pair(line)[1] + elif line.startswith("Atom map"): + system['atom_map'] = self._get_pair(line)[1] + elif line.startswith("Atoms"): + parts = self._split_values(line) + system['natoms'] = int(self._get_pair(parts[0])[1]) + system['ntypes'] = int(self._get_pair(parts[1])[1]) + system['style'] = self._get_pair(parts[2])[1] + elif line.startswith("Kspace style"): + system['kspace_style'] = self._get_pair(line)[1] + elif line.startswith("Dimensions"): + system['dimensions'] = int(self._get_pair(line)[1]) + elif line.startswith("Orthogonal box"): + system['orthogonal_box'] = [float(x) for x in self._get_pair(line)[1].split('x')] + elif line.startswith("Boundaries"): + system['boundaries'] = self._get_pair(line)[1] + elif line.startswith("xlo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("ylo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("zlo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("Molecule type"): + system['molecule_type'] = self._get_pair(line)[1] + elif line.startswith("Bonds"): + parts = self._split_values(line) + system['nbonds'] = int(self._get_pair(parts[0])[1]) + system['nbondtypes'] = int(self._get_pair(parts[1])[1]) + system['bond_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Angles"): + parts = self._split_values(line) + system['nangles'] = int(self._get_pair(parts[0])[1]) + system['nangletypes'] = int(self._get_pair(parts[1])[1]) + system['angle_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Dihedrals"): + parts = self._split_values(line) + system['ndihedrals'] = int(self._get_pair(parts[0])[1]) + system['ndihedraltypes'] = int(self._get_pair(parts[1])[1]) + system['dihedral_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Impropers"): + parts = self._split_values(line) + system['nimpropers'] = int(self._get_pair(parts[0])[1]) + system['nimpropertypes'] = int(self._get_pair(parts[1])[1]) + system['improper_style'] = self._get_pair(parts[2])[1] + + return system + + def _parse_info_communication(self, output): + comm = {} + + for line in output: + if line.startswith("MPI library"): + comm['mpi_version'] = line.split(':')[1].strip() + elif line.startswith("Comm style"): + parts = self._split_values(line) + comm['comm_style'] = self._get_pair(parts[0])[1] + comm['comm_layout'] = self._get_pair(parts[1])[1] + elif line.startswith("Processor grid"): + comm['proc_grid'] = [int(x) for x in self._get_pair(line)[1].split('x')] + elif line.startswith("Communicate velocities for ghost atoms"): + comm['ghost_velocity'] = (self._get_pair(line)[1] == "yes") + elif line.startswith("Nprocs"): + parts = self._split_values(line) + comm['nprocs'] = int(self._get_pair(parts[0])[1]) + comm['nthreads'] = int(self._get_pair(parts[1])[1]) + return comm + + def _parse_element_list(self, output): + elements = [] + + for line in output: + if not line or (":" not in line): continue + element_info = self._split_values(line.split(':')[1].strip()) + element = {'name': element_info[0]} + for key, value in [self._get_pair(x) for x in element_info[1:]]: + element[key] = value + elements.append(element) + return elements + + def _parse_groups(self, output): + groups = [] + group_pattern = re.compile(r"(?P.+) \((?P.+)\)") + + for line in output: + m = group_pattern.match(line.split(':')[1].strip()) + group = {'name': m.group('name'), 'type': m.group('type')} + groups.append(group) + return groups + + def lmp_print(self, s): + """ needed for Python2 compatibility, since print is a reserved keyword """ + return self.__getattr__("print")(s) + + def __dir__(self): + return sorted(set(['angle_coeff', 'angle_style', 'atom_modify', 'atom_style', 'atom_style', + 'bond_coeff', 'bond_style', 'boundary', 'change_box', 'communicate', 'compute', + 'create_atoms', 'create_box', 'delete_atoms', 'delete_bonds', 'dielectric', + 'dihedral_coeff', 'dihedral_style', 'dimension', 'dump', 'fix', 'fix_modify', + 'group', 'improper_coeff', 'improper_style', 'include', 'kspace_modify', + 'kspace_style', 'lattice', 'mass', 'minimize', 'min_style', 'neighbor', + 'neigh_modify', 'newton', 'nthreads', 'pair_coeff', 'pair_modify', + 'pair_style', 'processors', 'read', 'read_data', 'read_restart', 'region', + 'replicate', 'reset_timestep', 'restart', 'run', 'run_style', 'thermo', + 'thermo_modify', 'thermo_style', 'timestep', 'undump', 'unfix', 'units', + 'variable', 'velocity', 'write_restart'] + self.lmp.available_styles("command"))) + + def lmp_info(self, s): + # skip anything before and after Info-Info-Info + # also skip timestamp line + output = self.__getattr__("info")(s) + indices = [index for index, line in enumerate(output) if line.startswith("Info-Info-Info-Info")] + start = indices[0] + end = indices[1] + return [line for line in output[start+2:end] if line] + + def __getattr__(self, name): + """ + This method is where the Python 'magic' happens. If a method is not + defined by the class PyLammps, it assumes it is a LAMMPS command. It takes + all the arguments, concatinates them to a single string, and executes it using + :py:meth:`lammps.PyLammps.command()`. + + :param verbose: Print output of command + :type verbose: bool + :return: line or list of lines of output, None if no output + :rtype: list or string + """ + def handler(*args, **kwargs): + cmd_args = [name] + [str(x) for x in args] + self.lmp.flush_buffers() + + with OutputCapture() as capture: + cmd = ' '.join(cmd_args) + self.command(cmd) + self.lmp.flush_buffers() + output = capture.output + + comm = self.lmp.get_mpi_comm() + if comm: + output = self.lmp.comm.bcast(output, root=0) + + if self.verbose or ('verbose' in kwargs and kwargs['verbose']): + print(output, end = '') + + lines = output.splitlines() + + if self.has_echo: + lines = lines[1:] + + if len(lines) > 1: + return lines + elif len(lines) == 1: + return lines[0] + return None + + return handler + + +class IPyLammps(PyLammps): + """ + IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps interface + + It either creates its own instance of :py:class:`lammps` or can be + initialized with an existing instance. The arguments are the same of the + lower-level interface. The original interface can still be accessed via + :py:attr:`PyLammps.lmp`. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + """ + + def __init__(self,name="",cmdargs=None,ptr=None,comm=None): + super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) + + def image(self, filename="snapshot.png", group="all", color="type", diameter="type", + size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"): + """ Generate image using write_dump command and display it + + See :doc:`dump image ` for more information. + + :param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG + :type filename: string + :param group: the group of atoms write_image should use + :type group: string + :param color: name of property used to determine color + :type color: string + :param diameter: name of property used to determine atom diameter + :type diameter: string + :param size: dimensions of image + :type size: tuple (width, height) + :param view: view parameters + :type view: tuple (theta, phi) + :param center: center parameters + :type center: tuple (flag, center_x, center_y, center_z) + :param up: vector pointing to up direction + :type up: tuple (up_x, up_y, up_z) + :param zoom: zoom factor + :type zoom: float + :param background_color: background color of scene + :type background_color: string + + :return: Image instance used to display image in notebook + :rtype: :py:class:`IPython.core.display.Image` + """ + cmd_args = [group, "image", filename, color, diameter] + + if size is not None: + width = size[0] + height = size[1] + cmd_args += ["size", width, height] + + if view is not None: + theta = view[0] + phi = view[1] + cmd_args += ["view", theta, phi] + + if center is not None: + flag = center[0] + Cx = center[1] + Cy = center[2] + Cz = center[3] + cmd_args += ["center", flag, Cx, Cy, Cz] + + if up is not None: + Ux = up[0] + Uy = up[1] + Uz = up[2] + cmd_args += ["up", Ux, Uy, Uz] + + if zoom is not None: + cmd_args += ["zoom", zoom] + + cmd_args.append("modify backcolor " + background_color) + + self.write_dump(*cmd_args) + from IPython.core.display import Image + return Image(filename) + + def video(self, filename): + """ + Load video from file + + Can be used to visualize videos from :doc:`dump movie `. + + :param filename: Path to video file + :type filename: string + :return: HTML Video Tag used by notebook to embed a video + :rtype: :py:class:`IPython.display.HTML` + """ + from IPython.display import HTML + return HTML("") From 139ecad13cfab035760217d95d2ff1b73f1a596c Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:12:40 -0600 Subject: [PATCH 66/75] Trying to remove symlink --- python/lammps/__init__.py | 49 - python/lammps/constants.py | 48 - python/lammps/core.py | 2097 ------------------------------- python/lammps/data.py | 92 -- python/lammps/formats.py | 227 ---- python/lammps/mliap/__init__.py | 20 - python/lammps/mliap/loader.py | 52 - python/lammps/mliap/pytorch.py | 326 ----- python/lammps/numpy_wrapper.py | 483 ------- python/lammps/pylammps.py | 990 --------------- 10 files changed, 4384 deletions(-) delete mode 100644 python/lammps/__init__.py delete mode 100644 python/lammps/constants.py delete mode 100644 python/lammps/core.py delete mode 100644 python/lammps/data.py delete mode 100644 python/lammps/formats.py delete mode 100644 python/lammps/mliap/__init__.py delete mode 100644 python/lammps/mliap/loader.py delete mode 100644 python/lammps/mliap/pytorch.py delete mode 100644 python/lammps/numpy_wrapper.py delete mode 100644 python/lammps/pylammps.py diff --git a/python/lammps/__init__.py b/python/lammps/__init__.py deleted file mode 100644 index fc35e45225..0000000000 --- a/python/lammps/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -LAMMPS module global members: - -.. data:: __version__ - - Numerical representation of the LAMMPS version this - module was taken from. Has the same format as the - result of :py:func:`lammps.version`. -""" - -from .constants import * # lgtm [py/polluting-import] -from .core import * # lgtm [py/polluting-import] -from .data import * # lgtm [py/polluting-import] -from .pylammps import * # lgtm [py/polluting-import] - -# convert installed module string version to numeric version -def get_version_number(): - import time - from os.path import join - from sys import version_info - - # must report 0 when inside LAMMPS source tree - if __file__.find(join('python', 'lammps', '__init__.py')) > 0: - return 0 - - vstring = None - if version_info.major == 3 and version_info.minor >= 8: - from importlib.metadata import version, PackageNotFoundError - try: - vstring = version('lammps') - except PackageNotFoundError: - # nothing to do, ignore - pass - - else: - from pkg_resources import get_distribution, DistributionNotFound - try: - vstring = get_distribution('lammps').version - except DistributionNotFound: - # nothing to do, ignore - pass - - if not vstring: - return 0 - - t = time.strptime(vstring, "%Y.%m.%d") - return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday - -__version__ = get_version_number() diff --git a/python/lammps/constants.py b/python/lammps/constants.py deleted file mode 100644 index a50d58b28f..0000000000 --- a/python/lammps/constants.py +++ /dev/null @@ -1,48 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -# various symbolic constants to be used -# in certain calls to select data formats -LAMMPS_AUTODETECT = None -LAMMPS_INT = 0 -LAMMPS_INT_2D = 1 -LAMMPS_DOUBLE = 2 -LAMMPS_DOUBLE_2D = 3 -LAMMPS_INT64 = 4 -LAMMPS_INT64_2D = 5 -LAMMPS_STRING = 6 - -# these must be kept in sync with the enums in library.h -LMP_STYLE_GLOBAL = 0 -LMP_STYLE_ATOM = 1 -LMP_STYLE_LOCAL = 2 - -LMP_TYPE_SCALAR = 0 -LMP_TYPE_VECTOR = 1 -LMP_TYPE_ARRAY = 2 -LMP_SIZE_VECTOR = 3 -LMP_SIZE_ROWS = 4 -LMP_SIZE_COLS = 5 - -LMP_VAR_EQUAL = 0 -LMP_VAR_ATOM = 1 - -# ------------------------------------------------------------------------- - -def get_ctypes_int(size): - from ctypes import c_int, c_int32, c_int64 - if size == 4: - return c_int32 - elif size == 8: - return c_int64 - return c_int diff --git a/python/lammps/core.py b/python/lammps/core.py deleted file mode 100644 index 930a40a4b0..0000000000 --- a/python/lammps/core.py +++ /dev/null @@ -1,2097 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- -# Python wrapper for the LAMMPS library via ctypes - -# for python2/3 compatibility - -from __future__ import print_function - -import os -import sys -from ctypes import * # lgtm [py/polluting-import] -from os.path import dirname,abspath,join -from inspect import getsourcefile - -from .constants import * # lgtm [py/polluting-import] -from .data import * # lgtm [py/polluting-import] - -# ------------------------------------------------------------------------- - -class MPIAbortException(Exception): - def __init__(self, message): - self.message = message - - def __str__(self): - return repr(self.message) - -# ------------------------------------------------------------------------- - -class ExceptionCheck: - """Utility class to rethrow LAMMPS C++ exceptions as Python exceptions""" - def __init__(self, lmp): - self.lmp = lmp - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_value, traceback): - if self.lmp.has_exceptions and self.lmp.lib.lammps_has_error(self.lmp.lmp): - raise self.lmp._lammps_exception - -# ------------------------------------------------------------------------- - -class lammps(object): - """Create an instance of the LAMMPS Python class. - - .. _mpi4py_docs: https://mpi4py.readthedocs.io/ - - This is a Python wrapper class that exposes the LAMMPS C-library - interface to Python. It either requires that LAMMPS has been compiled - as shared library which is then dynamically loaded via the ctypes - Python module or that this module called from a Python function that - is called from a Python interpreter embedded into a LAMMPS executable, - for example through the :doc:`python invoke ` command. - When the class is instantiated it calls the :cpp:func:`lammps_open` - function of the LAMMPS C-library interface, which in - turn will create an instance of the :cpp:class:`LAMMPS ` - C++ class. The handle to this C++ class is stored internally - and automatically passed to the calls to the C library interface. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - """ - - # ------------------------------------------------------------------------- - # create an instance of LAMMPS - - def __init__(self,name='',cmdargs=None,ptr=None,comm=None): - self.comm = comm - self.opened = 0 - - # determine module file location - - modpath = dirname(abspath(getsourcefile(lambda:0))) - # for windows installers the shared library is in a different folder - winpath = abspath(os.path.join(modpath,'..','..','bin')) - # allow override for running tests on Windows - if (os.environ.get("LAMMPSDLLPATH")): - winpath = os.environ.get("LAMMPSDLLPATH") - self.lib = None - self.lmp = None - - # if a pointer to a LAMMPS object is handed in - # when being called from a Python interpreter - # embedded into a LAMMPS executable, all library - # symbols should already be available so we do not - # load a shared object. - - try: - if ptr is not None: self.lib = CDLL("",RTLD_GLOBAL) - except OSError: - self.lib = None - - # load liblammps.so unless name is given - # if name = "g++", load liblammps_g++.so - # try loading the LAMMPS shared object from the location - # of the lammps package with an absolute path, - # so that LD_LIBRARY_PATH does not need to be set for regular install - # fall back to loading with a relative path, - # typically requires LD_LIBRARY_PATH to be set appropriately - # guess shared library extension based on OS, if not inferred from actual file - - if any([f.startswith('liblammps') and f.endswith('.dylib') - for f in os.listdir(modpath)]): - lib_ext = ".dylib" - elif any([f.startswith('liblammps') and f.endswith('.dll') - for f in os.listdir(modpath)]): - lib_ext = ".dll" - elif os.path.exists(winpath) and any([f.startswith('liblammps') and f.endswith('.dll') - for f in os.listdir(winpath)]): - lib_ext = ".dll" - modpath = winpath - elif any([f.startswith('liblammps') and f.endswith('.so') - for f in os.listdir(modpath)]): - lib_ext = ".so" - else: - import platform - if platform.system() == "Darwin": - lib_ext = ".dylib" - elif platform.system() == "Windows": - lib_ext = ".dll" - else: - lib_ext = ".so" - - if not self.lib: - if name: - libpath = join(modpath,"liblammps_%s" % name + lib_ext) - else: - libpath = join(modpath,"liblammps" + lib_ext) - if not os.path.isfile(libpath): - if name: - libpath = "liblammps_%s" % name + lib_ext - else: - libpath = "liblammps" + lib_ext - self.lib = CDLL(libpath,RTLD_GLOBAL) - - # declare all argument and return types for all library methods here. - # exceptions are where the arguments depend on certain conditions and - # then are defined where the functions are used. - self.lib.lammps_extract_setting.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_setting.restype = c_int - - # set default types - # needed in later declarations - self.c_bigint = get_ctypes_int(self.extract_setting("bigint")) - self.c_tagint = get_ctypes_int(self.extract_setting("tagint")) - self.c_imageint = get_ctypes_int(self.extract_setting("imageint")) - - self.lib.lammps_open.restype = c_void_p - self.lib.lammps_open_no_mpi.restype = c_void_p - self.lib.lammps_close.argtypes = [c_void_p] - self.lib.lammps_flush_buffers.argtypes = [c_void_p] - self.lib.lammps_free.argtypes = [c_void_p] - - self.lib.lammps_file.argtypes = [c_void_p, c_char_p] - self.lib.lammps_file.restype = None - - self.lib.lammps_command.argtypes = [c_void_p, c_char_p] - self.lib.lammps_command.restype = c_char_p - self.lib.lammps_commands_list.restype = None - self.lib.lammps_commands_string.argtypes = [c_void_p, c_char_p] - self.lib.lammps_commands_string.restype = None - - self.lib.lammps_get_natoms.argtypes = [c_void_p] - self.lib.lammps_get_natoms.restype = c_double - self.lib.lammps_extract_box.argtypes = \ - [c_void_p,POINTER(c_double),POINTER(c_double), - POINTER(c_double),POINTER(c_double),POINTER(c_double), - POINTER(c_int),POINTER(c_int)] - self.lib.lammps_extract_box.restype = None - - self.lib.lammps_reset_box.argtypes = \ - [c_void_p,POINTER(c_double),POINTER(c_double),c_double,c_double,c_double] - self.lib.lammps_reset_box.restype = None - - self.lib.lammps_gather_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_atoms.restype = None - - self.lib.lammps_gather_atoms_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_atoms_concat.restype = None - - self.lib.lammps_gather_atoms_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_gather_atoms_subset.restype = None - - self.lib.lammps_scatter_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_scatter_atoms.restype = None - - self.lib.lammps_scatter_atoms_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_scatter_atoms_subset.restype = None - - self.lib.lammps_gather_bonds.argtypes = [c_void_p,c_void_p] - self.lib.lammps_gather_bonds.restype = None - - self.lib.lammps_gather.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather.restype = None - - self.lib.lammps_gather_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_gather_concat.restype = None - - self.lib.lammps_gather_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_gather_subset.restype = None - - self.lib.lammps_scatter.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] - self.lib.lammps_scatter.restype = None - - self.lib.lammps_scatter_subset.argtypes = \ - [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] - self.lib.lammps_scatter_subset.restype = None - - - self.lib.lammps_find_pair_neighlist.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int] - self.lib.lammps_find_pair_neighlist.restype = c_int - - self.lib.lammps_find_fix_neighlist.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_find_fix_neighlist.restype = c_int - - self.lib.lammps_find_compute_neighlist.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_find_compute_neighlist.restype = c_int - - self.lib.lammps_neighlist_num_elements.argtypes = [c_void_p, c_int] - self.lib.lammps_neighlist_num_elements.restype = c_int - - self.lib.lammps_neighlist_element_neighbors.argtypes = \ - [c_void_p, c_int, c_int, POINTER(c_int), POINTER(c_int), POINTER(POINTER(c_int))] - self.lib.lammps_neighlist_element_neighbors.restype = None - - self.lib.lammps_is_running.argtypes = [c_void_p] - self.lib.lammps_is_running.restype = c_int - - self.lib.lammps_force_timeout.argtypes = [c_void_p] - - self.lib.lammps_has_error.argtypes = [c_void_p] - self.lib.lammps_has_error.restype = c_int - - self.lib.lammps_get_last_error_message.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_get_last_error_message.restype = c_int - - self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_global_datatype.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_global_datatype.restype = c_int - self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int] - - self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p] - self.lib.lammps_get_thermo.restype = c_double - - self.lib.lammps_encode_image_flags.restype = self.c_imageint - - self.lib.lammps_config_package_name.argtypes = [c_int, c_char_p, c_int] - self.lib.lammps_config_accelerator.argtypes = [c_char_p, c_char_p, c_char_p] - - self.lib.lammps_set_variable.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_has_style.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_style_count.argtypes = [c_void_p, c_char_p] - - self.lib.lammps_style_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] - - self.lib.lammps_has_id.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_id_count.argtypes = [c_void_p, c_char_p] - - self.lib.lammps_id_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] - - self.lib.lammps_plugin_count.argtypes = [ ] - self.lib.lammps_plugin_name.argtypes = [c_int, c_char_p, c_char_p, c_int] - - self.lib.lammps_version.argtypes = [c_void_p] - - self.lib.lammps_get_os_info.argtypes = [c_char_p, c_int] - self.lib.lammps_get_gpu_device_info.argtypes = [c_char_p, c_int] - - self.lib.lammps_get_mpi_comm.argtypes = [c_void_p] - - self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)] - - self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p] - self.lib.lammps_extract_atom_datatype.restype = c_int - - self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] - - self.lib.lammps_extract_variable.argtypes = [c_void_p, c_char_p, c_char_p] - - self.lib.lammps_fix_external_get_force.argtypes = [c_void_p, c_char_p] - self.lib.lammps_fix_external_get_force.restype = POINTER(POINTER(c_double)) - - self.lib.lammps_fix_external_set_energy_global.argtypes = [c_void_p, c_char_p, c_double] - self.lib.lammps_fix_external_set_virial_global.argtypes = [c_void_p, c_char_p, POINTER(c_double)] - self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] - self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] - - self.lib.lammps_fix_external_set_vector_length.argtypes = [c_void_p, c_char_p, c_int] - self.lib.lammps_fix_external_set_vector.argtypes = [c_void_p, c_char_p, c_int, c_double] - - # detect if Python is using a version of mpi4py that can pass communicators - # only needed if LAMMPS has been compiled with MPI support. - self.has_mpi4py = False - if self.has_mpi_support: - try: - from mpi4py import __version__ as mpi4py_version - # tested to work with mpi4py versions 2 and 3 - self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3'] - except ImportError: - # ignore failing import - pass - - # if no ptr provided, create an instance of LAMMPS - # we can pass an MPI communicator from mpi4py v2.0.0 and later - # no_mpi call lets LAMMPS use MPI_COMM_WORLD - # cargs = array of C strings from args - # if ptr, then are embedding Python in LAMMPS input script - # ptr is the desired instance of LAMMPS - # just convert it to ctypes ptr and store in self.lmp - - if ptr is None: - - # with mpi4py v2+, we can pass MPI communicators to LAMMPS - # need to adjust for type of MPI communicator object - # allow for int (like MPICH) or void* (like OpenMPI) - if self.has_mpi_support and self.has_mpi4py: - from mpi4py import MPI - self.MPI = MPI - - if comm is not None: - if not self.has_mpi_support: - raise Exception('LAMMPS not compiled with real MPI library') - if not self.has_mpi4py: - raise Exception('Python mpi4py version is not 2 or 3') - if self.MPI._sizeof(self.MPI.Comm) == sizeof(c_int): - MPI_Comm = c_int - else: - MPI_Comm = c_void_p - - # Detect whether LAMMPS and mpi4py definitely use different MPI libs - if sizeof(MPI_Comm) != self.lib.lammps_config_has_mpi_support(): - raise Exception('Inconsistent MPI library in LAMMPS and mpi4py') - - narg = 0 - cargs = None - if cmdargs is not None: - cmdargs.insert(0,"lammps") - narg = len(cmdargs) - for i in range(narg): - if type(cmdargs[i]) is str: - cmdargs[i] = cmdargs[i].encode() - cargs = (c_char_p*narg)(*cmdargs) - self.lib.lammps_open.argtypes = [c_int, c_char_p*narg, MPI_Comm, c_void_p] - else: - self.lib.lammps_open.argtypes = [c_int, c_char_p, MPI_Comm, c_void_p] - - self.opened = 1 - comm_ptr = self.MPI._addressof(comm) - comm_val = MPI_Comm.from_address(comm_ptr) - self.lmp = c_void_p(self.lib.lammps_open(narg,cargs,comm_val,None)) - - else: - if self.has_mpi4py and self.has_mpi_support: - self.comm = self.MPI.COMM_WORLD - self.opened = 1 - if cmdargs is not None: - cmdargs.insert(0,"lammps") - narg = len(cmdargs) - for i in range(narg): - if type(cmdargs[i]) is str: - cmdargs[i] = cmdargs[i].encode() - cargs = (c_char_p*narg)(*cmdargs) - self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p*narg, c_void_p] - self.lmp = c_void_p(self.lib.lammps_open_no_mpi(narg,cargs,None)) - else: - self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p, c_void_p] - self.lmp = c_void_p(self.lib.lammps_open_no_mpi(0,None,None)) - - else: - # magic to convert ptr to ctypes ptr - if sys.version_info >= (3, 0): - # Python 3 (uses PyCapsule API) - pythonapi.PyCapsule_GetPointer.restype = c_void_p - pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] - self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None)) - else: - # Python 2 (uses PyCObject API) - pythonapi.PyCObject_AsVoidPtr.restype = c_void_p - pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] - self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) - - # check if library initilialization failed - if not self.lmp: - raise(RuntimeError("Failed to initialize LAMMPS object")) - - # optional numpy support (lazy loading) - self._numpy = None - - self._installed_packages = None - self._available_styles = None - - # check if liblammps version matches the installed python module version - # but not for in-place usage, i.e. when the version is 0 - import lammps - if lammps.__version__ > 0 and lammps.__version__ != self.lib.lammps_version(self.lmp): - raise(AttributeError("LAMMPS Python module installed for LAMMPS version %d, but shared library is version %d" \ - % (lammps.__version__, self.lib.lammps_version(self.lmp)))) - - # add way to insert Python callback for fix external - self.callback = {} - self.FIX_EXTERNAL_CALLBACK_FUNC = CFUNCTYPE(None, py_object, self.c_bigint, c_int, POINTER(self.c_tagint), POINTER(POINTER(c_double)), POINTER(POINTER(c_double))) - self.lib.lammps_set_fix_external_callback.argtypes = [c_void_p, c_char_p, self.FIX_EXTERNAL_CALLBACK_FUNC, py_object] - self.lib.lammps_set_fix_external_callback.restype = None - - # ------------------------------------------------------------------------- - # shut-down LAMMPS instance - - def __del__(self): - self.close() - - # ------------------------------------------------------------------------- - # context manager implementation - - def __enter__(self): - return self - - def __exit__(self, ex_type, ex_value, ex_traceback): - self.close() - - # ------------------------------------------------------------------------- - - @property - def numpy(self): - """ Return object to access numpy versions of API - - It provides alternative implementations of API functions that - return numpy arrays instead of ctypes pointers. If numpy is not installed, - accessing this property will lead to an ImportError. - - :return: instance of numpy wrapper object - :rtype: numpy_wrapper - """ - if not self._numpy: - from .numpy_wrapper import numpy_wrapper - self._numpy = numpy_wrapper(self) - return self._numpy - - # ------------------------------------------------------------------------- - - def close(self): - """Explicitly delete a LAMMPS instance through the C-library interface. - - This is a wrapper around the :cpp:func:`lammps_close` function of the C-library interface. - """ - if self.lmp and self.opened: - self.lib.lammps_close(self.lmp) - self.lmp = None - self.opened = 0 - - # ------------------------------------------------------------------------- - - 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`. - - You cannot create or use any LAMMPS instances after this function is called - unless LAMMPS was compiled without MPI and without Kokkos support. - """ - self.close() - self.lib.lammps_kokkos_finalize() - self.lib.lammps_mpi_finalize() - - # ------------------------------------------------------------------------- - - def version(self): - """Return a numerical representation of the LAMMPS version in use. - - This is a wrapper around the :cpp:func:`lammps_version` function of the C-library interface. - - :return: version number - :rtype: int - """ - return self.lib.lammps_version(self.lmp) - - # ------------------------------------------------------------------------- - - def get_os_info(self): - """Return a string with information about the OS and compiler runtime - - This is a wrapper around the :cpp:func:`lammps_get_os_info` function of the C-library interface. - - :return: OS info string - :rtype: string - """ - - sb = create_string_buffer(512) - self.lib.lammps_get_os_info(sb,512) - return sb.value.decode() - - # ------------------------------------------------------------------------- - - def get_mpi_comm(self): - """Get the MPI communicator in use by the current LAMMPS instance - - This is a wrapper around the :cpp:func:`lammps_get_mpi_comm` function - of the C-library interface. It will return ``None`` if either the - LAMMPS library was compiled without MPI support or the mpi4py - Python module is not available. - - :return: MPI communicator - :rtype: MPI_Comm - """ - - if self.has_mpi4py and self.has_mpi_support: - from mpi4py import MPI - f_comm = self.lib.lammps_get_mpi_comm(self.lmp) - c_comm = MPI.Comm.f2py(f_comm) - return c_comm - else: - return None - - # ------------------------------------------------------------------------- - - @property - def _lammps_exception(self): - sb = create_string_buffer(100) - error_type = self.lib.lammps_get_last_error_message(self.lmp, sb, 100) - error_msg = sb.value.decode().strip() - - if error_type == 2: - return MPIAbortException(error_msg) - return Exception(error_msg) - - # ------------------------------------------------------------------------- - - def file(self, path): - """Read LAMMPS commands from a file. - - This is a wrapper around the :cpp:func:`lammps_file` function of the C-library interface. - It will open the file with the name/path `file` and process the LAMMPS commands line by line until - the end. The function will return when the end of the file is reached. - - :param path: Name of the file/path with LAMMPS commands - :type path: string - """ - if path: path = path.encode() - else: return - - with ExceptionCheck(self): - self.lib.lammps_file(self.lmp, path) - - # ------------------------------------------------------------------------- - - def command(self,cmd): - """Process a single LAMMPS input command from a string. - - This is a wrapper around the :cpp:func:`lammps_command` - function of the C-library interface. - - :param cmd: a single lammps command - :type cmd: string - """ - if cmd: cmd = cmd.encode() - else: return - - with ExceptionCheck(self): - self.lib.lammps_command(self.lmp,cmd) - - # ------------------------------------------------------------------------- - - def commands_list(self,cmdlist): - """Process multiple LAMMPS input commands from a list of strings. - - This is a wrapper around the - :cpp:func:`lammps_commands_list` function of - the C-library interface. - - :param cmdlist: a single lammps command - :type cmdlist: list of strings - """ - cmds = [x.encode() for x in cmdlist if type(x) is str] - narg = len(cmdlist) - args = (c_char_p * narg)(*cmds) - self.lib.lammps_commands_list.argtypes = [c_void_p, c_int, c_char_p * narg] - - with ExceptionCheck(self): - self.lib.lammps_commands_list(self.lmp,narg,args) - - # ------------------------------------------------------------------------- - - def commands_string(self,multicmd): - """Process a block of LAMMPS input commands from a string. - - This is a wrapper around the - :cpp:func:`lammps_commands_string` - function of the C-library interface. - - :param multicmd: text block of lammps commands - :type multicmd: string - """ - if type(multicmd) is str: multicmd = multicmd.encode() - - with ExceptionCheck(self): - self.lib.lammps_commands_string(self.lmp,c_char_p(multicmd)) - - # ------------------------------------------------------------------------- - - def get_natoms(self): - """Get the total number of atoms in the LAMMPS instance. - - Will be precise up to 53-bit signed integer due to the - underlying :cpp:func:`lammps_get_natoms` function returning a double. - - :return: number of atoms - :rtype: int - """ - return int(self.lib.lammps_get_natoms(self.lmp)) - - # ------------------------------------------------------------------------- - - def extract_box(self): - """Extract simulation box parameters - - This is a wrapper around the :cpp:func:`lammps_extract_box` function - of the C-library interface. Unlike in the C function, the result is - returned as a list. - - :return: list of the extracted data: boxlo, boxhi, xy, yz, xz, periodicity, box_change - :rtype: [ 3*double, 3*double, double, double, 3*int, int] - """ - boxlo = (3*c_double)() - boxhi = (3*c_double)() - xy = c_double() - yz = c_double() - xz = c_double() - periodicity = (3*c_int)() - box_change = c_int() - - with ExceptionCheck(self): - self.lib.lammps_extract_box(self.lmp,boxlo,boxhi, - byref(xy),byref(yz),byref(xz), - periodicity,byref(box_change)) - - boxlo = boxlo[:3] - boxhi = boxhi[:3] - xy = xy.value - yz = yz.value - xz = xz.value - periodicity = periodicity[:3] - box_change = box_change.value - - return boxlo,boxhi,xy,yz,xz,periodicity,box_change - - # ------------------------------------------------------------------------- - - def reset_box(self,boxlo,boxhi,xy,yz,xz): - """Reset simulation box parameters - - This is a wrapper around the :cpp:func:`lammps_reset_box` function - of the C-library interface. - - :param boxlo: new lower box boundaries - :type boxlo: list of 3 floating point numbers - :param boxhi: new upper box boundaries - :type boxhi: list of 3 floating point numbers - :param xy: xy tilt factor - :type xy: float - :param yz: yz tilt factor - :type yz: float - :param xz: xz tilt factor - :type xz: float - """ - cboxlo = (3*c_double)(*boxlo) - cboxhi = (3*c_double)(*boxhi) - with ExceptionCheck(self): - self.lib.lammps_reset_box(self.lmp,cboxlo,cboxhi,xy,yz,xz) - - # ------------------------------------------------------------------------- - - def get_thermo(self,name): - """Get current value of a thermo keyword - - This is a wrapper around the :cpp:func:`lammps_get_thermo` - function of the C-library interface. - - :param name: name of thermo keyword - :type name: string - :return: value of thermo keyword - :rtype: double or None - """ - if name: name = name.encode() - else: return None - - with ExceptionCheck(self): - return self.lib.lammps_get_thermo(self.lmp,name) - - # ------------------------------------------------------------------------- - - def extract_setting(self, name): - """Query LAMMPS about global settings that can be expressed as an integer. - - This is a wrapper around the :cpp:func:`lammps_extract_setting` - function of the C-library interface. Its documentation includes - a list of the supported keywords. - - :param name: name of the setting - :type name: string - :return: value of the setting - :rtype: int - """ - if name: name = name.encode() - else: return None - return int(self.lib.lammps_extract_setting(self.lmp,name)) - - # ------------------------------------------------------------------------- - # extract global info datatype - - def extract_global_datatype(self, name): - """Retrieve global property datatype from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_global_datatype` - function of the C-library interface. Its documentation includes a - list of the supported keywords. - This function returns ``None`` if the keyword is not - recognized. Otherwise it will return a positive integer value that - corresponds to one of the :ref:`data type ` - constants define in the :py:mod:`lammps` module. - - :param name: name of the property - :type name: string - :return: data type of global property, see :ref:`py_datatype_constants` - :rtype: int - """ - if name: name = name.encode() - else: return None - return self.lib.lammps_extract_global_datatype(self.lmp, name) - - # ------------------------------------------------------------------------- - # extract global info - - def extract_global(self, name, dtype=LAMMPS_AUTODETECT): - """Query LAMMPS about global settings of different types. - - This is a wrapper around the :cpp:func:`lammps_extract_global` function - of the C-library interface. Since there are no pointers in Python, this - method will - unlike the C function - return the value or a list of - values. The :cpp:func:`lammps_extract_global` documentation includes a - list of the supported keywords and their data types. - Since Python needs to know the data type to be able to interpret - the result, by default, this function will try to auto-detect the data type - by asking the library. You can also force a specific data type. For that - purpose the :py:mod:`lammps` module contains :ref:`data type ` - constants. This function returns ``None`` if either the keyword is not recognized, - or an invalid data type constant is used. - - :param name: name of the property - :type name: string - :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :return: value of the property or list of values or None - :rtype: int, float, list, or NoneType - """ - - if dtype == LAMMPS_AUTODETECT: - dtype = self.extract_global_datatype(name) - - # set length of vector for items that are not a scalar - vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3, - 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } - if name in vec_dict: - veclen = vec_dict[name] - elif name == 'respa_dt': - veclen = self.extract_global('respa_levels',LAMMPS_INT) - else: - veclen = 1 - - if name: name = name.encode() - else: return None - - if dtype == LAMMPS_INT: - self.lib.lammps_extract_global.restype = POINTER(c_int32) - target_type = int - elif dtype == LAMMPS_INT64: - self.lib.lammps_extract_global.restype = POINTER(c_int64) - target_type = int - elif dtype == LAMMPS_DOUBLE: - self.lib.lammps_extract_global.restype = POINTER(c_double) - target_type = float - elif dtype == LAMMPS_STRING: - self.lib.lammps_extract_global.restype = c_char_p - target_type = str - else: - target_type = None - - ptr = self.lib.lammps_extract_global(self.lmp, name) - if ptr: - if dtype == LAMMPS_STRING: - return ptr.decode('utf-8') - if veclen > 1: - result = [] - for i in range(0,veclen): - result.append(target_type(ptr[i])) - return result - else: return target_type(ptr[0]) - return None - - # ------------------------------------------------------------------------- - # extract per-atom info datatype - - def extract_atom_datatype(self, name): - """Retrieve per-atom property datatype from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_atom_datatype` - function of the C-library interface. Its documentation includes a - list of the supported keywords. - This function returns ``None`` if the keyword is not - recognized. Otherwise it will return an integer value that - corresponds to one of the :ref:`data type ` constants - defined in the :py:mod:`lammps` module. - - :param name: name of the property - :type name: string - :return: data type of per-atom property (see :ref:`py_datatype_constants`) - :rtype: int - """ - if name: name = name.encode() - else: return None - return self.lib.lammps_extract_atom_datatype(self.lmp, name) - - # ------------------------------------------------------------------------- - # extract per-atom info - - def extract_atom(self, name, dtype=LAMMPS_AUTODETECT): - """Retrieve per-atom properties from LAMMPS - - This is a wrapper around the :cpp:func:`lammps_extract_atom` - function of the C-library interface. Its documentation includes a - list of the supported keywords and their data types. - Since Python needs to know the data type to be able to interpret - the result, by default, this function will try to auto-detect the data type - by asking the library. You can also force a specific data type by setting ``dtype`` - to one of the :ref:`data type ` constants defined in the - :py:mod:`lammps` module. - This function returns ``None`` if either the keyword is not - recognized, or an invalid data type constant is used. - - .. note:: - - While the returned arrays of per-atom data are dimensioned - for the range [0:nmax] - as is the underlying storage - - the data is usually only valid for the range of [0:nlocal], - unless the property of interest is also updated for ghost - atoms. In some cases, this depends on a LAMMPS setting, see - for example :doc:`comm_modify vel yes `. - - :param name: name of the property - :type name: string - :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :return: requested data or ``None`` - :rtype: ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.POINTER(ctypes.c_int32)), - ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - or NoneType - """ - if dtype == LAMMPS_AUTODETECT: - dtype = self.extract_atom_datatype(name) - - if name: name = name.encode() - else: return None - - if dtype == LAMMPS_INT: - self.lib.lammps_extract_atom.restype = POINTER(c_int32) - elif dtype == LAMMPS_INT_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int32)) - elif dtype == LAMMPS_DOUBLE: - self.lib.lammps_extract_atom.restype = POINTER(c_double) - elif dtype == LAMMPS_DOUBLE_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double)) - elif dtype == LAMMPS_INT64: - self.lib.lammps_extract_atom.restype = POINTER(c_int64) - elif dtype == LAMMPS_INT64_2D: - self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64)) - else: return None - - ptr = self.lib.lammps_extract_atom(self.lmp, name) - if ptr: return ptr - else: return None - - - # ------------------------------------------------------------------------- - - def extract_compute(self,cid,cstyle,ctype): - """Retrieve data from a LAMMPS compute - - This is a wrapper around the :cpp:func:`lammps_extract_compute` - function of the C-library interface. - This function returns ``None`` if either the compute id is not - recognized, or an invalid combination of :ref:`cstyle ` - and :ref:`ctype ` constants is used. The - names and functionality of the constants are the same as for - the corresponding C-library function. For requests to return - a scalar or a size, the value is returned, otherwise a pointer. - - :param cid: compute ID - :type cid: string - :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type cstyle: int - :param ctype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ctype: int - :return: requested data as scalar, pointer to 1d or 2d double array, or None - :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType - """ - if cid: cid = cid.encode() - else: return None - - if ctype == LMP_TYPE_SCALAR: - if cstyle == LMP_STYLE_GLOBAL: - self.lib.lammps_extract_compute.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - elif cstyle == LMP_STYLE_ATOM: - return None - elif cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - elif ctype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_compute.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr - - elif ctype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_compute.restype = POINTER(POINTER(c_double)) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr - - elif ctype == LMP_SIZE_COLS: - if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_ATOM or cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - elif ctype == LMP_SIZE_VECTOR or ctype == LMP_SIZE_ROWS: - if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_LOCAL: - self.lib.lammps_extract_compute.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) - return ptr[0] - - return None - - # ------------------------------------------------------------------------- - # extract fix info - # in case of global data, free memory for 1 double via lammps_free() - # double was allocated by library interface function - - def extract_fix(self,fid,fstyle,ftype,nrow=0,ncol=0): - """Retrieve data from a LAMMPS fix - - This is a wrapper around the :cpp:func:`lammps_extract_fix` - function of the C-library interface. - This function returns ``None`` if either the fix id is not - recognized, or an invalid combination of :ref:`fstyle ` - and :ref:`ftype ` constants is used. The - names and functionality of the constants are the same as for - the corresponding C-library function. For requests to return - a scalar or a size, the value is returned, also when accessing - global vectors or arrays, otherwise a pointer. - - :param fid: fix ID - :type fid: string - :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type fstyle: int - :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ftype: int - :param nrow: index of global vector element or row index of global array element - :type nrow: int - :param ncol: column index of global array element - :type ncol: int - :return: requested data or None - :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType - - """ - if fid: fid = fid.encode() - else: return None - - if fstyle == LMP_STYLE_GLOBAL: - if ftype in (LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): - self.lib.lammps_extract_fix.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - result = ptr[0] - self.lib.lammps_free(ptr) - return result - elif ftype in (LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): - self.lib.lammps_extract_fix.restype = POINTER(c_int) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - return ptr[0] - else: - return None - - elif fstyle == LMP_STYLE_ATOM: - if ftype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_fix.restype = POINTER(c_double) - elif ftype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) - elif ftype == LMP_SIZE_COLS: - self.lib.lammps_extract_fix.restype = POINTER(c_int) - else: - return None - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - if ftype == LMP_SIZE_COLS: - return ptr[0] - else: - return ptr - - elif fstyle == LMP_STYLE_LOCAL: - if ftype == LMP_TYPE_VECTOR: - self.lib.lammps_extract_fix.restype = POINTER(c_double) - elif ftype == LMP_TYPE_ARRAY: - self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) - elif ftype in (LMP_TYPE_SCALAR, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): - self.lib.lammps_extract_fix.restype = POINTER(c_int) - else: - return None - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) - if ftype in (LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): - return ptr - else: - return ptr[0] - else: - return None - - # ------------------------------------------------------------------------- - # extract variable info - # free memory for 1 double or 1 vector of doubles via lammps_free() - # for vector, must copy nlocal returned values to local c_double vector - # memory was allocated by library interface function - - def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): - """ Evaluate a LAMMPS variable and return its data - - This function is a wrapper around the function - :cpp:func:`lammps_extract_variable` of the C-library interface, - evaluates variable name and returns a copy of the computed data. - The memory temporarily allocated by the C-interface is deleted - after the data is copied to a Python variable or list. - The variable must be either an equal-style (or equivalent) - variable or an atom-style variable. The variable type has to - provided as ``vartype`` parameter which may be one of two constants: - ``LMP_VAR_EQUAL`` or ``LMP_VAR_ATOM``; it defaults to - equal-style variables. - The group parameter is only used for atom-style variables and - defaults to the group "all" if set to ``None``, which is the default. - - :param name: name of the variable to execute - :type name: string - :param group: name of group for atom-style variable - :type group: string, only for atom-style variables - :param vartype: type of variable, see :ref:`py_vartype_constants` - :type vartype: int - :return: the requested data - :rtype: c_double, (c_double), or NoneType - """ - if name: name = name.encode() - else: return None - if group: group = group.encode() - if vartype == LMP_VAR_EQUAL: - self.lib.lammps_extract_variable.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_variable(self.lmp,name,group) - if ptr: result = ptr[0] - else: return None - self.lib.lammps_free(ptr) - return result - elif vartype == LMP_VAR_ATOM: - nlocal = self.extract_global("nlocal") - result = (c_double*nlocal)() - self.lib.lammps_extract_variable.restype = POINTER(c_double) - with ExceptionCheck(self): - ptr = self.lib.lammps_extract_variable(self.lmp,name,group) - if ptr: - for i in range(nlocal): result[i] = ptr[i] - self.lib.lammps_free(ptr) - else: return None - return result - return None - - # ------------------------------------------------------------------------- - - def flush_buffers(self): - """Flush output buffers - - This is a wrapper around the :cpp:func:`lammps_flush_buffers` - function of the C-library interface. - """ - self.lib.lammps_flush_buffers(self.lmp) - - # ------------------------------------------------------------------------- - - def set_variable(self,name,value): - """Set a new value for a LAMMPS string style variable - - This is a wrapper around the :cpp:func:`lammps_set_variable` - function of the C-library interface. - - :param name: name of the variable - :type name: string - :param value: new variable value - :type value: any. will be converted to a string - :return: either 0 on success or -1 on failure - :rtype: int - """ - if name: name = name.encode() - else: return -1 - if value: value = str(value).encode() - else: return -1 - with ExceptionCheck(self): - return self.lib.lammps_set_variable(self.lmp,name,value) - - # ------------------------------------------------------------------------- - - # return vector of atom properties gathered across procs - # 3 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # dtype = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # returned data is a 1d vector - doc how it is ordered? - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def gather_atoms(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) - else: - return None - return data - - # ------------------------------------------------------------------------- - - def gather_atoms_concat(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_atoms_subset(self,name,dtype,count,ndata,ids): - if name: name = name.encode() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*ndata)*c_int)() - self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - elif dtype == 1: - data = ((count*ndata)*c_double)() - self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - else: - return None - return data - - # ------------------------------------------------------------------------- - - # scatter vector of atom properties across procs - # 2 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # assume data is of correct type and length, as created by gather_atoms() - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def scatter_atoms(self,name,dtype,count,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_atoms(self.lmp,name,dtype,count,data) - - # ------------------------------------------------------------------------- - - def scatter_atoms_subset(self,name,dtype,count,ndata,ids,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) - - - # ------------------------------------------------------------------------- - - def gather_bonds(self): - """Retrieve global list of bonds - - This is a wrapper around the :cpp:func:`lammps_gather_bonds` - function of the C-library interface. - - This function returns a tuple with the number of bonds and a - flat list of ctypes integer values with the bond type, bond atom1, - bond atom2 for each bond. - - .. versionadded:: 28Jul2021 - - :return: a tuple with the number of bonds and a list of c_int or c_long - :rtype: (int, 3*nbonds*c_tagint) - """ - nbonds = self.extract_global("nbonds") - with ExceptionCheck(self): - data = ((3*nbonds)*self.c_tagint)() - self.lib.lammps_gather_bonds(self.lmp,data) - return nbonds,data - - # ------------------------------------------------------------------------- - - # return vector of atom/compute/fix properties gathered across procs - # 3 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # returned data is a 1d vector - doc how it is ordered? - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - def gather(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_concat(self,name,dtype,count): - if name: name = name.encode() - natoms = self.get_natoms() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*natoms)*c_int)() - self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) - elif dtype == 1: - data = ((count*natoms)*c_double)() - self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) - else: - return None - return data - - def gather_subset(self,name,dtype,count,ndata,ids): - if name: name = name.encode() - with ExceptionCheck(self): - if dtype == 0: - data = ((count*ndata)*c_int)() - self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) - elif dtype == 1: - data = ((count*ndata)*c_double)() - self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) - else: - return None - return data - - # scatter vector of atom/compute/fix properties across procs - # 2 variants to match src/library.cpp - # name = atom property recognized by LAMMPS in atom->extract() - # type = 0 for integer values, 1 for double values - # count = number of per-atom valus, 1 for type or charge, 3 for x or f - # assume data is of correct type and length, as created by gather_atoms() - # NOTE: need to insure are converting to/from correct Python type - # e.g. for Python list or NumPy or ctypes - - def scatter(self,name,dtype,count,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter(self.lmp,name,dtype,count,data) - - def scatter_subset(self,name,dtype,count,ndata,ids,data): - if name: name = name.encode() - with ExceptionCheck(self): - self.lib.lammps_scatter_subset(self.lmp,name,dtype,count,ndata,ids,data) - - # ------------------------------------------------------------------------- - - def encode_image_flags(self,ix,iy,iz): - """ convert 3 integers with image flags for x-, y-, and z-direction - into a single integer like it is used internally in LAMMPS - - This method is a wrapper around the :cpp:func:`lammps_encode_image_flags` - function of library interface. - - :param ix: x-direction image flag - :type ix: int - :param iy: y-direction image flag - :type iy: int - :param iz: z-direction image flag - :type iz: int - :return: encoded image flags - :rtype: lammps.c_imageint - """ - return self.lib.lammps_encode_image_flags(ix,iy,iz) - - # ------------------------------------------------------------------------- - - def decode_image_flags(self,image): - """ Convert encoded image flag integer into list of three regular integers. - - This method is a wrapper around the :cpp:func:`lammps_decode_image_flags` - function of library interface. - - :param image: encoded image flags - :type image: lammps.c_imageint - :return: list of three image flags in x-, y-, and z- direction - :rtype: list of 3 int - """ - - flags = (c_int*3)() - self.lib.lammps_decode_image_flags(image,byref(flags)) - - return [int(i) for i in flags] - - # ------------------------------------------------------------------------- - - # create N atoms on all procs - # N = global number of atoms - # id = ID of each atom (optional, can be None) - # type = type of each atom (1 to Ntypes) (required) - # x = coords of each atom as (N,3) array (required) - # v = velocity of each atom as (N,3) array (optional, can be None) - # NOTE: how could we insure are passing correct type to LAMMPS - # e.g. for Python list or NumPy, etc - # ditto for gather_atoms() above - - def create_atoms(self,n,id,type,x,v=None,image=None,shrinkexceed=False): - """ - Create N atoms from list of coordinates and properties - - This function is a wrapper around the :cpp:func:`lammps_create_atoms` - function of the C-library interface, and the behavior is similar except - that the *v*, *image*, and *shrinkexceed* arguments are optional and - default to *None*, *None*, and *False*, respectively. With *None* being - equivalent to a ``NULL`` pointer in C. - - The lists of coordinates, types, atom IDs, velocities, image flags can - be provided in any format that may be converted into the required - internal data types. Also the list may contain more than *N* entries, - but not fewer. In the latter case, the function will return without - attempting to create atoms. You may use the :py:func:`encode_image_flags - ` method to properly combine three integers - with image flags into a single integer. - - :param n: number of atoms for which data is provided - :type n: int - :param id: list of atom IDs with at least n elements or None - :type id: list of lammps.tagint - :param type: list of atom types - :type type: list of int - :param x: list of coordinates for x-, y-, and z (flat list of 3n entries) - :type x: list of float - :param v: list of velocities for x-, y-, and z (flat list of 3n entries) or None (optional) - :type v: list of float - :param image: list of encoded image flags (optional) - :type image: list of lammps.imageint - :param shrinkexceed: whether to expand shrink-wrap boundaries if atoms are outside the box (optional) - :type shrinkexceed: bool - :return: number of atoms created. 0 if insufficient or invalid data - :rtype: int - """ - if id is not None: - id_lmp = (self.c_tagint*n)() - try: - id_lmp[:] = id[0:n] - except ValueError: - return 0 - else: - id_lmp = None - - type_lmp = (c_int*n)() - try: - type_lmp[:] = type[0:n] - except ValueError: - return 0 - - three_n = 3*n - x_lmp = (c_double*three_n)() - try: - x_lmp[:] = x[0:three_n] - except ValueError: - return 0 - - if v is not None: - v_lmp = (c_double*(three_n))() - try: - v_lmp[:] = v[0:three_n] - except ValueError: - return 0 - else: - v_lmp = None - - if image is not None: - img_lmp = (self.c_imageint*n)() - try: - img_lmp[:] = image[0:n] - except ValueError: - return 0 - else: - img_lmp = None - - if shrinkexceed: - se_lmp = 1 - else: - se_lmp = 0 - - self.lib.lammps_create_atoms.argtypes = [c_void_p, c_int, POINTER(self.c_tagint*n), - POINTER(c_int*n), POINTER(c_double*three_n), - POINTER(c_double*three_n), - POINTER(self.c_imageint*n), c_int] - with ExceptionCheck(self): - return self.lib.lammps_create_atoms(self.lmp, n, id_lmp, type_lmp, x_lmp, v_lmp, img_lmp, se_lmp) - - # ------------------------------------------------------------------------- - - @property - def has_mpi_support(self): - """ Report whether the LAMMPS shared library was compiled with a - real MPI library or in serial. - - This is a wrapper around the :cpp:func:`lammps_config_has_mpi_support` - function of the library interface. - - :return: False when compiled with MPI STUBS, otherwise True - :rtype: bool - """ - return self.lib.lammps_config_has_mpi_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def is_running(self): - """ Report whether being called from a function during a run or a minimization - - Various LAMMPS commands must not be called during an ongoing - run or minimization. This property allows to check for that. - This is a wrapper around the :cpp:func:`lammps_is_running` - function of the library interface. - - .. versionadded:: 9Oct2020 - - :return: True when called during a run otherwise false - :rtype: bool - """ - return self.lib.lammps_is_running(self.lmp) == 1 - - # ------------------------------------------------------------------------- - - def force_timeout(self): - """ Trigger an immediate timeout, i.e. a "soft stop" of a run. - - This function allows to cleanly stop an ongoing run or minimization - at the next loop iteration. - This is a wrapper around the :cpp:func:`lammps_force_timeout` - function of the library interface. - - .. versionadded:: 9Oct2020 - """ - self.lib.lammps_force_timeout(self.lmp) - - # ------------------------------------------------------------------------- - - @property - def has_exceptions(self): - """ Report whether the LAMMPS shared library was compiled with C++ - exceptions handling enabled - - This is a wrapper around the :cpp:func:`lammps_config_has_exceptions` - function of the library interface. - - :return: state of C++ exception support - :rtype: bool - """ - return self.lib.lammps_config_has_exceptions() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_gzip_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for reading and writing compressed files through ``gzip``. - - This is a wrapper around the :cpp:func:`lammps_config_has_gzip_support` - function of the library interface. - - :return: state of gzip support - :rtype: bool - """ - return self.lib.lammps_config_has_gzip_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_png_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for writing images in PNG format. - - This is a wrapper around the :cpp:func:`lammps_config_has_png_support` - function of the library interface. - - :return: state of PNG support - :rtype: bool - """ - return self.lib.lammps_config_has_png_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_jpeg_support(self): - """ Report whether the LAMMPS shared library was compiled with support - for writing images in JPEG format. - - This is a wrapper around the :cpp:func:`lammps_config_has_jpeg_support` - function of the library interface. - - :return: state of JPEG support - :rtype: bool - """ - return self.lib.lammps_config_has_jpeg_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def has_ffmpeg_support(self): - """ State of support for writing movies with ``ffmpeg`` in the LAMMPS shared library - - This is a wrapper around the :cpp:func:`lammps_config_has_ffmpeg_support` - function of the library interface. - - :return: state of ffmpeg support - :rtype: bool - """ - return self.lib.lammps_config_has_ffmpeg_support() != 0 - - # ------------------------------------------------------------------------- - - @property - def accelerator_config(self): - """ Return table with available accelerator configuration settings. - - This is a wrapper around the :cpp:func:`lammps_config_accelerator` - function of the library interface which loops over all known packages - and categories and returns enabled features as a nested dictionary - with all enabled settings as list of strings. - - :return: nested dictionary with all known enabled settings as list of strings - :rtype: dictionary - """ - - result = {} - for p in ['GPU', 'KOKKOS', 'INTEL', 'OPENMP']: - result[p] = {} - c = 'api' - result[p][c] = [] - for s in ['cuda', 'hip', 'phi', 'pthreads', 'opencl', 'openmp', 'serial']: - if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): - result[p][c].append(s) - c = 'precision' - result[p][c] = [] - for s in ['double', 'mixed', 'single']: - if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): - result[p][c].append(s) - return result - - # ------------------------------------------------------------------------- - - @property - def has_gpu_device(self): - """ Availability of GPU package compatible device - - This is a wrapper around the :cpp:func:`lammps_has_gpu_device` - function of the C library interface. - - :return: True if a GPU package compatible device is present, otherwise False - :rtype: bool - """ - return self.lib.lammps_has_gpu_device() != 0 - - # ------------------------------------------------------------------------- - - def get_gpu_device_info(self): - """Return a string with detailed information about any devices that are - usable by the GPU package. - - This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` - function of the C-library interface. - - :return: GPU device info string - :rtype: string - """ - - sb = create_string_buffer(8192) - self.lib.lammps_get_gpu_device_info(sb,8192) - return sb.value.decode() - - # ------------------------------------------------------------------------- - - @property - def installed_packages(self): - """ List of the names of enabled packages in the LAMMPS shared library - - This is a wrapper around the functions :cpp:func:`lammps_config_package_count` - and :cpp:func`lammps_config_package_name` of the library interface. - - :return - """ - if self._installed_packages is None: - self._installed_packages = [] - npackages = self.lib.lammps_config_package_count() - sb = create_string_buffer(100) - for idx in range(npackages): - self.lib.lammps_config_package_name(idx, sb, 100) - self._installed_packages.append(sb.value.decode()) - return self._installed_packages - - # ------------------------------------------------------------------------- - - def has_style(self, category, name): - """Returns whether a given style name is available in a given category - - This is a wrapper around the function :cpp:func:`lammps_has_style` - of the library interface. - - :param category: name of category - :type category: string - :param name: name of the style - :type name: string - - :return: true if style is available in given category - :rtype: bool - """ - return self.lib.lammps_has_style(self.lmp, category.encode(), name.encode()) != 0 - - # ------------------------------------------------------------------------- - - def available_styles(self, category): - """Returns a list of styles available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_style_count()` - and :cpp:func:`lammps_style_name()` of the library interface. - - :param category: name of category - :type category: string - - :return: list of style names in given category - :rtype: list - """ - if self._available_styles is None: - self._available_styles = {} - - if category not in self._available_styles: - self._available_styles[category] = [] - with ExceptionCheck(self): - nstyles = self.lib.lammps_style_count(self.lmp, category.encode()) - sb = create_string_buffer(100) - for idx in range(nstyles): - with ExceptionCheck(self): - self.lib.lammps_style_name(self.lmp, category.encode(), idx, sb, 100) - self._available_styles[category].append(sb.value.decode()) - return self._available_styles[category] - - # ------------------------------------------------------------------------- - - def has_id(self, category, name): - """Returns whether a given ID name is available in a given category - - This is a wrapper around the function :cpp:func:`lammps_has_id` - of the library interface. - - .. versionadded:: 9Oct2020 - - :param category: name of category - :type category: string - :param name: name of the ID - :type name: string - - :return: true if ID is available in given category - :rtype: bool - """ - return self.lib.lammps_has_id(self.lmp, category.encode(), name.encode()) != 0 - - # ------------------------------------------------------------------------- - - def available_ids(self, category): - """Returns a list of IDs available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_id_count()` - and :cpp:func:`lammps_id_name()` of the library interface. - - .. versionadded:: 9Oct2020 - - :param category: name of category - :type category: string - - :return: list of id names in given category - :rtype: list - """ - - categories = ['compute','dump','fix','group','molecule','region','variable'] - available_ids = [] - if category in categories: - num = self.lib.lammps_id_count(self.lmp, category.encode()) - sb = create_string_buffer(100) - for idx in range(num): - self.lib.lammps_id_name(self.lmp, category.encode(), idx, sb, 100) - available_ids.append(sb.value.decode()) - return available_ids - - # ------------------------------------------------------------------------- - - def available_plugins(self, category): - """Returns a list of plugins available for a given category - - This is a wrapper around the functions :cpp:func:`lammps_plugin_count()` - and :cpp:func:`lammps_plugin_name()` of the library interface. - - .. versionadded:: 10Mar2021 - - :return: list of style/name pairs of loaded plugins - :rtype: list - """ - - available_plugins = [] - num = self.lib.lammps_plugin_count(self.lmp) - sty = create_string_buffer(100) - nam = create_string_buffer(100) - for idx in range(num): - self.lib.lammps_plugin_name(idx, sty, nam, 100) - available_plugins.append([sty.value.decode(), nam.value.decode()]) - return available_plugins - - # ------------------------------------------------------------------------- - - def set_fix_external_callback(self, fix_id, callback, caller=None): - """Set the callback function for a fix external instance with a given fix ID. - - Optionally also set a reference to the calling object. - - This is a wrapper around the :cpp:func:`lammps_set_fix_external_callback` function - of the C-library interface. However this is set up to call a Python function with - the following arguments. - - .. code-block: python - - def func(object, ntimestep, nlocal, tag, x, f): - - - object is the value of the "caller" argument - - ntimestep is the current timestep - - nlocal is the number of local atoms on the current MPI process - - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms - - x is a 2d NumPy array of doubles of the coordinates of the local atoms - - f is a 2d NumPy array of doubles of the forces on the local atoms that will be added - - .. versionchanged:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param callback: Python function that will be called from fix external - :type: function - :param caller: reference to some object passed to the callback function - :type: object, optional - """ - import numpy as np - - def callback_wrapper(caller, ntimestep, nlocal, tag_ptr, x_ptr, fext_ptr): - tag = self.numpy.iarray(self.c_tagint, tag_ptr, nlocal, 1) - x = self.numpy.darray(x_ptr, nlocal, 3) - f = self.numpy.darray(fext_ptr, nlocal, 3) - callback(caller, ntimestep, nlocal, tag, x, f) - - cFunc = self.FIX_EXTERNAL_CALLBACK_FUNC(callback_wrapper) - cCaller = caller - - self.callback[fix_id] = { 'function': cFunc, 'caller': caller } - with ExceptionCheck(self): - self.lib.lammps_set_fix_external_callback(self.lmp, fix_id.encode(), cFunc, cCaller) - - # ------------------------------------------------------------------------- - - def fix_external_get_force(self, fix_id): - """Get access to the array with per-atom forces of a fix external instance with a given fix ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :return: requested data - :rtype: ctypes.POINTER(ctypes.POINTER(ctypes.double)) - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_get_force(self.lmp, fix_id.encode()) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_global(self, fix_id, eng): - """Set the global energy contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eng: potential energy value to be added by fix external - :type: float - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_global(self, fix_id, virial): - """Set the global virial contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eng: list of 6 floating point numbers with the virial to be added by fix external - :type: float - """ - - cvirial = (6*c_double)(*virial) - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_peratom(self, fix_id, eatom): - """Set the per-atom energy contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: list of potential energy values for local atoms to be added by fix external - :type: float - """ - - nlocal = self.extract_setting('nlocal') - if len(eatom) < nlocal: - raise Exception('per-atom energy list length must be at least nlocal') - ceatom = (nlocal*c_double)(*eatom) - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_peratom(self, fix_id, vatom): - """Set the per-atom virial contribution for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external - :type: float - """ - - # copy virial data to C compatible buffer - nlocal = self.extract_setting('nlocal') - if len(vatom) < nlocal: - raise Exception('per-atom virial first dimension must be at least nlocal') - if len(vatom[0]) != 6: - raise Exception('per-atom virial second dimension must be 6') - vbuf = (c_double * 6) - vptr = POINTER(c_double) - c_virial = (vptr * nlocal)() - for i in range(nlocal): - c_virial[i] = vbuf() - for j in range(6): - c_virial[i][j] = vatom[i][j] - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), c_virial) - - # ------------------------------------------------------------------------- - def fix_external_set_vector_length(self, fix_id, length): - """Set the vector length for a global vector stored with fix external for analysis - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param length: length of the global vector - :type: int - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_vector_length(self.lmp, fix_id.encode(), length) - - # ------------------------------------------------------------------------- - def fix_external_set_vector(self, fix_id, idx, val): - """Store a global vector value for a fix external instance with the given ID. - - This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function - of the C-library interface. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param idx: 1-based index of the value in the global vector - :type: int - :param val: value to be stored in the global vector - :type: float - """ - - with ExceptionCheck(self): - return self.lib.lammps_fix_external_set_vector(self.lmp, fix_id.encode(), idx, val) - - # ------------------------------------------------------------------------- - - def get_neighlist(self, idx): - """Returns an instance of :class:`NeighList` which wraps access to the neighbor list with the given index - - See :py:meth:`lammps.numpy.get_neighlist() ` if you want to use - NumPy arrays instead of ``c_int`` pointers. - - :param idx: index of neighbor list - :type idx: int - :return: an instance of :class:`NeighList` wrapping access to neighbor list data - :rtype: NeighList - """ - if idx < 0: - return None - return NeighList(self, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_size(self, idx): - """Return the number of elements in neighbor list with the given index - - :param idx: neighbor list index - :type idx: int - :return: number of elements in neighbor list with index idx - :rtype: int - """ - return self.lib.lammps_neighlist_num_elements(self.lmp, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_element_neighbors(self, idx, element): - """Return data of neighbor list entry - - :param element: neighbor list index - :type element: int - :param element: neighbor list element index - :type element: int - :return: tuple with atom local index, number of neighbors and array of neighbor local atom indices - :rtype: (int, int, POINTER(c_int)) - """ - c_iatom = c_int() - c_numneigh = c_int() - c_neighbors = POINTER(c_int)() - self.lib.lammps_neighlist_element_neighbors(self.lmp, idx, element, byref(c_iatom), byref(c_numneigh), byref(c_neighbors)) - return c_iatom.value, c_numneigh.value, c_neighbors - - # ------------------------------------------------------------------------- - - def find_pair_neighlist(self, style, exact=True, nsub=0, reqid=0): - """Find neighbor list index of pair style neighbor list - - Search for a neighbor list requested by a pair style instance that - matches "style". If exact is True, the pair style name must match - exactly. If exact is False, the pair style name is matched against - "style" as regular expression or sub-string. If the pair style is a - hybrid pair style, the style is instead matched against the hybrid - sub-styles. If the same pair style is used as sub-style multiple - types, you must set nsub to a value n > 0 which indicates the nth - instance of that sub-style to be used (same as for the pair_coeff - command). The default value of 0 will fail to match in that case. - - Once the pair style instance has been identified, it may have - requested multiple neighbor lists. Those are uniquely identified by - a request ID > 0 as set by the pair style. Otherwise the request - ID is 0. - - :param style: name of pair style that should be searched for - :type style: string - :param exact: controls whether style should match exactly or only must be contained in pair style name, defaults to True - :type exact: bool, optional - :param nsub: match nsub-th hybrid sub-style, defaults to 0 - :type nsub: int, optional - :param reqid: list request id, > 0 in case there are more than one, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - style = style.encode() - exact = int(exact) - idx = self.lib.lammps_find_pair_neighlist(self.lmp, style, exact, nsub, reqid) - return idx - - # ------------------------------------------------------------------------- - - def find_fix_neighlist(self, fixid, reqid=0): - """Find neighbor list index of fix neighbor list - - The fix instance requesting the neighbor list is uniquely identified - by the fix ID. In case the fix has requested multiple neighbor - lists, those are uniquely identified by a request ID > 0 as set by - the fix. Otherwise the request ID is 0 (the default). - - :param fixid: name of fix - :type fixid: string - :param reqid: id of neighbor list request, in case there are more than one request, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - fixid = fixid.encode() - idx = self.lib.lammps_find_fix_neighlist(self.lmp, fixid, reqid) - return idx - - # ------------------------------------------------------------------------- - - def find_compute_neighlist(self, computeid, reqid=0): - """Find neighbor list index of compute neighbor list - - The compute instance requesting the neighbor list is uniquely - identified by the compute ID. In case the compute has requested - multiple neighbor lists, those are uniquely identified by a request - ID > 0 as set by the compute. Otherwise the request ID is 0 (the - default). - - :param computeid: name of compute - :type computeid: string - :param reqid: index of neighbor list request, in case there are more than one request, defaults to 0 - :type reqid: int, optional - :return: neighbor list index if found, otherwise -1 - :rtype: int - - """ - computeid = computeid.encode() - idx = self.lib.lammps_find_compute_neighlist(self.lmp, computeid, reqid) - return idx diff --git a/python/lammps/data.py b/python/lammps/data.py deleted file mode 100644 index 731a8c514a..0000000000 --- a/python/lammps/data.py +++ /dev/null @@ -1,92 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# LAMMPS data structures -# Written by Richard Berger -################################################################################ - -class NeighList(object): - """This is a wrapper class that exposes the contents of a neighbor list. - - It can be used like a regular Python list. Each element is a tuple of: - - * the atom local index - * its number of neighbors - * and a pointer to an c_int array containing local atom indices of its - neighbors - - Internally it uses the lower-level LAMMPS C-library interface. - - :param lmp: reference to instance of :py:class:`lammps` - :type lmp: lammps - :param idx: neighbor list index - :type idx: int - """ - def __init__(self, lmp, idx): - self.lmp = lmp - self.idx = idx - - def __str__(self): - return "Neighbor List ({} atoms)".format(self.size) - - def __repr__(self): - return self.__str__() - - @property - def size(self): - """ - :return: number of elements in neighbor list - """ - return self.lmp.get_neighlist_size(self.idx) - - def get(self, element): - """ - Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list - - :return: tuple with atom local index, number of neighbors and ctypes pointer to neighbor's local atom indices - :rtype: (int, int, ctypes.POINTER(c_int)) - """ - iatom, numneigh, neighbors = self.lmp.get_neighlist_element_neighbors(self.idx, element) - return iatom, numneigh, neighbors - - # the methods below implement the iterator interface, so NeighList can be used like a regular Python list - - def __getitem__(self, element): - return self.get(element) - - def __len__(self): - return self.size - - def __iter__(self): - inum = self.size - - for ii in range(inum): - yield self.get(ii) - - def find(self, iatom): - """ - Find the neighbor list for a specific (local) atom iatom. - If there is no list for iatom, (-1, None) is returned. - - :return: tuple with number of neighbors and ctypes pointer to neighbor's local atom indices - :rtype: (int, ctypes.POINTER(c_int)) - """ - - inum = self.size - for ii in range(inum): - idx, numneigh, neighbors = self.get(ii) - if idx == iatom: - return numneigh, neighbors - - return -1, None diff --git a/python/lammps/formats.py b/python/lammps/formats.py deleted file mode 100644 index b7c267466f..0000000000 --- a/python/lammps/formats.py +++ /dev/null @@ -1,227 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# LAMMPS output formats -# Written by Richard Berger -# and Axel Kohlmeyer -################################################################################ - -import re - -has_yaml = False -try: - import yaml - has_yaml = True - try: - from yaml import CSafeLoader as Loader - except ImportError: - from yaml import SafeLoader as Loader -except ImportError: - # ignore here, raise an exception when trying to parse yaml instead - pass - -class LogFile: - """Reads LAMMPS log files and extracts the thermo information - - It supports the line, multi, and yaml thermo output styles. - - :param filename: path to log file - :type filename: str - - :ivar runs: List of LAMMPS runs in log file. Each run is a dictionary with - thermo fields as keys, storing the values over time - :ivar errors: List of error lines in log file - """ - - STYLE_DEFAULT = 0 - STYLE_MULTI = 1 - STYLE_YAML = 2 - - def __init__(self, filename): - alpha = re.compile(r'[a-df-zA-DF-Z]') # except e or E for floating-point numbers - kvpairs = re.compile(r'([a-zA-Z_0-9]+)\s+=\s*([0-9\.eE\-]+)') - style = LogFile.STYLE_DEFAULT - yamllog = "" - self.runs = [] - self.errors = [] - with open(filename, 'rt') as f: - in_thermo = False - in_data_section = False - for line in f: - if "ERROR" in line or "exited on signal" in line: - self.errors.append(line) - - elif re.match(r'^ *Step ', line): - in_thermo = True - in_data_section = True - keys = line.split() - current_run = {} - for k in keys: - current_run[k] = [] - - elif re.match(r'^(keywords:.*$|data:$|---$| - \[.*\]$)', line): - if not has_yaml: - raise Exception('Cannot process YAML format logs without the PyYAML Python module') - style = LogFile.STYLE_YAML - yamllog += line; - current_run = {} - - elif re.match(r'^\.\.\.$', line): - thermo = yaml.load(yamllog, Loader=Loader) - for k in thermo['keywords']: - current_run[k] = [] - for step in thermo['data']: - icol = 0 - for k in thermo['keywords']: - current_run[k].append(step[icol]) - icol += 1 - self.runs.append(current_run) - yamllog = "" - - elif re.match(r'^------* Step ', line): - if not in_thermo: - current_run = {'Step': [], 'CPU': []} - in_thermo = True - in_data_section = True - style = LogFile.STYLE_MULTI - str_step, str_cpu = line.strip('-\n').split('-----') - step = float(str_step.split()[1]) - cpu = float(str_cpu.split('=')[1].split()[0]) - current_run["Step"].append(step) - current_run["CPU"].append(cpu) - - elif line.startswith('Loop time of'): - in_thermo = False - if style != LogFile.STYLE_YAML: - self.runs.append(current_run) - - elif in_thermo and in_data_section: - if style == LogFile.STYLE_DEFAULT: - if alpha.search(line): - continue - for k, v in zip(keys, map(float, line.split())): - current_run[k].append(v) - - elif style == LogFile.STYLE_MULTI: - if '=' not in line: - in_data_section = False - continue - for k,v in kvpairs.findall(line): - if k not in current_run: - current_run[k] = [float(v)] - else: - current_run[k].append(float(v)) - -class AvgChunkFile: - """Reads files generated by fix ave/chunk - - :param filename: path to ave/chunk file - :type filename: str - - :ivar timesteps: List of timesteps stored in file - :ivar total_count: total count over time - :ivar chunks: List of chunks. Each chunk is a dictionary containing its ID, the coordinates, and the averaged quantities - """ - def __init__(self, filename): - with open(filename, 'rt') as f: - timestep = None - chunks_read = 0 - - self.timesteps = [] - self.total_count = [] - self.chunks = [] - - for lineno, line in enumerate(f): - if lineno == 0: - if not line.startswith("# Chunk-averaged data for fix"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - parts = line.split() - self.fix_name = parts[5] - self.group_name = parts[8] - continue - elif lineno == 1: - if not line.startswith("# Timestep Number-of-chunks Total-count"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - continue - elif lineno == 2: - if not line.startswith("#"): - raise Exception("Chunk data reader only supports default avg/chunk headers!") - columns = line.split()[1:] - ndim = line.count("Coord") - compress = 'OrigID' in line - if ndim > 0: - coord_start = columns.index("Coord1") - coord_end = columns.index("Coord%d" % ndim) - ncount_start = coord_end + 1 - data_start = ncount_start + 1 - else: - coord_start = None - coord_end = None - ncount_start = 2 - data_start = 3 - continue - - parts = line.split() - - if timestep is None: - timestep = int(parts[0]) - num_chunks = int(parts[1]) - total_count = float(parts[2]) - - self.timesteps.append(timestep) - self.total_count.append(total_count) - - for i in range(num_chunks): - self.chunks.append({ - 'coord' : [], - 'ncount' : [] - }) - elif chunks_read < num_chunks: - chunk = int(parts[0]) - ncount = float(parts[ncount_start]) - - if compress: - chunk_id = int(parts[1]) - else: - chunk_id = chunk - - current = self.chunks[chunk_id - 1] - current['id'] = chunk_id - current['ncount'].append(ncount) - - if ndim > 0: - coord = tuple(map(float, parts[coord_start:coord_end+1])) - current['coord'].append(coord) - - for i, data_column in list(enumerate(columns))[data_start:]: - value = float(parts[i]) - - if data_column in current: - current[data_column].append(value) - else: - current[data_column] = [value] - - chunks_read += 1 - assert chunk == chunks_read - else: - # do not support changing number of chunks - if not (num_chunks == int(parts[1])): - raise Exception("Currently, changing numbers of chunks are not supported.") - - timestep = int(parts[0]) - total_count = float(parts[2]) - chunks_read = 0 - - self.timesteps.append(timestep) - self.total_count.append(total_count) diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py deleted file mode 100644 index 57fe97d803..0000000000 --- a/python/lammps/mliap/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ - -# Check compatiblity of this build with the python shared library. -# If this fails, lammps will segfault because its library will -# try to improperly start up a new interpreter. -import sysconfig -import ctypes -library = sysconfig.get_config_vars('INSTSONAME')[0] -try: - pylib = ctypes.CDLL(library) -except OSError as e: - if pylib.endswith(".a"): - pylib.strip(".a") + ".so" - pylib = ctypes.CDLL(library) - else: - raise e -if not pylib.Py_IsInitialized(): - raise RuntimeError("This interpreter is not compatible with python-based mliap for LAMMPS.") -del sysconfig, ctypes, library, pylib - -from .loader import load_model, activate_mliappy diff --git a/python/lammps/mliap/loader.py b/python/lammps/mliap/loader.py deleted file mode 100644 index dff791bfc1..0000000000 --- a/python/lammps/mliap/loader.py +++ /dev/null @@ -1,52 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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: Nicholas Lubbers (LANL) -# ------------------------------------------------------------------------- - - -import sys -import importlib.util -import importlib.machinery - -def activate_mliappy(lmp): - try: - # Begin Importlib magic to find the embedded python module - # This is needed because the filename for liblammps does not - # match the spec for normal python modules, wherein - # file names match with PyInit function names. - # Also, python normally doesn't look for extensions besides '.so' - # We fix both of these problems by providing an explict - # path to the extension module 'mliap_model_python_couple' in - - path = lmp.lib._name - loader = importlib.machinery.ExtensionFileLoader('mliap_model_python_couple', path) - spec = importlib.util.spec_from_loader('mliap_model_python_couple', loader) - module = importlib.util.module_from_spec(spec) - sys.modules['mliap_model_python_couple'] = module - spec.loader.exec_module(module) - # End Importlib magic to find the embedded python module - - except Exception as ee: - raise ImportError("Could not load ML-IAP python coupling module.") from ee - -def load_model(model): - try: - import mliap_model_python_couple - except ImportError as ie: - raise ImportError("ML-IAP python module must be activated before loading\n" - "the pair style. Call lammps.mliap.activate_mliappy(lmp)." - ) from ie - mliap_model_python_couple.load_from_python(model) - diff --git a/python/lammps/mliap/pytorch.py b/python/lammps/mliap/pytorch.py deleted file mode 100644 index 9aa2da80f4..0000000000 --- a/python/lammps/mliap/pytorch.py +++ /dev/null @@ -1,326 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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: Nicholas Lubbers (LANL) -# ------------------------------------------------------------------------- - -import numpy as np -import torch - -def calc_n_params(model): - """ - Returns the sum of two decimal numbers in binary digits. - - Parameters: - model (torch.nn.Module): Network model that maps descriptors to a per atom attribute - - Returns: - n_params (int): Number of NN model parameters - """ - return sum(p.nelement() for p in model.parameters()) - -class TorchWrapper(torch.nn.Module): - """ - A class to wrap Modules to ensure lammps mliap compatability. - - ... - - Attributes - ---------- - model : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - device : torch.nn.Module (None) - Accelerator device - - dtype : torch.dtype (torch.float64) - Dtype to use on device - - n_params : torch.nn.Module (None) - Number of NN model parameters - - n_descriptors : int - Max number of per atom descriptors - - n_elements : int - Max number of elements - - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model to produce per atom energies and forces. - """ - - def __init__(self, model, n_descriptors, n_elements, n_params=None, device=None, dtype=torch.float64): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - model : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - n_descriptors : int - Max number of per atom descriptors - - n_elements : int - Max number of elements - - n_params : torch.nn.Module (None) - Number of NN model parameters - - device : torch.nn.Module (None) - Accelerator device - - dtype : torch.dtype (torch.float64) - Dtype to use on device - """ - - super().__init__() - - self.model = model - self.device = device - self.dtype = dtype - - # Put model on device and convert to dtype - self.to(self.dtype) - self.to(self.device) - - if n_params is None: - n_params = calc_n_params(model) - - self.n_params = n_params - self.n_descriptors = n_descriptors - self.n_elements = n_elements - - def forward(self, elems, descriptors, beta, energy): - """ - Takes element types and descriptors calculated via lammps and - calculates the per atom energies and forces. - - Parameters - ---------- - elems : numpy.array - Per atom element types - - descriptors : numpy.array - Per atom descriptors - - beta : numpy.array - Expired beta array to be filled with new betas - - energy : numpy.array - Expired per atom energy array to be filled with new per atom energy - (Note: This is a pointer to the lammps per atom energies) - - - Returns - ------- - None - """ - - descriptors = torch.from_numpy(descriptors).to(dtype=self.dtype, device=self.device).requires_grad_(True) - elems = torch.from_numpy(elems).to(dtype=torch.long, device=self.device) - 1 - - with torch.autograd.enable_grad(): - - energy_nn = self.model(descriptors, elems) - if energy_nn.ndim > 1: - energy_nn = energy_nn.flatten() - - beta_nn = torch.autograd.grad(energy_nn.sum(), descriptors)[0] - - beta[:] = beta_nn.detach().cpu().numpy().astype(np.float64) - energy[:] = energy_nn.detach().cpu().numpy().astype(np.float64) - - -class IgnoreElems(torch.nn.Module): - """ - A class to represent a NN model agnostic of element typing. - - ... - - Attributes - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model - """ - - def __init__(self, subnet): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - """ - - super().__init__() - self.subnet = subnet - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnet(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - return self.subnet(descriptors) - - -class UnpackElems(torch.nn.Module): - """ - A class to represent a NN model pseudo-agnostic of element typing for - systems with multiple element typings. - - ... - - Attributes - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute - - n_types : int - Number of atom types used in training the NN model. - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - """ - - def __init__(self, subnet, n_types): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnet : torch.nn.Module - Network model that maps descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - """ - super().__init__() - self.subnet = subnet - self.n_types = n_types - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnet(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - unpacked_descriptors = torch.zeros(elems.shape[0], self.n_types, descriptors.shape[1], dtype=torch.float64) - for i, ind in enumerate(elems): - unpacked_descriptors[i, ind, :] = descriptors[i] - return self.subnet(torch.reshape(unpacked_descriptors, (elems.shape[0], -1)), elems) - - -class ElemwiseModels(torch.nn.Module): - """ - A class to represent a NN model dependent on element typing. - - ... - - Attributes - ---------- - subnets : list of torch.nn.Modules - Per element type network models that maps per element type - descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - - Methods - ------- - forward(descriptors, elems): - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - """ - - def __init__(self, subnets, n_types): - """ - Constructs all the necessary attributes for the network module. - - Parameters - ---------- - subnets : list of torch.nn.Modules - Per element type network models that maps per element - type descriptors to a per atom attribute. - - n_types : int - Number of atom types used in training the NN model. - """ - - super().__init__() - self.subnets = subnets - self.n_types = n_types - - def forward(self, descriptors, elems): - """ - Feeds descriptors to network model after adding zeros into - descriptor columns relating to different atom types - - Parameters - ---------- - descriptors : torch.tensor - Per atom descriptors - - elems : torch.tensor - Per atom element types - - Returns - ------- - self.subnets(descriptors) : torch.tensor - Per atom attribute computed by the network model - """ - - per_atom_attributes = torch.zeros(elems.size[0]) - given_elems, elem_indices = torch.unique(elems, return_inverse=True) - for i, elem in enumerate(given_elems): - per_atom_attribute[elem_indices == i] = self.subnets[elem](descriptors[elem_indices == i]) - return per_atom_attributes diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py deleted file mode 100644 index ce0cb35e47..0000000000 --- a/python/lammps/numpy_wrapper.py +++ /dev/null @@ -1,483 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# NumPy additions -# Written by Richard Berger -################################################################################ - -import warnings -from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast - - -from .constants import * # lgtm [py/polluting-import] -from .data import NeighList - - -class numpy_wrapper: - """lammps API NumPy Wrapper - - This is a wrapper class that provides additional methods on top of an - existing :py:class:`lammps` instance. The methods transform raw ctypes - pointers into NumPy arrays, which give direct access to the - original data while protecting against out-of-bounds accesses. - - There is no need to explicitly instantiate this class. Each instance - of :py:class:`lammps` has a :py:attr:`numpy ` property - that returns an instance. - - :param lmp: instance of the :py:class:`lammps` class - :type lmp: lammps - """ - def __init__(self, lmp): - self.lmp = lmp - - # ------------------------------------------------------------------------- - - def _ctype_to_numpy_int(self, ctype_int): - import numpy as np - if ctype_int == c_int32: - return np.int32 - elif ctype_int == c_int64: - return np.int64 - return np.intc - - # ------------------------------------------------------------------------- - - def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT): - """Retrieve per-atom properties from LAMMPS as NumPy arrays - - This is a wrapper around the :py:meth:`lammps.extract_atom()` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - .. note:: - - While the returned arrays of per-atom data are dimensioned - for the range [0:nmax] - as is the underlying storage - - the data is usually only valid for the range of [0:nlocal], - unless the property of interest is also updated for ghost - atoms. In some cases, this depends on a LAMMPS setting, see - for example :doc:`comm_modify vel yes `. - - :param name: name of the property - :type name: string - :param dtype: type of the returned data (see :ref:`py_datatype_constants`) - :type dtype: int, optional - :param nelem: number of elements in array - :type nelem: int, optional - :param dim: dimension of each element - :type dim: int, optional - :return: requested data as NumPy array with direct access to C data or None - :rtype: numpy.array or NoneType - """ - if dtype == LAMMPS_AUTODETECT: - dtype = self.lmp.extract_atom_datatype(name) - - if nelem == LAMMPS_AUTODETECT: - if name == "mass": - nelem = self.lmp.extract_global("ntypes") + 1 - else: - nelem = self.lmp.extract_global("nlocal") - if dim == LAMMPS_AUTODETECT: - if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D): - # TODO add other fields - if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"): - dim = 3 - elif name == "smd_data_9": - dim = 9 - elif name == "smd_stress": - dim = 6 - else: - dim = 2 - else: - dim = 1 - - raw_ptr = self.lmp.extract_atom(name, dtype) - - if dtype in (LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D): - return self.darray(raw_ptr, nelem, dim) - elif dtype in (LAMMPS_INT, LAMMPS_INT_2D): - return self.iarray(c_int32, raw_ptr, nelem, dim) - elif dtype in (LAMMPS_INT64, LAMMPS_INT64_2D): - return self.iarray(c_int64, raw_ptr, nelem, dim) - return raw_ptr - - # ------------------------------------------------------------------------- - - def extract_atom_iarray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if name in ['id', 'molecule']: - c_int_type = self.lmp.c_tagint - elif name in ['image']: - c_int_type = self.lmp.c_imageint - else: - c_int_type = c_int - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D) - - return self.iarray(c_int_type, raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - - def extract_atom_darray(self, name, nelem, dim=1): - warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) - - if dim == 1: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) - else: - raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D) - - return self.darray(raw_ptr, nelem, dim) - - # ------------------------------------------------------------------------- - - def extract_compute(self, cid, cstyle, ctype): - """Retrieve data from a LAMMPS compute - - This is a wrapper around the - :py:meth:`lammps.extract_compute() ` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param cid: compute ID - :type cid: string - :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type cstyle: int - :param ctype: type of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ctype: int - :return: requested data either as float, as NumPy array with direct access to C data, or None - :rtype: float, numpy.array, or NoneType - """ - value = self.lmp.extract_compute(cid, cstyle, ctype) - - if cstyle == LMP_STYLE_GLOBAL: - if ctype == LMP_TYPE_VECTOR: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_VECTOR) - return self.darray(value, nrows) - elif ctype == LMP_TYPE_ARRAY: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - return self.darray(value, nrows, ncols) - elif cstyle == LMP_STYLE_LOCAL: - nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - if ncols == 0: - return self.darray(value, nrows) - else: - return self.darray(value, nrows, ncols) - elif cstyle == LMP_STYLE_ATOM: - if ctype == LMP_TYPE_VECTOR: - nlocal = self.lmp.extract_global("nlocal") - return self.darray(value, nlocal) - elif ctype == LMP_TYPE_ARRAY: - nlocal = self.lmp.extract_global("nlocal") - ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) - return self.darray(value, nlocal, ncols) - return value - - # ------------------------------------------------------------------------- - - def extract_fix(self, fid, fstyle, ftype, nrow=0, ncol=0): - """Retrieve data from a LAMMPS fix - - This is a wrapper around the :py:meth:`lammps.extract_fix() ` method. - It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param fid: fix ID - :type fid: string - :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` - :type fstyle: int - :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` - :type ftype: int - :param nrow: index of global vector element or row index of global array element - :type nrow: int - :param ncol: column index of global array element - :type ncol: int - :return: requested data - :rtype: integer or double value, pointer to 1d or 2d double array or None - - """ - value = self.lmp.extract_fix(fid, fstyle, ftype, nrow, ncol) - if fstyle == LMP_STYLE_ATOM: - if ftype == LMP_TYPE_VECTOR: - nlocal = self.lmp.extract_global("nlocal") - return self.darray(value, nlocal) - elif ftype == LMP_TYPE_ARRAY: - nlocal = self.lmp.extract_global("nlocal") - ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) - return self.darray(value, nlocal, ncols) - elif fstyle == LMP_STYLE_LOCAL: - if ftype == LMP_TYPE_VECTOR: - nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) - return self.darray(value, nrows) - elif ftype == LMP_TYPE_ARRAY: - nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) - ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) - return self.darray(value, nrows, ncols) - return value - - # ------------------------------------------------------------------------- - - def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): - """ Evaluate a LAMMPS variable and return its data - - This function is a wrapper around the function - :py:meth:`lammps.extract_variable() ` - method. It behaves the same as the original method, but returns NumPy arrays - instead of ``ctypes`` pointers. - - :param name: name of the variable to execute - :type name: string - :param group: name of group for atom-style variable (ignored for equal-style variables) - :type group: string - :param vartype: type of variable, see :ref:`py_vartype_constants` - :type vartype: int - :return: the requested data or None - :rtype: c_double, numpy.array, or NoneType - """ - import numpy as np - value = self.lmp.extract_variable(name, group, vartype) - if vartype == LMP_VAR_ATOM: - return np.ctypeslib.as_array(value) - return value - - # ------------------------------------------------------------------------- - - def gather_bonds(self): - """Retrieve global list of bonds as NumPy array - - This is a wrapper around :py:meth:`lammps.gather_bonds() ` - It behaves the same as the original method, but returns a NumPy array instead - of a ``ctypes`` list. - - .. versionadded:: 28Jul2021 - - :return: the requested data as a 2d-integer numpy array - :rtype: numpy.array(nbonds,3) - """ - import numpy as np - nbonds, value = self.lmp.gather_bonds() - return np.ctypeslib.as_array(value).reshape(nbonds,3) - - # ------------------------------------------------------------------------- - - def fix_external_get_force(self, fix_id): - """Get access to the array with per-atom forces of a fix external instance with a given fix ID. - - This function is a wrapper around the - :py:meth:`lammps.fix_external_get_force() ` - method. It behaves the same as the original method, but returns a NumPy array instead - of a ``ctypes`` pointer. - - .. versionchanged:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :return: requested data - :rtype: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - value = self.lmp.fix_external_get_force(fix_id) - return self.darray(value,nlocal,3) - - # ------------------------------------------------------------------------- - - def fix_external_set_energy_peratom(self, fix_id, eatom): - """Set the per-atom energy contribution for a fix external instance with the given ID. - - This function is an alternative to - :py:meth:`lammps.fix_external_set_energy_peratom() ` - method. It behaves the same as the original method, but accepts a NumPy array - instead of a list as argument. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: per-atom potential energy - :type: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - if len(eatom) < nlocal: - raise Exception('per-atom energy dimension must be at least nlocal') - - c_double_p = POINTER(c_double) - value = eatom.astype(np.double) - return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), - value.ctypes.data_as(c_double_p)) - - # ------------------------------------------------------------------------- - - def fix_external_set_virial_peratom(self, fix_id, vatom): - """Set the per-atom virial contribution for a fix external instance with the given ID. - - This function is an alternative to - :py:meth:`lammps.fix_external_set_virial_peratom() ` - method. It behaves the same as the original method, but accepts a NumPy array - instead of a list as argument. - - .. versionadded:: 28Jul2021 - - :param fix_id: Fix-ID of a fix external instance - :type: string - :param eatom: per-atom potential energy - :type: numpy.array - """ - import numpy as np - nlocal = self.lmp.extract_setting('nlocal') - if len(vatom) < nlocal: - raise Exception('per-atom virial first dimension must be at least nlocal') - if len(vatom[0]) != 6: - raise Exception('per-atom virial second dimension must be 6') - - c_double_pp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C') - - # recast numpy array to be compatible with library interface - value = (vatom.__array_interface__['data'][0] - + np.arange(vatom.shape[0])*vatom.strides[0]).astype(np.uintp) - - # change prototype to our custom type - self.lmp.lib.lammps_fix_external_set_virial_peratom.argtypes = [ c_void_p, c_char_p, c_double_pp ] - - self.lmp.lib.lammps_fix_external_set_virial_peratom(self.lmp.lmp, fix_id.encode(), value) - - # ------------------------------------------------------------------------- - - def get_neighlist(self, idx): - """Returns an instance of :class:`NumPyNeighList` which wraps access to the neighbor list with the given index - - :param idx: index of neighbor list - :type idx: int - :return: an instance of :class:`NumPyNeighList` wrapping access to neighbor list data - :rtype: NumPyNeighList - """ - if idx < 0: - return None - return NumPyNeighList(self.lmp, idx) - - # ------------------------------------------------------------------------- - - def get_neighlist_element_neighbors(self, idx, element): - """Return data of neighbor list entry - - This function is a wrapper around the function - :py:meth:`lammps.get_neighlist_element_neighbors() ` - method. It behaves the same as the original method, but returns a NumPy array containing the neighbors - instead of a ``ctypes`` pointer. - - :param element: neighbor list index - :type element: int - :param element: neighbor list element index - :type element: int - :return: tuple with atom local index and numpy array of neighbor local atom indices - :rtype: (int, numpy.array) - """ - iatom, numneigh, c_neighbors = self.lmp.get_neighlist_element_neighbors(idx, element) - neighbors = self.iarray(c_int, c_neighbors, numneigh, 1) - return iatom, neighbors - - # ------------------------------------------------------------------------- - - def iarray(self, c_int_type, raw_ptr, nelem, dim=1): - if raw_ptr is None: - return None - - import numpy as np - np_int_type = self._ctype_to_numpy_int(c_int_type) - - if dim == 1: - ptr = cast(raw_ptr, POINTER(c_int_type * nelem)) - else: - ptr = cast(raw_ptr[0], POINTER(c_int_type * nelem * dim)) - - a = np.frombuffer(ptr.contents, dtype=np_int_type) - - if dim > 1: - a.shape = (nelem, dim) - else: - a.shape = (nelem) - return a - - # ------------------------------------------------------------------------- - - def darray(self, raw_ptr, nelem, dim=1): - if raw_ptr is None: - return None - - import numpy as np - - if dim == 1: - ptr = cast(raw_ptr, POINTER(c_double * nelem)) - else: - ptr = cast(raw_ptr[0], POINTER(c_double * nelem * dim)) - - a = np.frombuffer(ptr.contents) - - if dim > 1: - a.shape = (nelem, dim) - else: - a.shape = (nelem) - return a - -# ------------------------------------------------------------------------- - -class NumPyNeighList(NeighList): - """This is a wrapper class that exposes the contents of a neighbor list. - - It can be used like a regular Python list. Each element is a tuple of: - - * the atom local index - * a NumPy array containing the local atom indices of its neighbors - - Internally it uses the lower-level LAMMPS C-library interface. - - :param lmp: reference to instance of :py:class:`lammps` - :type lmp: lammps - :param idx: neighbor list index - :type idx: int - """ - def __init__(self, lmp, idx): - super(NumPyNeighList, self).__init__(lmp, idx) - - def get(self, element): - """ - Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list - - :return: tuple with atom local index, numpy array of neighbor local atom indices - :rtype: (int, numpy.array) - """ - iatom, neighbors = self.lmp.numpy.get_neighlist_element_neighbors(self.idx, element) - return iatom, neighbors - - def find(self, iatom): - """ - Find the neighbor list for a specific (local) atom iatom. - If there is no list for iatom, None is returned. - - :return: numpy array of neighbor local atom indices - :rtype: numpy.array or None - """ - inum = self.size - for ii in range(inum): - idx, neighbors = self.get(ii) - if idx == iatom: - return neighbors - return None diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py deleted file mode 100644 index 1fe1f2452b..0000000000 --- a/python/lammps/pylammps.py +++ /dev/null @@ -1,990 +0,0 @@ -# ---------------------------------------------------------------------- -# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator -# https://www.lammps.org/ Sandia National Laboratories -# Steve Plimpton, sjplimp@sandia.gov -# -# 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. -# ------------------------------------------------------------------------- - -################################################################################ -# Alternative Python Wrapper -# Written by Richard Berger -################################################################################ - -# for python2/3 compatibility - -from __future__ import print_function - -import io -import os -import re -import sys -import tempfile -from collections import namedtuple - -from .core import lammps - -# ------------------------------------------------------------------------- - -class OutputCapture(object): - """ Utility class to capture LAMMPS library output """ - def __init__(self): - self.stdout_fd = 1 - self.captured_output = "" - - def __enter__(self): - self.tmpfile = tempfile.TemporaryFile(mode='w+b') - - sys.stdout.flush() - - # make copy of original stdout - self.stdout_orig = os.dup(self.stdout_fd) - - # replace stdout and redirect to temp file - os.dup2(self.tmpfile.fileno(), self.stdout_fd) - return self - - def __exit__(self, exc_type, exc_value, traceback): - os.dup2(self.stdout_orig, self.stdout_fd) - os.close(self.stdout_orig) - self.tmpfile.close() - - @property - def output(self): - sys.stdout.flush() - self.tmpfile.flush() - self.tmpfile.seek(0, io.SEEK_SET) - self.captured_output = self.tmpfile.read().decode('utf-8') - return self.captured_output - -# ------------------------------------------------------------------------- - -class Variable(object): - def __init__(self, pylammps_instance, name, style, definition): - self._pylmp = pylammps_instance - self.name = name - self.style = style - self.definition = definition.split() - - @property - def value(self): - if self.style == 'atom': - return list(self._pylmp.lmp.extract_variable(self.name, "all", 1)) - else: - value = self._pylmp.lmp_print('"${%s}"' % self.name).strip() - try: - return float(value) - except ValueError: - return value - -# ------------------------------------------------------------------------- - -class AtomList(object): - """ - A dynamic list of atoms that returns either an :py:class:`Atom` or - :py:class:`Atom2D` instance for each atom. Instances are only allocated - when accessed. - - :ivar natoms: total number of atoms - :ivar dimensions: number of dimensions in system - """ - def __init__(self, pylammps_instance): - self._pylmp = pylammps_instance - self.natoms = self._pylmp.system.natoms - self.dimensions = self._pylmp.system.dimensions - self._loaded = {} - - def __getitem__(self, index): - """ - Return Atom with given local index - - :param index: Local index of atom - :type index: int - :rtype: Atom or Atom2D - """ - if index not in self._loaded: - if self.dimensions == 2: - atom = Atom2D(self._pylmp, index) - else: - atom = Atom(self._pylmp, index) - self._loaded[index] = atom - return self._loaded[index] - - def __len__(self): - return self.natoms - - -# ------------------------------------------------------------------------- - -class Atom(object): - """ - A wrapper class then represents a single atom inside of LAMMPS - - It provides access to properties of the atom and allows you to change some of them. - """ - def __init__(self, pylammps_instance, index): - self._pylmp = pylammps_instance - self.index = index - - def __dir__(self): - return [k for k in super().__dir__() if not k.startswith('_')] - - def get(self, name, index): - prop = self._pylmp.lmp.numpy.extract_atom(name) - if prop is not None: - return prop[index] - return None - - @property - def id(self): - """ - Return the atom ID - - :type: int - """ - return self.get("id", self.index) - - @property - def type(self): - """ - Return the atom type - - :type: int - """ - return self.get("type", self.index) - - @property - def mol(self): - """ - Return the atom molecule index - - :type: int - """ - return self.get("mol", self.index) - - @property - def mass(self): - """ - Return the atom mass - - :type: float - """ - return self.get("mass", self.index) - - @property - def radius(self): - """ - Return the particle radius - - :type: float - """ - return self.get("radius", self.index) - - @property - def position(self): - """ - :getter: Return position of atom - :setter: Set position of atom - :type: numpy.array (float, float, float) - """ - return self.get("x", self.index) - - @position.setter - def position(self, value): - current = self.position - current[:] = value - - @property - def velocity(self): - """ - :getter: Return velocity of atom - :setter: Set velocity of atom - :type: numpy.array (float, float, float) - """ - return self.get("v", self.index) - - @velocity.setter - def velocity(self, value): - current = self.velocity - current[:] = value - - @property - def force(self): - """ - Return the total force acting on the atom - - :type: numpy.array (float, float, float) - """ - return self.get("f", self.index) - - @force.setter - def force(self, value): - current = self.force - current[:] = value - - @property - def torque(self): - """ - Return the total torque acting on the atom - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @force.setter - def torque(self, value): - current = self.torque - current[:] = value - - @property - def omega(self): - """ - Return the rotational velocity of the particle - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @omega.setter - def omega(self, value): - current = self.torque - current[:] = value - - @property - def torque(self): - """ - Return the total torque acting on the particle - - :type: numpy.array (float, float, float) - """ - return self.get("torque", self.index) - - @torque.setter - def torque(self, value): - current = self.torque - current[:] = value - - @property - def angular_momentum(self): - """ - Return the angular momentum of the particle - - :type: numpy.array (float, float, float) - """ - return self.get("angmom", self.index) - - @angular_momentum.setter - def angular_momentum(self, value): - current = self.angular_momentum - current[:] = value - - @property - def charge(self): - """ - Return the atom charge - - :type: float - """ - return self.get("q", self.index) - -# ------------------------------------------------------------------------- - -class Atom2D(Atom): - """ - A wrapper class then represents a single 2D atom inside of LAMMPS - - Inherits all properties from the :py:class:`Atom` class, but returns 2D versions - of position, velocity, and force. - - It provides access to properties of the atom and allows you to change some of them. - """ - def __init__(self, pylammps_instance, index): - super(Atom2D, self).__init__(pylammps_instance, index) - - @property - def position(self): - """Access to coordinates of an atom - - :getter: Return position of atom - :setter: Set position of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).position[0:2] - - @position.setter - def position(self, value): - current = self.position - current[:] = value - - @property - def velocity(self): - """Access to velocity of an atom - :getter: Return velocity of atom - :setter: Set velocity of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).velocity[0:2] - - @velocity.setter - def velocity(self, value): - current = self.velocity - current[:] = value - - @property - def force(self): - """Access to force of an atom - :getter: Return force of atom - :setter: Set force of atom - :type: numpy.array (float, float) - """ - return super(Atom2D, self).force[0:2] - - @force.setter - def force(self, value): - current = self.force - current[:] = value - -# ------------------------------------------------------------------------- - -class variable_set: - def __init__(self, name, variable_dict): - self._name = name - array_pattern = re.compile(r"(?P.+)\[(?P[0-9]+)\]") - - for key, value in variable_dict.items(): - m = array_pattern.match(key) - if m: - g = m.groupdict() - varname = g['arr'] - idx = int(g['index']) - if varname not in self.__dict__: - self.__dict__[varname] = {} - self.__dict__[varname][idx] = value - else: - self.__dict__[key] = value - - def __str__(self): - return "{}({})".format(self._name, ','.join(["{}={}".format(k, self.__dict__[k]) for k in self.__dict__.keys() if not k.startswith('_')])) - - def __dir__(self): - return [k for k in self.__dict__.keys() if not k.startswith('_')] - - def __repr__(self): - return self.__str__() - -# ------------------------------------------------------------------------- - -def get_thermo_data(output): - """ traverse output of runs and extract thermo data columns """ - if isinstance(output, str): - lines = output.splitlines() - else: - lines = output - - runs = [] - columns = [] - in_run = False - current_run = {} - - for line in lines: - if line.startswith("Per MPI rank memory allocation"): - in_run = True - elif in_run and len(columns) == 0: - # first line after memory usage are column names - columns = line.split() - - current_run = {} - - for col in columns: - current_run[col] = [] - - elif line.startswith("Loop time of "): - in_run = False - columns = [] - thermo_data = variable_set('ThermoData', current_run) - r = {'thermo' : thermo_data } - runs.append(namedtuple('Run', list(r.keys()))(*list(r.values()))) - elif in_run and len(columns) > 0: - items = line.split() - # Convert thermo output and store it. - # It must have the same number of columns and - # all of them must be convertible to floats. - # Otherwise we ignore the line - if len(items) == len(columns): - try: - values = [float(x) for x in items] - for i, col in enumerate(columns): - current_run[col].append(values[i]) - except ValueError: - # cannot convert. must be a non-thermo output. ignore. - pass - - return runs - -# ------------------------------------------------------------------------- -# ------------------------------------------------------------------------- - -class PyLammps(object): - """ - This is a Python wrapper class around the lower-level - :py:class:`lammps` class, exposing a more Python-like, - object-oriented interface for prototyping system inside of IPython and - Jupyter notebooks. - - It either creates its own instance of :py:class:`lammps` or can be - initialized with an existing instance. The arguments are the same of the - lower-level interface. The original interface can still be accessed via - :py:attr:`PyLammps.lmp`. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - :param verbose: print all LAMMPS output to stdout - :type verbose: bool - - :ivar lmp: instance of original LAMMPS Python interface - :vartype lmp: :py:class:`lammps` - - :ivar runs: list of completed runs, each storing the thermo output - :vartype run: list - """ - - def __init__(self, name="", cmdargs=None, ptr=None, comm=None, verbose=False): - self.has_echo = False - self.verbose = verbose - - if cmdargs: - if '-echo' in cmdargs: - idx = cmdargs.index('-echo') - # ensures that echo line is ignored during output capture - self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both') - - if ptr: - if isinstance(ptr,PyLammps): - self.lmp = ptr.lmp - elif isinstance(ptr,lammps): - self.lmp = ptr - else: - self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) - else: - self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm) - print("LAMMPS output is captured by PyLammps wrapper") - self._cmd_history = [] - self._enable_cmd_history = False - self.runs = [] - - def __enter__(self): - return self - - def __exit__(self, ex_type, ex_value, ex_traceback): - self.close() - - def __del__(self): - if self.lmp: self.lmp.close() - self.lmp = None - - def close(self): - """Explicitly delete a LAMMPS instance - - This is a wrapper around the :py:meth:`lammps.close` of the Python interface. - """ - if self.lmp: self.lmp.close() - self.lmp = None - - def version(self): - """Return a numerical representation of the LAMMPS version in use. - - This is a wrapper around the :py:meth:`lammps.version` function of the Python interface. - - :return: version number - :rtype: int - """ - return self.lmp.version() - - def file(self, file): - """Read LAMMPS commands from a file. - - This is a wrapper around the :py:meth:`lammps.file` function of the Python interface. - - :param path: Name of the file/path with LAMMPS commands - :type path: string - """ - self.lmp.file(file) - - @property - def enable_cmd_history(self): - """ - :getter: Return whether command history is saved - :setter: Set if command history should be saved - :type: bool - """ - return self._enable_cmd_history - - @enable_cmd_history.setter - def enable_cmd_history(self, value): - """ - :getter: Return whether command history is saved - :setter: Set if command history should be saved - :type: bool - """ - self._enable_cmd_history = (value == True) - - def write_script(self, filepath): - """ - Write LAMMPS script file containing all commands executed up until now - - :param filepath: path to script file that should be written - :type filepath: string - """ - with open(filepath, "w") as f: - for cmd in self._cmd_history: - print(cmd, file=f) - - def clear_cmd_history(self): - """ - Clear LAMMPS command history up to this point - """ - self._cmd_history = [] - - def command(self, cmd): - """ - Execute LAMMPS command - - If :py:attr:`PyLammps.enable_cmd_history` is set to ``True``, commands executed - will be recorded. The entire command history can be written to a file using - :py:meth:`PyLammps.write_script()`. To clear the command history, use - :py:meth:`PyLammps.clear_cmd_history()`. - - :param cmd: command string that should be executed - :type: cmd: string - """ - self.lmp.command(cmd) - - if self.enable_cmd_history: - self._cmd_history.append(cmd) - - def run(self, *args, **kwargs): - """ - Execute LAMMPS run command with given arguments - - All thermo output during the run is captured and saved as new entry in - :py:attr:`PyLammps.runs`. The latest run can be retrieved by - :py:attr:`PyLammps.last_run`. - """ - output = self.__getattr__('run')(*args, **kwargs) - self.runs += get_thermo_data(output) - return output - - @property - def last_run(self): - """ - Return data produced of last completed run command - - :getter: Returns an object containing information about the last run command - :type: dict - """ - if len(self.runs) > 0: - return self.runs[-1] - return None - - @property - def atoms(self): - """ - All atoms of this LAMMPS instance - - :getter: Returns a list of atoms currently in the system - :type: AtomList - """ - return AtomList(self) - - @property - def system(self): - """ - The system state of this LAMMPS instance - - :getter: Returns an object with properties storing the current system state - :type: namedtuple - """ - output = self.lmp_info("system") - output = output[output.index("System information:")+1:] - d = self._parse_info_system(output) - return namedtuple('System', d.keys())(*d.values()) - - @property - def communication(self): - """ - The communication state of this LAMMPS instance - - :getter: Returns an object with properties storing the current communication state - :type: namedtuple - """ - output = self.lmp_info("communication") - output = output[output.index("Communication information:")+1:] - d = self._parse_info_communication(output) - return namedtuple('Communication', d.keys())(*d.values()) - - @property - def computes(self): - """ - The list of active computes of this LAMMPS instance - - :getter: Returns a list of computes that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("computes") - output = output[output.index("Compute information:")+1:] - return self._parse_element_list(output) - - @property - def dumps(self): - """ - The list of active dumps of this LAMMPS instance - - :getter: Returns a list of dumps that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("dumps") - output = output[output.index("Dump information:")+1:] - return self._parse_element_list(output) - - @property - def fixes(self): - """ - The list of active fixes of this LAMMPS instance - - :getter: Returns a list of fixes that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("fixes") - output = output[output.index("Fix information:")+1:] - return self._parse_element_list(output) - - @property - def groups(self): - """ - The list of active atom groups of this LAMMPS instance - - :getter: Returns a list of atom groups that are currently active in this LAMMPS instance - :type: list - """ - output = self.lmp_info("groups") - output = output[output.index("Group information:")+1:] - return self._parse_groups(output) - - @property - def variables(self): - """ - Returns a dictionary of all variables defined in the current LAMMPS instance - - :getter: Returns a dictionary of all variables that are defined in this LAMMPS instance - :type: dict - """ - output = self.lmp_info("variables") - output = output[output.index("Variable information:")+1:] - variables = {} - for v in self._parse_element_list(output): - variables[v['name']] = Variable(self, v['name'], v['style'], v['def']) - return variables - - def eval(self, expr): - """ - Evaluate expression - - :param expr: the expression string that should be evaluated inside of LAMMPS - :type expr: string - - :return: the value of the evaluated expression - :rtype: float if numeric, string otherwise - """ - value = self.lmp_print('"$(%s)"' % expr).strip() - try: - return float(value) - except ValueError: - return value - - def _split_values(self, line): - return [x.strip() for x in line.split(',')] - - def _get_pair(self, value): - return [x.strip() for x in value.split('=')] - - def _parse_info_system(self, output): - system = {} - - for line in output: - if line.startswith("Units"): - system['units'] = self._get_pair(line)[1] - elif line.startswith("Atom style"): - system['atom_style'] = self._get_pair(line)[1] - elif line.startswith("Atom map"): - system['atom_map'] = self._get_pair(line)[1] - elif line.startswith("Atoms"): - parts = self._split_values(line) - system['natoms'] = int(self._get_pair(parts[0])[1]) - system['ntypes'] = int(self._get_pair(parts[1])[1]) - system['style'] = self._get_pair(parts[2])[1] - elif line.startswith("Kspace style"): - system['kspace_style'] = self._get_pair(line)[1] - elif line.startswith("Dimensions"): - system['dimensions'] = int(self._get_pair(line)[1]) - elif line.startswith("Orthogonal box"): - system['orthogonal_box'] = [float(x) for x in self._get_pair(line)[1].split('x')] - elif line.startswith("Boundaries"): - system['boundaries'] = self._get_pair(line)[1] - elif line.startswith("xlo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("ylo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("zlo"): - keys, values = [self._split_values(x) for x in self._get_pair(line)] - for key, value in zip(keys, values): - system[key] = float(value) - elif line.startswith("Molecule type"): - system['molecule_type'] = self._get_pair(line)[1] - elif line.startswith("Bonds"): - parts = self._split_values(line) - system['nbonds'] = int(self._get_pair(parts[0])[1]) - system['nbondtypes'] = int(self._get_pair(parts[1])[1]) - system['bond_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Angles"): - parts = self._split_values(line) - system['nangles'] = int(self._get_pair(parts[0])[1]) - system['nangletypes'] = int(self._get_pair(parts[1])[1]) - system['angle_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Dihedrals"): - parts = self._split_values(line) - system['ndihedrals'] = int(self._get_pair(parts[0])[1]) - system['ndihedraltypes'] = int(self._get_pair(parts[1])[1]) - system['dihedral_style'] = self._get_pair(parts[2])[1] - elif line.startswith("Impropers"): - parts = self._split_values(line) - system['nimpropers'] = int(self._get_pair(parts[0])[1]) - system['nimpropertypes'] = int(self._get_pair(parts[1])[1]) - system['improper_style'] = self._get_pair(parts[2])[1] - - return system - - def _parse_info_communication(self, output): - comm = {} - - for line in output: - if line.startswith("MPI library"): - comm['mpi_version'] = line.split(':')[1].strip() - elif line.startswith("Comm style"): - parts = self._split_values(line) - comm['comm_style'] = self._get_pair(parts[0])[1] - comm['comm_layout'] = self._get_pair(parts[1])[1] - elif line.startswith("Processor grid"): - comm['proc_grid'] = [int(x) for x in self._get_pair(line)[1].split('x')] - elif line.startswith("Communicate velocities for ghost atoms"): - comm['ghost_velocity'] = (self._get_pair(line)[1] == "yes") - elif line.startswith("Nprocs"): - parts = self._split_values(line) - comm['nprocs'] = int(self._get_pair(parts[0])[1]) - comm['nthreads'] = int(self._get_pair(parts[1])[1]) - return comm - - def _parse_element_list(self, output): - elements = [] - - for line in output: - if not line or (":" not in line): continue - element_info = self._split_values(line.split(':')[1].strip()) - element = {'name': element_info[0]} - for key, value in [self._get_pair(x) for x in element_info[1:]]: - element[key] = value - elements.append(element) - return elements - - def _parse_groups(self, output): - groups = [] - group_pattern = re.compile(r"(?P.+) \((?P.+)\)") - - for line in output: - m = group_pattern.match(line.split(':')[1].strip()) - group = {'name': m.group('name'), 'type': m.group('type')} - groups.append(group) - return groups - - def lmp_print(self, s): - """ needed for Python2 compatibility, since print is a reserved keyword """ - return self.__getattr__("print")(s) - - def __dir__(self): - return sorted(set(['angle_coeff', 'angle_style', 'atom_modify', 'atom_style', 'atom_style', - 'bond_coeff', 'bond_style', 'boundary', 'change_box', 'communicate', 'compute', - 'create_atoms', 'create_box', 'delete_atoms', 'delete_bonds', 'dielectric', - 'dihedral_coeff', 'dihedral_style', 'dimension', 'dump', 'fix', 'fix_modify', - 'group', 'improper_coeff', 'improper_style', 'include', 'kspace_modify', - 'kspace_style', 'lattice', 'mass', 'minimize', 'min_style', 'neighbor', - 'neigh_modify', 'newton', 'nthreads', 'pair_coeff', 'pair_modify', - 'pair_style', 'processors', 'read', 'read_data', 'read_restart', 'region', - 'replicate', 'reset_timestep', 'restart', 'run', 'run_style', 'thermo', - 'thermo_modify', 'thermo_style', 'timestep', 'undump', 'unfix', 'units', - 'variable', 'velocity', 'write_restart'] + self.lmp.available_styles("command"))) - - def lmp_info(self, s): - # skip anything before and after Info-Info-Info - # also skip timestamp line - output = self.__getattr__("info")(s) - indices = [index for index, line in enumerate(output) if line.startswith("Info-Info-Info-Info")] - start = indices[0] - end = indices[1] - return [line for line in output[start+2:end] if line] - - def __getattr__(self, name): - """ - This method is where the Python 'magic' happens. If a method is not - defined by the class PyLammps, it assumes it is a LAMMPS command. It takes - all the arguments, concatinates them to a single string, and executes it using - :py:meth:`lammps.PyLammps.command()`. - - :param verbose: Print output of command - :type verbose: bool - :return: line or list of lines of output, None if no output - :rtype: list or string - """ - def handler(*args, **kwargs): - cmd_args = [name] + [str(x) for x in args] - self.lmp.flush_buffers() - - with OutputCapture() as capture: - cmd = ' '.join(cmd_args) - self.command(cmd) - self.lmp.flush_buffers() - output = capture.output - - comm = self.lmp.get_mpi_comm() - if comm: - output = self.lmp.comm.bcast(output, root=0) - - if self.verbose or ('verbose' in kwargs and kwargs['verbose']): - print(output, end = '') - - lines = output.splitlines() - - if self.has_echo: - lines = lines[1:] - - if len(lines) > 1: - return lines - elif len(lines) == 1: - return lines[0] - return None - - return handler - - -class IPyLammps(PyLammps): - """ - IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps interface - - It either creates its own instance of :py:class:`lammps` or can be - initialized with an existing instance. The arguments are the same of the - lower-level interface. The original interface can still be accessed via - :py:attr:`PyLammps.lmp`. - - :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) - :type name: string - :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. - :type cmdargs: list - :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. - :type ptr: pointer - :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. - :type comm: MPI_Comm - """ - - def __init__(self,name="",cmdargs=None,ptr=None,comm=None): - super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) - - def image(self, filename="snapshot.png", group="all", color="type", diameter="type", - size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"): - """ Generate image using write_dump command and display it - - See :doc:`dump image ` for more information. - - :param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG - :type filename: string - :param group: the group of atoms write_image should use - :type group: string - :param color: name of property used to determine color - :type color: string - :param diameter: name of property used to determine atom diameter - :type diameter: string - :param size: dimensions of image - :type size: tuple (width, height) - :param view: view parameters - :type view: tuple (theta, phi) - :param center: center parameters - :type center: tuple (flag, center_x, center_y, center_z) - :param up: vector pointing to up direction - :type up: tuple (up_x, up_y, up_z) - :param zoom: zoom factor - :type zoom: float - :param background_color: background color of scene - :type background_color: string - - :return: Image instance used to display image in notebook - :rtype: :py:class:`IPython.core.display.Image` - """ - cmd_args = [group, "image", filename, color, diameter] - - if size is not None: - width = size[0] - height = size[1] - cmd_args += ["size", width, height] - - if view is not None: - theta = view[0] - phi = view[1] - cmd_args += ["view", theta, phi] - - if center is not None: - flag = center[0] - Cx = center[1] - Cy = center[2] - Cz = center[3] - cmd_args += ["center", flag, Cx, Cy, Cz] - - if up is not None: - Ux = up[0] - Uy = up[1] - Uz = up[2] - cmd_args += ["up", Ux, Uy, Uz] - - if zoom is not None: - cmd_args += ["zoom", zoom] - - cmd_args.append("modify backcolor " + background_color) - - self.write_dump(*cmd_args) - from IPython.core.display import Image - return Image(filename) - - def video(self, filename): - """ - Load video from file - - Can be used to visualize videos from :doc:`dump movie `. - - :param filename: Path to video file - :type filename: string - :return: HTML Video Tag used by notebook to embed a video - :rtype: :py:class:`IPython.display.HTML` - """ - from IPython.display import HTML - return HTML("") From b4fc400ed315adae49f890a4c01256f15ef96583 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:14:04 -0600 Subject: [PATCH 67/75] Add python/lammps back --- python/lammps/__init__.py | 49 + python/lammps/constants.py | 48 + python/lammps/core.py | 2097 +++++++++++++++++++++++++++++++ python/lammps/data.py | 92 ++ python/lammps/formats.py | 227 ++++ python/lammps/mliap/__init__.py | 20 + python/lammps/mliap/loader.py | 52 + python/lammps/mliap/pytorch.py | 326 +++++ python/lammps/numpy_wrapper.py | 483 +++++++ python/lammps/pylammps.py | 990 +++++++++++++++ 10 files changed, 4384 insertions(+) create mode 100644 python/lammps/__init__.py create mode 100644 python/lammps/constants.py create mode 100644 python/lammps/core.py create mode 100644 python/lammps/data.py create mode 100644 python/lammps/formats.py create mode 100644 python/lammps/mliap/__init__.py create mode 100644 python/lammps/mliap/loader.py create mode 100644 python/lammps/mliap/pytorch.py create mode 100644 python/lammps/numpy_wrapper.py create mode 100644 python/lammps/pylammps.py diff --git a/python/lammps/__init__.py b/python/lammps/__init__.py new file mode 100644 index 0000000000..fc35e45225 --- /dev/null +++ b/python/lammps/__init__.py @@ -0,0 +1,49 @@ +""" +LAMMPS module global members: + +.. data:: __version__ + + Numerical representation of the LAMMPS version this + module was taken from. Has the same format as the + result of :py:func:`lammps.version`. +""" + +from .constants import * # lgtm [py/polluting-import] +from .core import * # lgtm [py/polluting-import] +from .data import * # lgtm [py/polluting-import] +from .pylammps import * # lgtm [py/polluting-import] + +# convert installed module string version to numeric version +def get_version_number(): + import time + from os.path import join + from sys import version_info + + # must report 0 when inside LAMMPS source tree + if __file__.find(join('python', 'lammps', '__init__.py')) > 0: + return 0 + + vstring = None + if version_info.major == 3 and version_info.minor >= 8: + from importlib.metadata import version, PackageNotFoundError + try: + vstring = version('lammps') + except PackageNotFoundError: + # nothing to do, ignore + pass + + else: + from pkg_resources import get_distribution, DistributionNotFound + try: + vstring = get_distribution('lammps').version + except DistributionNotFound: + # nothing to do, ignore + pass + + if not vstring: + return 0 + + t = time.strptime(vstring, "%Y.%m.%d") + return t.tm_year*10000 + t.tm_mon*100 + t.tm_mday + +__version__ = get_version_number() diff --git a/python/lammps/constants.py b/python/lammps/constants.py new file mode 100644 index 0000000000..a50d58b28f --- /dev/null +++ b/python/lammps/constants.py @@ -0,0 +1,48 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +# various symbolic constants to be used +# in certain calls to select data formats +LAMMPS_AUTODETECT = None +LAMMPS_INT = 0 +LAMMPS_INT_2D = 1 +LAMMPS_DOUBLE = 2 +LAMMPS_DOUBLE_2D = 3 +LAMMPS_INT64 = 4 +LAMMPS_INT64_2D = 5 +LAMMPS_STRING = 6 + +# these must be kept in sync with the enums in library.h +LMP_STYLE_GLOBAL = 0 +LMP_STYLE_ATOM = 1 +LMP_STYLE_LOCAL = 2 + +LMP_TYPE_SCALAR = 0 +LMP_TYPE_VECTOR = 1 +LMP_TYPE_ARRAY = 2 +LMP_SIZE_VECTOR = 3 +LMP_SIZE_ROWS = 4 +LMP_SIZE_COLS = 5 + +LMP_VAR_EQUAL = 0 +LMP_VAR_ATOM = 1 + +# ------------------------------------------------------------------------- + +def get_ctypes_int(size): + from ctypes import c_int, c_int32, c_int64 + if size == 4: + return c_int32 + elif size == 8: + return c_int64 + return c_int diff --git a/python/lammps/core.py b/python/lammps/core.py new file mode 100644 index 0000000000..930a40a4b0 --- /dev/null +++ b/python/lammps/core.py @@ -0,0 +1,2097 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- +# Python wrapper for the LAMMPS library via ctypes + +# for python2/3 compatibility + +from __future__ import print_function + +import os +import sys +from ctypes import * # lgtm [py/polluting-import] +from os.path import dirname,abspath,join +from inspect import getsourcefile + +from .constants import * # lgtm [py/polluting-import] +from .data import * # lgtm [py/polluting-import] + +# ------------------------------------------------------------------------- + +class MPIAbortException(Exception): + def __init__(self, message): + self.message = message + + def __str__(self): + return repr(self.message) + +# ------------------------------------------------------------------------- + +class ExceptionCheck: + """Utility class to rethrow LAMMPS C++ exceptions as Python exceptions""" + def __init__(self, lmp): + self.lmp = lmp + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + if self.lmp.has_exceptions and self.lmp.lib.lammps_has_error(self.lmp.lmp): + raise self.lmp._lammps_exception + +# ------------------------------------------------------------------------- + +class lammps(object): + """Create an instance of the LAMMPS Python class. + + .. _mpi4py_docs: https://mpi4py.readthedocs.io/ + + This is a Python wrapper class that exposes the LAMMPS C-library + interface to Python. It either requires that LAMMPS has been compiled + as shared library which is then dynamically loaded via the ctypes + Python module or that this module called from a Python function that + is called from a Python interpreter embedded into a LAMMPS executable, + for example through the :doc:`python invoke ` command. + When the class is instantiated it calls the :cpp:func:`lammps_open` + function of the LAMMPS C-library interface, which in + turn will create an instance of the :cpp:class:`LAMMPS ` + C++ class. The handle to this C++ class is stored internally + and automatically passed to the calls to the C library interface. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + """ + + # ------------------------------------------------------------------------- + # create an instance of LAMMPS + + def __init__(self,name='',cmdargs=None,ptr=None,comm=None): + self.comm = comm + self.opened = 0 + + # determine module file location + + modpath = dirname(abspath(getsourcefile(lambda:0))) + # for windows installers the shared library is in a different folder + winpath = abspath(os.path.join(modpath,'..','..','bin')) + # allow override for running tests on Windows + if (os.environ.get("LAMMPSDLLPATH")): + winpath = os.environ.get("LAMMPSDLLPATH") + self.lib = None + self.lmp = None + + # if a pointer to a LAMMPS object is handed in + # when being called from a Python interpreter + # embedded into a LAMMPS executable, all library + # symbols should already be available so we do not + # load a shared object. + + try: + if ptr is not None: self.lib = CDLL("",RTLD_GLOBAL) + except OSError: + self.lib = None + + # load liblammps.so unless name is given + # if name = "g++", load liblammps_g++.so + # try loading the LAMMPS shared object from the location + # of the lammps package with an absolute path, + # so that LD_LIBRARY_PATH does not need to be set for regular install + # fall back to loading with a relative path, + # typically requires LD_LIBRARY_PATH to be set appropriately + # guess shared library extension based on OS, if not inferred from actual file + + if any([f.startswith('liblammps') and f.endswith('.dylib') + for f in os.listdir(modpath)]): + lib_ext = ".dylib" + elif any([f.startswith('liblammps') and f.endswith('.dll') + for f in os.listdir(modpath)]): + lib_ext = ".dll" + elif os.path.exists(winpath) and any([f.startswith('liblammps') and f.endswith('.dll') + for f in os.listdir(winpath)]): + lib_ext = ".dll" + modpath = winpath + elif any([f.startswith('liblammps') and f.endswith('.so') + for f in os.listdir(modpath)]): + lib_ext = ".so" + else: + import platform + if platform.system() == "Darwin": + lib_ext = ".dylib" + elif platform.system() == "Windows": + lib_ext = ".dll" + else: + lib_ext = ".so" + + if not self.lib: + if name: + libpath = join(modpath,"liblammps_%s" % name + lib_ext) + else: + libpath = join(modpath,"liblammps" + lib_ext) + if not os.path.isfile(libpath): + if name: + libpath = "liblammps_%s" % name + lib_ext + else: + libpath = "liblammps" + lib_ext + self.lib = CDLL(libpath,RTLD_GLOBAL) + + # declare all argument and return types for all library methods here. + # exceptions are where the arguments depend on certain conditions and + # then are defined where the functions are used. + self.lib.lammps_extract_setting.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_setting.restype = c_int + + # set default types + # needed in later declarations + self.c_bigint = get_ctypes_int(self.extract_setting("bigint")) + self.c_tagint = get_ctypes_int(self.extract_setting("tagint")) + self.c_imageint = get_ctypes_int(self.extract_setting("imageint")) + + self.lib.lammps_open.restype = c_void_p + self.lib.lammps_open_no_mpi.restype = c_void_p + self.lib.lammps_close.argtypes = [c_void_p] + self.lib.lammps_flush_buffers.argtypes = [c_void_p] + self.lib.lammps_free.argtypes = [c_void_p] + + self.lib.lammps_file.argtypes = [c_void_p, c_char_p] + self.lib.lammps_file.restype = None + + self.lib.lammps_command.argtypes = [c_void_p, c_char_p] + self.lib.lammps_command.restype = c_char_p + self.lib.lammps_commands_list.restype = None + self.lib.lammps_commands_string.argtypes = [c_void_p, c_char_p] + self.lib.lammps_commands_string.restype = None + + self.lib.lammps_get_natoms.argtypes = [c_void_p] + self.lib.lammps_get_natoms.restype = c_double + self.lib.lammps_extract_box.argtypes = \ + [c_void_p,POINTER(c_double),POINTER(c_double), + POINTER(c_double),POINTER(c_double),POINTER(c_double), + POINTER(c_int),POINTER(c_int)] + self.lib.lammps_extract_box.restype = None + + self.lib.lammps_reset_box.argtypes = \ + [c_void_p,POINTER(c_double),POINTER(c_double),c_double,c_double,c_double] + self.lib.lammps_reset_box.restype = None + + self.lib.lammps_gather_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_atoms.restype = None + + self.lib.lammps_gather_atoms_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_atoms_concat.restype = None + + self.lib.lammps_gather_atoms_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_gather_atoms_subset.restype = None + + self.lib.lammps_scatter_atoms.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_scatter_atoms.restype = None + + self.lib.lammps_scatter_atoms_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_scatter_atoms_subset.restype = None + + self.lib.lammps_gather_bonds.argtypes = [c_void_p,c_void_p] + self.lib.lammps_gather_bonds.restype = None + + self.lib.lammps_gather.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather.restype = None + + self.lib.lammps_gather_concat.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_gather_concat.restype = None + + self.lib.lammps_gather_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_gather_subset.restype = None + + self.lib.lammps_scatter.argtypes = [c_void_p,c_char_p,c_int,c_int,c_void_p] + self.lib.lammps_scatter.restype = None + + self.lib.lammps_scatter_subset.argtypes = \ + [c_void_p,c_char_p,c_int,c_int,c_int,POINTER(c_int),c_void_p] + self.lib.lammps_scatter_subset.restype = None + + + self.lib.lammps_find_pair_neighlist.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int] + self.lib.lammps_find_pair_neighlist.restype = c_int + + self.lib.lammps_find_fix_neighlist.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_find_fix_neighlist.restype = c_int + + self.lib.lammps_find_compute_neighlist.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_find_compute_neighlist.restype = c_int + + self.lib.lammps_neighlist_num_elements.argtypes = [c_void_p, c_int] + self.lib.lammps_neighlist_num_elements.restype = c_int + + self.lib.lammps_neighlist_element_neighbors.argtypes = \ + [c_void_p, c_int, c_int, POINTER(c_int), POINTER(c_int), POINTER(POINTER(c_int))] + self.lib.lammps_neighlist_element_neighbors.restype = None + + self.lib.lammps_is_running.argtypes = [c_void_p] + self.lib.lammps_is_running.restype = c_int + + self.lib.lammps_force_timeout.argtypes = [c_void_p] + + self.lib.lammps_has_error.argtypes = [c_void_p] + self.lib.lammps_has_error.restype = c_int + + self.lib.lammps_get_last_error_message.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_get_last_error_message.restype = c_int + + self.lib.lammps_extract_global.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_global_datatype.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_global_datatype.restype = c_int + self.lib.lammps_extract_compute.argtypes = [c_void_p, c_char_p, c_int, c_int] + + self.lib.lammps_get_thermo.argtypes = [c_void_p, c_char_p] + self.lib.lammps_get_thermo.restype = c_double + + self.lib.lammps_encode_image_flags.restype = self.c_imageint + + self.lib.lammps_config_package_name.argtypes = [c_int, c_char_p, c_int] + self.lib.lammps_config_accelerator.argtypes = [c_char_p, c_char_p, c_char_p] + + self.lib.lammps_set_variable.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_has_style.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_style_count.argtypes = [c_void_p, c_char_p] + + self.lib.lammps_style_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] + + self.lib.lammps_has_id.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_id_count.argtypes = [c_void_p, c_char_p] + + self.lib.lammps_id_name.argtypes = [c_void_p, c_char_p, c_int, c_char_p, c_int] + + self.lib.lammps_plugin_count.argtypes = [ ] + self.lib.lammps_plugin_name.argtypes = [c_int, c_char_p, c_char_p, c_int] + + self.lib.lammps_version.argtypes = [c_void_p] + + self.lib.lammps_get_os_info.argtypes = [c_char_p, c_int] + self.lib.lammps_get_gpu_device_info.argtypes = [c_char_p, c_int] + + self.lib.lammps_get_mpi_comm.argtypes = [c_void_p] + + self.lib.lammps_decode_image_flags.argtypes = [self.c_imageint, POINTER(c_int*3)] + + self.lib.lammps_extract_atom.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_atom_datatype.argtypes = [c_void_p, c_char_p] + self.lib.lammps_extract_atom_datatype.restype = c_int + + self.lib.lammps_extract_fix.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int, c_int] + + self.lib.lammps_extract_variable.argtypes = [c_void_p, c_char_p, c_char_p] + + self.lib.lammps_fix_external_get_force.argtypes = [c_void_p, c_char_p] + self.lib.lammps_fix_external_get_force.restype = POINTER(POINTER(c_double)) + + self.lib.lammps_fix_external_set_energy_global.argtypes = [c_void_p, c_char_p, c_double] + self.lib.lammps_fix_external_set_virial_global.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_energy_peratom.argtypes = [c_void_p, c_char_p, POINTER(c_double)] + self.lib.lammps_fix_external_set_virial_peratom.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_double))] + + self.lib.lammps_fix_external_set_vector_length.argtypes = [c_void_p, c_char_p, c_int] + self.lib.lammps_fix_external_set_vector.argtypes = [c_void_p, c_char_p, c_int, c_double] + + # detect if Python is using a version of mpi4py that can pass communicators + # only needed if LAMMPS has been compiled with MPI support. + self.has_mpi4py = False + if self.has_mpi_support: + try: + from mpi4py import __version__ as mpi4py_version + # tested to work with mpi4py versions 2 and 3 + self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3'] + except ImportError: + # ignore failing import + pass + + # if no ptr provided, create an instance of LAMMPS + # we can pass an MPI communicator from mpi4py v2.0.0 and later + # no_mpi call lets LAMMPS use MPI_COMM_WORLD + # cargs = array of C strings from args + # if ptr, then are embedding Python in LAMMPS input script + # ptr is the desired instance of LAMMPS + # just convert it to ctypes ptr and store in self.lmp + + if ptr is None: + + # with mpi4py v2+, we can pass MPI communicators to LAMMPS + # need to adjust for type of MPI communicator object + # allow for int (like MPICH) or void* (like OpenMPI) + if self.has_mpi_support and self.has_mpi4py: + from mpi4py import MPI + self.MPI = MPI + + if comm is not None: + if not self.has_mpi_support: + raise Exception('LAMMPS not compiled with real MPI library') + if not self.has_mpi4py: + raise Exception('Python mpi4py version is not 2 or 3') + if self.MPI._sizeof(self.MPI.Comm) == sizeof(c_int): + MPI_Comm = c_int + else: + MPI_Comm = c_void_p + + # Detect whether LAMMPS and mpi4py definitely use different MPI libs + if sizeof(MPI_Comm) != self.lib.lammps_config_has_mpi_support(): + raise Exception('Inconsistent MPI library in LAMMPS and mpi4py') + + narg = 0 + cargs = None + if cmdargs is not None: + cmdargs.insert(0,"lammps") + narg = len(cmdargs) + for i in range(narg): + if type(cmdargs[i]) is str: + cmdargs[i] = cmdargs[i].encode() + cargs = (c_char_p*narg)(*cmdargs) + self.lib.lammps_open.argtypes = [c_int, c_char_p*narg, MPI_Comm, c_void_p] + else: + self.lib.lammps_open.argtypes = [c_int, c_char_p, MPI_Comm, c_void_p] + + self.opened = 1 + comm_ptr = self.MPI._addressof(comm) + comm_val = MPI_Comm.from_address(comm_ptr) + self.lmp = c_void_p(self.lib.lammps_open(narg,cargs,comm_val,None)) + + else: + if self.has_mpi4py and self.has_mpi_support: + self.comm = self.MPI.COMM_WORLD + self.opened = 1 + if cmdargs is not None: + cmdargs.insert(0,"lammps") + narg = len(cmdargs) + for i in range(narg): + if type(cmdargs[i]) is str: + cmdargs[i] = cmdargs[i].encode() + cargs = (c_char_p*narg)(*cmdargs) + self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p*narg, c_void_p] + self.lmp = c_void_p(self.lib.lammps_open_no_mpi(narg,cargs,None)) + else: + self.lib.lammps_open_no_mpi.argtypes = [c_int, c_char_p, c_void_p] + self.lmp = c_void_p(self.lib.lammps_open_no_mpi(0,None,None)) + + else: + # magic to convert ptr to ctypes ptr + if sys.version_info >= (3, 0): + # Python 3 (uses PyCapsule API) + pythonapi.PyCapsule_GetPointer.restype = c_void_p + pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] + self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None)) + else: + # Python 2 (uses PyCObject API) + pythonapi.PyCObject_AsVoidPtr.restype = c_void_p + pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] + self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) + + # check if library initilialization failed + if not self.lmp: + raise(RuntimeError("Failed to initialize LAMMPS object")) + + # optional numpy support (lazy loading) + self._numpy = None + + self._installed_packages = None + self._available_styles = None + + # check if liblammps version matches the installed python module version + # but not for in-place usage, i.e. when the version is 0 + import lammps + if lammps.__version__ > 0 and lammps.__version__ != self.lib.lammps_version(self.lmp): + raise(AttributeError("LAMMPS Python module installed for LAMMPS version %d, but shared library is version %d" \ + % (lammps.__version__, self.lib.lammps_version(self.lmp)))) + + # add way to insert Python callback for fix external + self.callback = {} + self.FIX_EXTERNAL_CALLBACK_FUNC = CFUNCTYPE(None, py_object, self.c_bigint, c_int, POINTER(self.c_tagint), POINTER(POINTER(c_double)), POINTER(POINTER(c_double))) + self.lib.lammps_set_fix_external_callback.argtypes = [c_void_p, c_char_p, self.FIX_EXTERNAL_CALLBACK_FUNC, py_object] + self.lib.lammps_set_fix_external_callback.restype = None + + # ------------------------------------------------------------------------- + # shut-down LAMMPS instance + + def __del__(self): + self.close() + + # ------------------------------------------------------------------------- + # context manager implementation + + def __enter__(self): + return self + + def __exit__(self, ex_type, ex_value, ex_traceback): + self.close() + + # ------------------------------------------------------------------------- + + @property + def numpy(self): + """ Return object to access numpy versions of API + + It provides alternative implementations of API functions that + return numpy arrays instead of ctypes pointers. If numpy is not installed, + accessing this property will lead to an ImportError. + + :return: instance of numpy wrapper object + :rtype: numpy_wrapper + """ + if not self._numpy: + from .numpy_wrapper import numpy_wrapper + self._numpy = numpy_wrapper(self) + return self._numpy + + # ------------------------------------------------------------------------- + + def close(self): + """Explicitly delete a LAMMPS instance through the C-library interface. + + This is a wrapper around the :cpp:func:`lammps_close` function of the C-library interface. + """ + if self.lmp and self.opened: + self.lib.lammps_close(self.lmp) + self.lmp = None + self.opened = 0 + + # ------------------------------------------------------------------------- + + 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`. + + You cannot create or use any LAMMPS instances after this function is called + unless LAMMPS was compiled without MPI and without Kokkos support. + """ + self.close() + self.lib.lammps_kokkos_finalize() + self.lib.lammps_mpi_finalize() + + # ------------------------------------------------------------------------- + + def version(self): + """Return a numerical representation of the LAMMPS version in use. + + This is a wrapper around the :cpp:func:`lammps_version` function of the C-library interface. + + :return: version number + :rtype: int + """ + return self.lib.lammps_version(self.lmp) + + # ------------------------------------------------------------------------- + + def get_os_info(self): + """Return a string with information about the OS and compiler runtime + + This is a wrapper around the :cpp:func:`lammps_get_os_info` function of the C-library interface. + + :return: OS info string + :rtype: string + """ + + sb = create_string_buffer(512) + self.lib.lammps_get_os_info(sb,512) + return sb.value.decode() + + # ------------------------------------------------------------------------- + + def get_mpi_comm(self): + """Get the MPI communicator in use by the current LAMMPS instance + + This is a wrapper around the :cpp:func:`lammps_get_mpi_comm` function + of the C-library interface. It will return ``None`` if either the + LAMMPS library was compiled without MPI support or the mpi4py + Python module is not available. + + :return: MPI communicator + :rtype: MPI_Comm + """ + + if self.has_mpi4py and self.has_mpi_support: + from mpi4py import MPI + f_comm = self.lib.lammps_get_mpi_comm(self.lmp) + c_comm = MPI.Comm.f2py(f_comm) + return c_comm + else: + return None + + # ------------------------------------------------------------------------- + + @property + def _lammps_exception(self): + sb = create_string_buffer(100) + error_type = self.lib.lammps_get_last_error_message(self.lmp, sb, 100) + error_msg = sb.value.decode().strip() + + if error_type == 2: + return MPIAbortException(error_msg) + return Exception(error_msg) + + # ------------------------------------------------------------------------- + + def file(self, path): + """Read LAMMPS commands from a file. + + This is a wrapper around the :cpp:func:`lammps_file` function of the C-library interface. + It will open the file with the name/path `file` and process the LAMMPS commands line by line until + the end. The function will return when the end of the file is reached. + + :param path: Name of the file/path with LAMMPS commands + :type path: string + """ + if path: path = path.encode() + else: return + + with ExceptionCheck(self): + self.lib.lammps_file(self.lmp, path) + + # ------------------------------------------------------------------------- + + def command(self,cmd): + """Process a single LAMMPS input command from a string. + + This is a wrapper around the :cpp:func:`lammps_command` + function of the C-library interface. + + :param cmd: a single lammps command + :type cmd: string + """ + if cmd: cmd = cmd.encode() + else: return + + with ExceptionCheck(self): + self.lib.lammps_command(self.lmp,cmd) + + # ------------------------------------------------------------------------- + + def commands_list(self,cmdlist): + """Process multiple LAMMPS input commands from a list of strings. + + This is a wrapper around the + :cpp:func:`lammps_commands_list` function of + the C-library interface. + + :param cmdlist: a single lammps command + :type cmdlist: list of strings + """ + cmds = [x.encode() for x in cmdlist if type(x) is str] + narg = len(cmdlist) + args = (c_char_p * narg)(*cmds) + self.lib.lammps_commands_list.argtypes = [c_void_p, c_int, c_char_p * narg] + + with ExceptionCheck(self): + self.lib.lammps_commands_list(self.lmp,narg,args) + + # ------------------------------------------------------------------------- + + def commands_string(self,multicmd): + """Process a block of LAMMPS input commands from a string. + + This is a wrapper around the + :cpp:func:`lammps_commands_string` + function of the C-library interface. + + :param multicmd: text block of lammps commands + :type multicmd: string + """ + if type(multicmd) is str: multicmd = multicmd.encode() + + with ExceptionCheck(self): + self.lib.lammps_commands_string(self.lmp,c_char_p(multicmd)) + + # ------------------------------------------------------------------------- + + def get_natoms(self): + """Get the total number of atoms in the LAMMPS instance. + + Will be precise up to 53-bit signed integer due to the + underlying :cpp:func:`lammps_get_natoms` function returning a double. + + :return: number of atoms + :rtype: int + """ + return int(self.lib.lammps_get_natoms(self.lmp)) + + # ------------------------------------------------------------------------- + + def extract_box(self): + """Extract simulation box parameters + + This is a wrapper around the :cpp:func:`lammps_extract_box` function + of the C-library interface. Unlike in the C function, the result is + returned as a list. + + :return: list of the extracted data: boxlo, boxhi, xy, yz, xz, periodicity, box_change + :rtype: [ 3*double, 3*double, double, double, 3*int, int] + """ + boxlo = (3*c_double)() + boxhi = (3*c_double)() + xy = c_double() + yz = c_double() + xz = c_double() + periodicity = (3*c_int)() + box_change = c_int() + + with ExceptionCheck(self): + self.lib.lammps_extract_box(self.lmp,boxlo,boxhi, + byref(xy),byref(yz),byref(xz), + periodicity,byref(box_change)) + + boxlo = boxlo[:3] + boxhi = boxhi[:3] + xy = xy.value + yz = yz.value + xz = xz.value + periodicity = periodicity[:3] + box_change = box_change.value + + return boxlo,boxhi,xy,yz,xz,periodicity,box_change + + # ------------------------------------------------------------------------- + + def reset_box(self,boxlo,boxhi,xy,yz,xz): + """Reset simulation box parameters + + This is a wrapper around the :cpp:func:`lammps_reset_box` function + of the C-library interface. + + :param boxlo: new lower box boundaries + :type boxlo: list of 3 floating point numbers + :param boxhi: new upper box boundaries + :type boxhi: list of 3 floating point numbers + :param xy: xy tilt factor + :type xy: float + :param yz: yz tilt factor + :type yz: float + :param xz: xz tilt factor + :type xz: float + """ + cboxlo = (3*c_double)(*boxlo) + cboxhi = (3*c_double)(*boxhi) + with ExceptionCheck(self): + self.lib.lammps_reset_box(self.lmp,cboxlo,cboxhi,xy,yz,xz) + + # ------------------------------------------------------------------------- + + def get_thermo(self,name): + """Get current value of a thermo keyword + + This is a wrapper around the :cpp:func:`lammps_get_thermo` + function of the C-library interface. + + :param name: name of thermo keyword + :type name: string + :return: value of thermo keyword + :rtype: double or None + """ + if name: name = name.encode() + else: return None + + with ExceptionCheck(self): + return self.lib.lammps_get_thermo(self.lmp,name) + + # ------------------------------------------------------------------------- + + def extract_setting(self, name): + """Query LAMMPS about global settings that can be expressed as an integer. + + This is a wrapper around the :cpp:func:`lammps_extract_setting` + function of the C-library interface. Its documentation includes + a list of the supported keywords. + + :param name: name of the setting + :type name: string + :return: value of the setting + :rtype: int + """ + if name: name = name.encode() + else: return None + return int(self.lib.lammps_extract_setting(self.lmp,name)) + + # ------------------------------------------------------------------------- + # extract global info datatype + + def extract_global_datatype(self, name): + """Retrieve global property datatype from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_global_datatype` + function of the C-library interface. Its documentation includes a + list of the supported keywords. + This function returns ``None`` if the keyword is not + recognized. Otherwise it will return a positive integer value that + corresponds to one of the :ref:`data type ` + constants define in the :py:mod:`lammps` module. + + :param name: name of the property + :type name: string + :return: data type of global property, see :ref:`py_datatype_constants` + :rtype: int + """ + if name: name = name.encode() + else: return None + return self.lib.lammps_extract_global_datatype(self.lmp, name) + + # ------------------------------------------------------------------------- + # extract global info + + def extract_global(self, name, dtype=LAMMPS_AUTODETECT): + """Query LAMMPS about global settings of different types. + + This is a wrapper around the :cpp:func:`lammps_extract_global` function + of the C-library interface. Since there are no pointers in Python, this + method will - unlike the C function - return the value or a list of + values. The :cpp:func:`lammps_extract_global` documentation includes a + list of the supported keywords and their data types. + Since Python needs to know the data type to be able to interpret + the result, by default, this function will try to auto-detect the data type + by asking the library. You can also force a specific data type. For that + purpose the :py:mod:`lammps` module contains :ref:`data type ` + constants. This function returns ``None`` if either the keyword is not recognized, + or an invalid data type constant is used. + + :param name: name of the property + :type name: string + :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :return: value of the property or list of values or None + :rtype: int, float, list, or NoneType + """ + + if dtype == LAMMPS_AUTODETECT: + dtype = self.extract_global_datatype(name) + + # set length of vector for items that are not a scalar + vec_dict = { 'boxlo':3, 'boxhi':3, 'sublo':3, 'subhi':3, + 'sublo_lambda':3, 'subhi_lambda':3, 'periodicity':3 } + if name in vec_dict: + veclen = vec_dict[name] + elif name == 'respa_dt': + veclen = self.extract_global('respa_levels',LAMMPS_INT) + else: + veclen = 1 + + if name: name = name.encode() + else: return None + + if dtype == LAMMPS_INT: + self.lib.lammps_extract_global.restype = POINTER(c_int32) + target_type = int + elif dtype == LAMMPS_INT64: + self.lib.lammps_extract_global.restype = POINTER(c_int64) + target_type = int + elif dtype == LAMMPS_DOUBLE: + self.lib.lammps_extract_global.restype = POINTER(c_double) + target_type = float + elif dtype == LAMMPS_STRING: + self.lib.lammps_extract_global.restype = c_char_p + target_type = str + else: + target_type = None + + ptr = self.lib.lammps_extract_global(self.lmp, name) + if ptr: + if dtype == LAMMPS_STRING: + return ptr.decode('utf-8') + if veclen > 1: + result = [] + for i in range(0,veclen): + result.append(target_type(ptr[i])) + return result + else: return target_type(ptr[0]) + return None + + # ------------------------------------------------------------------------- + # extract per-atom info datatype + + def extract_atom_datatype(self, name): + """Retrieve per-atom property datatype from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_atom_datatype` + function of the C-library interface. Its documentation includes a + list of the supported keywords. + This function returns ``None`` if the keyword is not + recognized. Otherwise it will return an integer value that + corresponds to one of the :ref:`data type ` constants + defined in the :py:mod:`lammps` module. + + :param name: name of the property + :type name: string + :return: data type of per-atom property (see :ref:`py_datatype_constants`) + :rtype: int + """ + if name: name = name.encode() + else: return None + return self.lib.lammps_extract_atom_datatype(self.lmp, name) + + # ------------------------------------------------------------------------- + # extract per-atom info + + def extract_atom(self, name, dtype=LAMMPS_AUTODETECT): + """Retrieve per-atom properties from LAMMPS + + This is a wrapper around the :cpp:func:`lammps_extract_atom` + function of the C-library interface. Its documentation includes a + list of the supported keywords and their data types. + Since Python needs to know the data type to be able to interpret + the result, by default, this function will try to auto-detect the data type + by asking the library. You can also force a specific data type by setting ``dtype`` + to one of the :ref:`data type ` constants defined in the + :py:mod:`lammps` module. + This function returns ``None`` if either the keyword is not + recognized, or an invalid data type constant is used. + + .. note:: + + While the returned arrays of per-atom data are dimensioned + for the range [0:nmax] - as is the underlying storage - + the data is usually only valid for the range of [0:nlocal], + unless the property of interest is also updated for ghost + atoms. In some cases, this depends on a LAMMPS setting, see + for example :doc:`comm_modify vel yes `. + + :param name: name of the property + :type name: string + :param dtype: data type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :return: requested data or ``None`` + :rtype: ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.POINTER(ctypes.c_int32)), + ctypes.POINTER(ctypes.c_int64), ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), + ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + or NoneType + """ + if dtype == LAMMPS_AUTODETECT: + dtype = self.extract_atom_datatype(name) + + if name: name = name.encode() + else: return None + + if dtype == LAMMPS_INT: + self.lib.lammps_extract_atom.restype = POINTER(c_int32) + elif dtype == LAMMPS_INT_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int32)) + elif dtype == LAMMPS_DOUBLE: + self.lib.lammps_extract_atom.restype = POINTER(c_double) + elif dtype == LAMMPS_DOUBLE_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_double)) + elif dtype == LAMMPS_INT64: + self.lib.lammps_extract_atom.restype = POINTER(c_int64) + elif dtype == LAMMPS_INT64_2D: + self.lib.lammps_extract_atom.restype = POINTER(POINTER(c_int64)) + else: return None + + ptr = self.lib.lammps_extract_atom(self.lmp, name) + if ptr: return ptr + else: return None + + + # ------------------------------------------------------------------------- + + def extract_compute(self,cid,cstyle,ctype): + """Retrieve data from a LAMMPS compute + + This is a wrapper around the :cpp:func:`lammps_extract_compute` + function of the C-library interface. + This function returns ``None`` if either the compute id is not + recognized, or an invalid combination of :ref:`cstyle ` + and :ref:`ctype ` constants is used. The + names and functionality of the constants are the same as for + the corresponding C-library function. For requests to return + a scalar or a size, the value is returned, otherwise a pointer. + + :param cid: compute ID + :type cid: string + :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type cstyle: int + :param ctype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ctype: int + :return: requested data as scalar, pointer to 1d or 2d double array, or None + :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType + """ + if cid: cid = cid.encode() + else: return None + + if ctype == LMP_TYPE_SCALAR: + if cstyle == LMP_STYLE_GLOBAL: + self.lib.lammps_extract_compute.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + elif cstyle == LMP_STYLE_ATOM: + return None + elif cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + elif ctype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_compute.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr + + elif ctype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_compute.restype = POINTER(POINTER(c_double)) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr + + elif ctype == LMP_SIZE_COLS: + if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_ATOM or cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + elif ctype == LMP_SIZE_VECTOR or ctype == LMP_SIZE_ROWS: + if cstyle == LMP_STYLE_GLOBAL or cstyle == LMP_STYLE_LOCAL: + self.lib.lammps_extract_compute.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_compute(self.lmp,cid,cstyle,ctype) + return ptr[0] + + return None + + # ------------------------------------------------------------------------- + # extract fix info + # in case of global data, free memory for 1 double via lammps_free() + # double was allocated by library interface function + + def extract_fix(self,fid,fstyle,ftype,nrow=0,ncol=0): + """Retrieve data from a LAMMPS fix + + This is a wrapper around the :cpp:func:`lammps_extract_fix` + function of the C-library interface. + This function returns ``None`` if either the fix id is not + recognized, or an invalid combination of :ref:`fstyle ` + and :ref:`ftype ` constants is used. The + names and functionality of the constants are the same as for + the corresponding C-library function. For requests to return + a scalar or a size, the value is returned, also when accessing + global vectors or arrays, otherwise a pointer. + + :param fid: fix ID + :type fid: string + :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type fstyle: int + :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ftype: int + :param nrow: index of global vector element or row index of global array element + :type nrow: int + :param ncol: column index of global array element + :type ncol: int + :return: requested data or None + :rtype: c_double, ctypes.POINTER(c_double), ctypes.POINTER(ctypes.POINTER(c_double)), or NoneType + + """ + if fid: fid = fid.encode() + else: return None + + if fstyle == LMP_STYLE_GLOBAL: + if ftype in (LMP_TYPE_SCALAR, LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): + self.lib.lammps_extract_fix.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + result = ptr[0] + self.lib.lammps_free(ptr) + return result + elif ftype in (LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): + self.lib.lammps_extract_fix.restype = POINTER(c_int) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + return ptr[0] + else: + return None + + elif fstyle == LMP_STYLE_ATOM: + if ftype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_fix.restype = POINTER(c_double) + elif ftype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) + elif ftype == LMP_SIZE_COLS: + self.lib.lammps_extract_fix.restype = POINTER(c_int) + else: + return None + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + if ftype == LMP_SIZE_COLS: + return ptr[0] + else: + return ptr + + elif fstyle == LMP_STYLE_LOCAL: + if ftype == LMP_TYPE_VECTOR: + self.lib.lammps_extract_fix.restype = POINTER(c_double) + elif ftype == LMP_TYPE_ARRAY: + self.lib.lammps_extract_fix.restype = POINTER(POINTER(c_double)) + elif ftype in (LMP_TYPE_SCALAR, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS): + self.lib.lammps_extract_fix.restype = POINTER(c_int) + else: + return None + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_fix(self.lmp,fid,fstyle,ftype,nrow,ncol) + if ftype in (LMP_TYPE_VECTOR, LMP_TYPE_ARRAY): + return ptr + else: + return ptr[0] + else: + return None + + # ------------------------------------------------------------------------- + # extract variable info + # free memory for 1 double or 1 vector of doubles via lammps_free() + # for vector, must copy nlocal returned values to local c_double vector + # memory was allocated by library interface function + + def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): + """ Evaluate a LAMMPS variable and return its data + + This function is a wrapper around the function + :cpp:func:`lammps_extract_variable` of the C-library interface, + evaluates variable name and returns a copy of the computed data. + The memory temporarily allocated by the C-interface is deleted + after the data is copied to a Python variable or list. + The variable must be either an equal-style (or equivalent) + variable or an atom-style variable. The variable type has to + provided as ``vartype`` parameter which may be one of two constants: + ``LMP_VAR_EQUAL`` or ``LMP_VAR_ATOM``; it defaults to + equal-style variables. + The group parameter is only used for atom-style variables and + defaults to the group "all" if set to ``None``, which is the default. + + :param name: name of the variable to execute + :type name: string + :param group: name of group for atom-style variable + :type group: string, only for atom-style variables + :param vartype: type of variable, see :ref:`py_vartype_constants` + :type vartype: int + :return: the requested data + :rtype: c_double, (c_double), or NoneType + """ + if name: name = name.encode() + else: return None + if group: group = group.encode() + if vartype == LMP_VAR_EQUAL: + self.lib.lammps_extract_variable.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_variable(self.lmp,name,group) + if ptr: result = ptr[0] + else: return None + self.lib.lammps_free(ptr) + return result + elif vartype == LMP_VAR_ATOM: + nlocal = self.extract_global("nlocal") + result = (c_double*nlocal)() + self.lib.lammps_extract_variable.restype = POINTER(c_double) + with ExceptionCheck(self): + ptr = self.lib.lammps_extract_variable(self.lmp,name,group) + if ptr: + for i in range(nlocal): result[i] = ptr[i] + self.lib.lammps_free(ptr) + else: return None + return result + return None + + # ------------------------------------------------------------------------- + + def flush_buffers(self): + """Flush output buffers + + This is a wrapper around the :cpp:func:`lammps_flush_buffers` + function of the C-library interface. + """ + self.lib.lammps_flush_buffers(self.lmp) + + # ------------------------------------------------------------------------- + + def set_variable(self,name,value): + """Set a new value for a LAMMPS string style variable + + This is a wrapper around the :cpp:func:`lammps_set_variable` + function of the C-library interface. + + :param name: name of the variable + :type name: string + :param value: new variable value + :type value: any. will be converted to a string + :return: either 0 on success or -1 on failure + :rtype: int + """ + if name: name = name.encode() + else: return -1 + if value: value = str(value).encode() + else: return -1 + with ExceptionCheck(self): + return self.lib.lammps_set_variable(self.lmp,name,value) + + # ------------------------------------------------------------------------- + + # return vector of atom properties gathered across procs + # 3 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # dtype = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # returned data is a 1d vector - doc how it is ordered? + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def gather_atoms(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data) + else: + return None + return data + + # ------------------------------------------------------------------------- + + def gather_atoms_concat(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_atoms_subset(self,name,dtype,count,ndata,ids): + if name: name = name.encode() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*ndata)*c_int)() + self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + elif dtype == 1: + data = ((count*ndata)*c_double)() + self.lib.lammps_gather_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + else: + return None + return data + + # ------------------------------------------------------------------------- + + # scatter vector of atom properties across procs + # 2 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # assume data is of correct type and length, as created by gather_atoms() + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def scatter_atoms(self,name,dtype,count,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_atoms(self.lmp,name,dtype,count,data) + + # ------------------------------------------------------------------------- + + def scatter_atoms_subset(self,name,dtype,count,ndata,ids,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_atoms_subset(self.lmp,name,dtype,count,ndata,ids,data) + + + # ------------------------------------------------------------------------- + + def gather_bonds(self): + """Retrieve global list of bonds + + This is a wrapper around the :cpp:func:`lammps_gather_bonds` + function of the C-library interface. + + This function returns a tuple with the number of bonds and a + flat list of ctypes integer values with the bond type, bond atom1, + bond atom2 for each bond. + + .. versionadded:: 28Jul2021 + + :return: a tuple with the number of bonds and a list of c_int or c_long + :rtype: (int, 3*nbonds*c_tagint) + """ + nbonds = self.extract_global("nbonds") + with ExceptionCheck(self): + data = ((3*nbonds)*self.c_tagint)() + self.lib.lammps_gather_bonds(self.lmp,data) + return nbonds,data + + # ------------------------------------------------------------------------- + + # return vector of atom/compute/fix properties gathered across procs + # 3 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # returned data is a 1d vector - doc how it is ordered? + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + def gather(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_concat(self,name,dtype,count): + if name: name = name.encode() + natoms = self.get_natoms() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*natoms)*c_int)() + self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) + elif dtype == 1: + data = ((count*natoms)*c_double)() + self.lib.lammps_gather_concat(self.lmp,name,dtype,count,data) + else: + return None + return data + + def gather_subset(self,name,dtype,count,ndata,ids): + if name: name = name.encode() + with ExceptionCheck(self): + if dtype == 0: + data = ((count*ndata)*c_int)() + self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) + elif dtype == 1: + data = ((count*ndata)*c_double)() + self.lib.lammps_gather_subset(self.lmp,name,dtype,count,ndata,ids,data) + else: + return None + return data + + # scatter vector of atom/compute/fix properties across procs + # 2 variants to match src/library.cpp + # name = atom property recognized by LAMMPS in atom->extract() + # type = 0 for integer values, 1 for double values + # count = number of per-atom valus, 1 for type or charge, 3 for x or f + # assume data is of correct type and length, as created by gather_atoms() + # NOTE: need to insure are converting to/from correct Python type + # e.g. for Python list or NumPy or ctypes + + def scatter(self,name,dtype,count,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter(self.lmp,name,dtype,count,data) + + def scatter_subset(self,name,dtype,count,ndata,ids,data): + if name: name = name.encode() + with ExceptionCheck(self): + self.lib.lammps_scatter_subset(self.lmp,name,dtype,count,ndata,ids,data) + + # ------------------------------------------------------------------------- + + def encode_image_flags(self,ix,iy,iz): + """ convert 3 integers with image flags for x-, y-, and z-direction + into a single integer like it is used internally in LAMMPS + + This method is a wrapper around the :cpp:func:`lammps_encode_image_flags` + function of library interface. + + :param ix: x-direction image flag + :type ix: int + :param iy: y-direction image flag + :type iy: int + :param iz: z-direction image flag + :type iz: int + :return: encoded image flags + :rtype: lammps.c_imageint + """ + return self.lib.lammps_encode_image_flags(ix,iy,iz) + + # ------------------------------------------------------------------------- + + def decode_image_flags(self,image): + """ Convert encoded image flag integer into list of three regular integers. + + This method is a wrapper around the :cpp:func:`lammps_decode_image_flags` + function of library interface. + + :param image: encoded image flags + :type image: lammps.c_imageint + :return: list of three image flags in x-, y-, and z- direction + :rtype: list of 3 int + """ + + flags = (c_int*3)() + self.lib.lammps_decode_image_flags(image,byref(flags)) + + return [int(i) for i in flags] + + # ------------------------------------------------------------------------- + + # create N atoms on all procs + # N = global number of atoms + # id = ID of each atom (optional, can be None) + # type = type of each atom (1 to Ntypes) (required) + # x = coords of each atom as (N,3) array (required) + # v = velocity of each atom as (N,3) array (optional, can be None) + # NOTE: how could we insure are passing correct type to LAMMPS + # e.g. for Python list or NumPy, etc + # ditto for gather_atoms() above + + def create_atoms(self,n,id,type,x,v=None,image=None,shrinkexceed=False): + """ + Create N atoms from list of coordinates and properties + + This function is a wrapper around the :cpp:func:`lammps_create_atoms` + function of the C-library interface, and the behavior is similar except + that the *v*, *image*, and *shrinkexceed* arguments are optional and + default to *None*, *None*, and *False*, respectively. With *None* being + equivalent to a ``NULL`` pointer in C. + + The lists of coordinates, types, atom IDs, velocities, image flags can + be provided in any format that may be converted into the required + internal data types. Also the list may contain more than *N* entries, + but not fewer. In the latter case, the function will return without + attempting to create atoms. You may use the :py:func:`encode_image_flags + ` method to properly combine three integers + with image flags into a single integer. + + :param n: number of atoms for which data is provided + :type n: int + :param id: list of atom IDs with at least n elements or None + :type id: list of lammps.tagint + :param type: list of atom types + :type type: list of int + :param x: list of coordinates for x-, y-, and z (flat list of 3n entries) + :type x: list of float + :param v: list of velocities for x-, y-, and z (flat list of 3n entries) or None (optional) + :type v: list of float + :param image: list of encoded image flags (optional) + :type image: list of lammps.imageint + :param shrinkexceed: whether to expand shrink-wrap boundaries if atoms are outside the box (optional) + :type shrinkexceed: bool + :return: number of atoms created. 0 if insufficient or invalid data + :rtype: int + """ + if id is not None: + id_lmp = (self.c_tagint*n)() + try: + id_lmp[:] = id[0:n] + except ValueError: + return 0 + else: + id_lmp = None + + type_lmp = (c_int*n)() + try: + type_lmp[:] = type[0:n] + except ValueError: + return 0 + + three_n = 3*n + x_lmp = (c_double*three_n)() + try: + x_lmp[:] = x[0:three_n] + except ValueError: + return 0 + + if v is not None: + v_lmp = (c_double*(three_n))() + try: + v_lmp[:] = v[0:three_n] + except ValueError: + return 0 + else: + v_lmp = None + + if image is not None: + img_lmp = (self.c_imageint*n)() + try: + img_lmp[:] = image[0:n] + except ValueError: + return 0 + else: + img_lmp = None + + if shrinkexceed: + se_lmp = 1 + else: + se_lmp = 0 + + self.lib.lammps_create_atoms.argtypes = [c_void_p, c_int, POINTER(self.c_tagint*n), + POINTER(c_int*n), POINTER(c_double*three_n), + POINTER(c_double*three_n), + POINTER(self.c_imageint*n), c_int] + with ExceptionCheck(self): + return self.lib.lammps_create_atoms(self.lmp, n, id_lmp, type_lmp, x_lmp, v_lmp, img_lmp, se_lmp) + + # ------------------------------------------------------------------------- + + @property + def has_mpi_support(self): + """ Report whether the LAMMPS shared library was compiled with a + real MPI library or in serial. + + This is a wrapper around the :cpp:func:`lammps_config_has_mpi_support` + function of the library interface. + + :return: False when compiled with MPI STUBS, otherwise True + :rtype: bool + """ + return self.lib.lammps_config_has_mpi_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def is_running(self): + """ Report whether being called from a function during a run or a minimization + + Various LAMMPS commands must not be called during an ongoing + run or minimization. This property allows to check for that. + This is a wrapper around the :cpp:func:`lammps_is_running` + function of the library interface. + + .. versionadded:: 9Oct2020 + + :return: True when called during a run otherwise false + :rtype: bool + """ + return self.lib.lammps_is_running(self.lmp) == 1 + + # ------------------------------------------------------------------------- + + def force_timeout(self): + """ Trigger an immediate timeout, i.e. a "soft stop" of a run. + + This function allows to cleanly stop an ongoing run or minimization + at the next loop iteration. + This is a wrapper around the :cpp:func:`lammps_force_timeout` + function of the library interface. + + .. versionadded:: 9Oct2020 + """ + self.lib.lammps_force_timeout(self.lmp) + + # ------------------------------------------------------------------------- + + @property + def has_exceptions(self): + """ Report whether the LAMMPS shared library was compiled with C++ + exceptions handling enabled + + This is a wrapper around the :cpp:func:`lammps_config_has_exceptions` + function of the library interface. + + :return: state of C++ exception support + :rtype: bool + """ + return self.lib.lammps_config_has_exceptions() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_gzip_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for reading and writing compressed files through ``gzip``. + + This is a wrapper around the :cpp:func:`lammps_config_has_gzip_support` + function of the library interface. + + :return: state of gzip support + :rtype: bool + """ + return self.lib.lammps_config_has_gzip_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_png_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for writing images in PNG format. + + This is a wrapper around the :cpp:func:`lammps_config_has_png_support` + function of the library interface. + + :return: state of PNG support + :rtype: bool + """ + return self.lib.lammps_config_has_png_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_jpeg_support(self): + """ Report whether the LAMMPS shared library was compiled with support + for writing images in JPEG format. + + This is a wrapper around the :cpp:func:`lammps_config_has_jpeg_support` + function of the library interface. + + :return: state of JPEG support + :rtype: bool + """ + return self.lib.lammps_config_has_jpeg_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def has_ffmpeg_support(self): + """ State of support for writing movies with ``ffmpeg`` in the LAMMPS shared library + + This is a wrapper around the :cpp:func:`lammps_config_has_ffmpeg_support` + function of the library interface. + + :return: state of ffmpeg support + :rtype: bool + """ + return self.lib.lammps_config_has_ffmpeg_support() != 0 + + # ------------------------------------------------------------------------- + + @property + def accelerator_config(self): + """ Return table with available accelerator configuration settings. + + This is a wrapper around the :cpp:func:`lammps_config_accelerator` + function of the library interface which loops over all known packages + and categories and returns enabled features as a nested dictionary + with all enabled settings as list of strings. + + :return: nested dictionary with all known enabled settings as list of strings + :rtype: dictionary + """ + + result = {} + for p in ['GPU', 'KOKKOS', 'INTEL', 'OPENMP']: + result[p] = {} + c = 'api' + result[p][c] = [] + for s in ['cuda', 'hip', 'phi', 'pthreads', 'opencl', 'openmp', 'serial']: + if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): + result[p][c].append(s) + c = 'precision' + result[p][c] = [] + for s in ['double', 'mixed', 'single']: + if self.lib.lammps_config_accelerator(p.encode(),c.encode(),s.encode()): + result[p][c].append(s) + return result + + # ------------------------------------------------------------------------- + + @property + def has_gpu_device(self): + """ Availability of GPU package compatible device + + This is a wrapper around the :cpp:func:`lammps_has_gpu_device` + function of the C library interface. + + :return: True if a GPU package compatible device is present, otherwise False + :rtype: bool + """ + return self.lib.lammps_has_gpu_device() != 0 + + # ------------------------------------------------------------------------- + + def get_gpu_device_info(self): + """Return a string with detailed information about any devices that are + usable by the GPU package. + + This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` + function of the C-library interface. + + :return: GPU device info string + :rtype: string + """ + + sb = create_string_buffer(8192) + self.lib.lammps_get_gpu_device_info(sb,8192) + return sb.value.decode() + + # ------------------------------------------------------------------------- + + @property + def installed_packages(self): + """ List of the names of enabled packages in the LAMMPS shared library + + This is a wrapper around the functions :cpp:func:`lammps_config_package_count` + and :cpp:func`lammps_config_package_name` of the library interface. + + :return + """ + if self._installed_packages is None: + self._installed_packages = [] + npackages = self.lib.lammps_config_package_count() + sb = create_string_buffer(100) + for idx in range(npackages): + self.lib.lammps_config_package_name(idx, sb, 100) + self._installed_packages.append(sb.value.decode()) + return self._installed_packages + + # ------------------------------------------------------------------------- + + def has_style(self, category, name): + """Returns whether a given style name is available in a given category + + This is a wrapper around the function :cpp:func:`lammps_has_style` + of the library interface. + + :param category: name of category + :type category: string + :param name: name of the style + :type name: string + + :return: true if style is available in given category + :rtype: bool + """ + return self.lib.lammps_has_style(self.lmp, category.encode(), name.encode()) != 0 + + # ------------------------------------------------------------------------- + + def available_styles(self, category): + """Returns a list of styles available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_style_count()` + and :cpp:func:`lammps_style_name()` of the library interface. + + :param category: name of category + :type category: string + + :return: list of style names in given category + :rtype: list + """ + if self._available_styles is None: + self._available_styles = {} + + if category not in self._available_styles: + self._available_styles[category] = [] + with ExceptionCheck(self): + nstyles = self.lib.lammps_style_count(self.lmp, category.encode()) + sb = create_string_buffer(100) + for idx in range(nstyles): + with ExceptionCheck(self): + self.lib.lammps_style_name(self.lmp, category.encode(), idx, sb, 100) + self._available_styles[category].append(sb.value.decode()) + return self._available_styles[category] + + # ------------------------------------------------------------------------- + + def has_id(self, category, name): + """Returns whether a given ID name is available in a given category + + This is a wrapper around the function :cpp:func:`lammps_has_id` + of the library interface. + + .. versionadded:: 9Oct2020 + + :param category: name of category + :type category: string + :param name: name of the ID + :type name: string + + :return: true if ID is available in given category + :rtype: bool + """ + return self.lib.lammps_has_id(self.lmp, category.encode(), name.encode()) != 0 + + # ------------------------------------------------------------------------- + + def available_ids(self, category): + """Returns a list of IDs available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_id_count()` + and :cpp:func:`lammps_id_name()` of the library interface. + + .. versionadded:: 9Oct2020 + + :param category: name of category + :type category: string + + :return: list of id names in given category + :rtype: list + """ + + categories = ['compute','dump','fix','group','molecule','region','variable'] + available_ids = [] + if category in categories: + num = self.lib.lammps_id_count(self.lmp, category.encode()) + sb = create_string_buffer(100) + for idx in range(num): + self.lib.lammps_id_name(self.lmp, category.encode(), idx, sb, 100) + available_ids.append(sb.value.decode()) + return available_ids + + # ------------------------------------------------------------------------- + + def available_plugins(self, category): + """Returns a list of plugins available for a given category + + This is a wrapper around the functions :cpp:func:`lammps_plugin_count()` + and :cpp:func:`lammps_plugin_name()` of the library interface. + + .. versionadded:: 10Mar2021 + + :return: list of style/name pairs of loaded plugins + :rtype: list + """ + + available_plugins = [] + num = self.lib.lammps_plugin_count(self.lmp) + sty = create_string_buffer(100) + nam = create_string_buffer(100) + for idx in range(num): + self.lib.lammps_plugin_name(idx, sty, nam, 100) + available_plugins.append([sty.value.decode(), nam.value.decode()]) + return available_plugins + + # ------------------------------------------------------------------------- + + def set_fix_external_callback(self, fix_id, callback, caller=None): + """Set the callback function for a fix external instance with a given fix ID. + + Optionally also set a reference to the calling object. + + This is a wrapper around the :cpp:func:`lammps_set_fix_external_callback` function + of the C-library interface. However this is set up to call a Python function with + the following arguments. + + .. code-block: python + + def func(object, ntimestep, nlocal, tag, x, f): + + - object is the value of the "caller" argument + - ntimestep is the current timestep + - nlocal is the number of local atoms on the current MPI process + - tag is a 1d NumPy array of integers representing the atom IDs of the local atoms + - x is a 2d NumPy array of doubles of the coordinates of the local atoms + - f is a 2d NumPy array of doubles of the forces on the local atoms that will be added + + .. versionchanged:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param callback: Python function that will be called from fix external + :type: function + :param caller: reference to some object passed to the callback function + :type: object, optional + """ + import numpy as np + + def callback_wrapper(caller, ntimestep, nlocal, tag_ptr, x_ptr, fext_ptr): + tag = self.numpy.iarray(self.c_tagint, tag_ptr, nlocal, 1) + x = self.numpy.darray(x_ptr, nlocal, 3) + f = self.numpy.darray(fext_ptr, nlocal, 3) + callback(caller, ntimestep, nlocal, tag, x, f) + + cFunc = self.FIX_EXTERNAL_CALLBACK_FUNC(callback_wrapper) + cCaller = caller + + self.callback[fix_id] = { 'function': cFunc, 'caller': caller } + with ExceptionCheck(self): + self.lib.lammps_set_fix_external_callback(self.lmp, fix_id.encode(), cFunc, cCaller) + + # ------------------------------------------------------------------------- + + def fix_external_get_force(self, fix_id): + """Get access to the array with per-atom forces of a fix external instance with a given fix ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_get_force` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: ctypes.POINTER(ctypes.POINTER(ctypes.double)) + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_get_force(self.lmp, fix_id.encode()) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_global(self, fix_id, eng): + """Set the global energy contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_global` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: potential energy value to be added by fix external + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_global(self.lmp, fix_id.encode(), eng) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_global(self, fix_id, virial): + """Set the global virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_global` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eng: list of 6 floating point numbers with the virial to be added by fix external + :type: float + """ + + cvirial = (6*c_double)(*virial) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_global(self.lmp, fix_id.encode(), cvirial) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the per-atom energy contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_energy_peratom` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: list of potential energy values for local atoms to be added by fix external + :type: float + """ + + nlocal = self.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy list length must be at least nlocal') + ceatom = (nlocal*c_double)(*eatom) + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_energy_peratom(self.lmp, fix_id.encode(), ceatom) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_virial_peratom` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param vatom: list of natoms lists with 6 floating point numbers to be added by fix external + :type: float + """ + + # copy virial data to C compatible buffer + nlocal = self.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') + vbuf = (c_double * 6) + vptr = POINTER(c_double) + c_virial = (vptr * nlocal)() + for i in range(nlocal): + c_virial[i] = vbuf() + for j in range(6): + c_virial[i][j] = vatom[i][j] + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_virial_peratom(self.lmp, fix_id.encode(), c_virial) + + # ------------------------------------------------------------------------- + def fix_external_set_vector_length(self, fix_id, length): + """Set the vector length for a global vector stored with fix external for analysis + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector_length` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param length: length of the global vector + :type: int + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector_length(self.lmp, fix_id.encode(), length) + + # ------------------------------------------------------------------------- + def fix_external_set_vector(self, fix_id, idx, val): + """Store a global vector value for a fix external instance with the given ID. + + This is a wrapper around the :cpp:func:`lammps_fix_external_set_vector` function + of the C-library interface. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param idx: 1-based index of the value in the global vector + :type: int + :param val: value to be stored in the global vector + :type: float + """ + + with ExceptionCheck(self): + return self.lib.lammps_fix_external_set_vector(self.lmp, fix_id.encode(), idx, val) + + # ------------------------------------------------------------------------- + + def get_neighlist(self, idx): + """Returns an instance of :class:`NeighList` which wraps access to the neighbor list with the given index + + See :py:meth:`lammps.numpy.get_neighlist() ` if you want to use + NumPy arrays instead of ``c_int`` pointers. + + :param idx: index of neighbor list + :type idx: int + :return: an instance of :class:`NeighList` wrapping access to neighbor list data + :rtype: NeighList + """ + if idx < 0: + return None + return NeighList(self, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_size(self, idx): + """Return the number of elements in neighbor list with the given index + + :param idx: neighbor list index + :type idx: int + :return: number of elements in neighbor list with index idx + :rtype: int + """ + return self.lib.lammps_neighlist_num_elements(self.lmp, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_element_neighbors(self, idx, element): + """Return data of neighbor list entry + + :param element: neighbor list index + :type element: int + :param element: neighbor list element index + :type element: int + :return: tuple with atom local index, number of neighbors and array of neighbor local atom indices + :rtype: (int, int, POINTER(c_int)) + """ + c_iatom = c_int() + c_numneigh = c_int() + c_neighbors = POINTER(c_int)() + self.lib.lammps_neighlist_element_neighbors(self.lmp, idx, element, byref(c_iatom), byref(c_numneigh), byref(c_neighbors)) + return c_iatom.value, c_numneigh.value, c_neighbors + + # ------------------------------------------------------------------------- + + def find_pair_neighlist(self, style, exact=True, nsub=0, reqid=0): + """Find neighbor list index of pair style neighbor list + + Search for a neighbor list requested by a pair style instance that + matches "style". If exact is True, the pair style name must match + exactly. If exact is False, the pair style name is matched against + "style" as regular expression or sub-string. If the pair style is a + hybrid pair style, the style is instead matched against the hybrid + sub-styles. If the same pair style is used as sub-style multiple + types, you must set nsub to a value n > 0 which indicates the nth + instance of that sub-style to be used (same as for the pair_coeff + command). The default value of 0 will fail to match in that case. + + Once the pair style instance has been identified, it may have + requested multiple neighbor lists. Those are uniquely identified by + a request ID > 0 as set by the pair style. Otherwise the request + ID is 0. + + :param style: name of pair style that should be searched for + :type style: string + :param exact: controls whether style should match exactly or only must be contained in pair style name, defaults to True + :type exact: bool, optional + :param nsub: match nsub-th hybrid sub-style, defaults to 0 + :type nsub: int, optional + :param reqid: list request id, > 0 in case there are more than one, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + style = style.encode() + exact = int(exact) + idx = self.lib.lammps_find_pair_neighlist(self.lmp, style, exact, nsub, reqid) + return idx + + # ------------------------------------------------------------------------- + + def find_fix_neighlist(self, fixid, reqid=0): + """Find neighbor list index of fix neighbor list + + The fix instance requesting the neighbor list is uniquely identified + by the fix ID. In case the fix has requested multiple neighbor + lists, those are uniquely identified by a request ID > 0 as set by + the fix. Otherwise the request ID is 0 (the default). + + :param fixid: name of fix + :type fixid: string + :param reqid: id of neighbor list request, in case there are more than one request, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + fixid = fixid.encode() + idx = self.lib.lammps_find_fix_neighlist(self.lmp, fixid, reqid) + return idx + + # ------------------------------------------------------------------------- + + def find_compute_neighlist(self, computeid, reqid=0): + """Find neighbor list index of compute neighbor list + + The compute instance requesting the neighbor list is uniquely + identified by the compute ID. In case the compute has requested + multiple neighbor lists, those are uniquely identified by a request + ID > 0 as set by the compute. Otherwise the request ID is 0 (the + default). + + :param computeid: name of compute + :type computeid: string + :param reqid: index of neighbor list request, in case there are more than one request, defaults to 0 + :type reqid: int, optional + :return: neighbor list index if found, otherwise -1 + :rtype: int + + """ + computeid = computeid.encode() + idx = self.lib.lammps_find_compute_neighlist(self.lmp, computeid, reqid) + return idx diff --git a/python/lammps/data.py b/python/lammps/data.py new file mode 100644 index 0000000000..731a8c514a --- /dev/null +++ b/python/lammps/data.py @@ -0,0 +1,92 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# LAMMPS data structures +# Written by Richard Berger +################################################################################ + +class NeighList(object): + """This is a wrapper class that exposes the contents of a neighbor list. + + It can be used like a regular Python list. Each element is a tuple of: + + * the atom local index + * its number of neighbors + * and a pointer to an c_int array containing local atom indices of its + neighbors + + Internally it uses the lower-level LAMMPS C-library interface. + + :param lmp: reference to instance of :py:class:`lammps` + :type lmp: lammps + :param idx: neighbor list index + :type idx: int + """ + def __init__(self, lmp, idx): + self.lmp = lmp + self.idx = idx + + def __str__(self): + return "Neighbor List ({} atoms)".format(self.size) + + def __repr__(self): + return self.__str__() + + @property + def size(self): + """ + :return: number of elements in neighbor list + """ + return self.lmp.get_neighlist_size(self.idx) + + def get(self, element): + """ + Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list + + :return: tuple with atom local index, number of neighbors and ctypes pointer to neighbor's local atom indices + :rtype: (int, int, ctypes.POINTER(c_int)) + """ + iatom, numneigh, neighbors = self.lmp.get_neighlist_element_neighbors(self.idx, element) + return iatom, numneigh, neighbors + + # the methods below implement the iterator interface, so NeighList can be used like a regular Python list + + def __getitem__(self, element): + return self.get(element) + + def __len__(self): + return self.size + + def __iter__(self): + inum = self.size + + for ii in range(inum): + yield self.get(ii) + + def find(self, iatom): + """ + Find the neighbor list for a specific (local) atom iatom. + If there is no list for iatom, (-1, None) is returned. + + :return: tuple with number of neighbors and ctypes pointer to neighbor's local atom indices + :rtype: (int, ctypes.POINTER(c_int)) + """ + + inum = self.size + for ii in range(inum): + idx, numneigh, neighbors = self.get(ii) + if idx == iatom: + return numneigh, neighbors + + return -1, None diff --git a/python/lammps/formats.py b/python/lammps/formats.py new file mode 100644 index 0000000000..b7c267466f --- /dev/null +++ b/python/lammps/formats.py @@ -0,0 +1,227 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# LAMMPS output formats +# Written by Richard Berger +# and Axel Kohlmeyer +################################################################################ + +import re + +has_yaml = False +try: + import yaml + has_yaml = True + try: + from yaml import CSafeLoader as Loader + except ImportError: + from yaml import SafeLoader as Loader +except ImportError: + # ignore here, raise an exception when trying to parse yaml instead + pass + +class LogFile: + """Reads LAMMPS log files and extracts the thermo information + + It supports the line, multi, and yaml thermo output styles. + + :param filename: path to log file + :type filename: str + + :ivar runs: List of LAMMPS runs in log file. Each run is a dictionary with + thermo fields as keys, storing the values over time + :ivar errors: List of error lines in log file + """ + + STYLE_DEFAULT = 0 + STYLE_MULTI = 1 + STYLE_YAML = 2 + + def __init__(self, filename): + alpha = re.compile(r'[a-df-zA-DF-Z]') # except e or E for floating-point numbers + kvpairs = re.compile(r'([a-zA-Z_0-9]+)\s+=\s*([0-9\.eE\-]+)') + style = LogFile.STYLE_DEFAULT + yamllog = "" + self.runs = [] + self.errors = [] + with open(filename, 'rt') as f: + in_thermo = False + in_data_section = False + for line in f: + if "ERROR" in line or "exited on signal" in line: + self.errors.append(line) + + elif re.match(r'^ *Step ', line): + in_thermo = True + in_data_section = True + keys = line.split() + current_run = {} + for k in keys: + current_run[k] = [] + + elif re.match(r'^(keywords:.*$|data:$|---$| - \[.*\]$)', line): + if not has_yaml: + raise Exception('Cannot process YAML format logs without the PyYAML Python module') + style = LogFile.STYLE_YAML + yamllog += line; + current_run = {} + + elif re.match(r'^\.\.\.$', line): + thermo = yaml.load(yamllog, Loader=Loader) + for k in thermo['keywords']: + current_run[k] = [] + for step in thermo['data']: + icol = 0 + for k in thermo['keywords']: + current_run[k].append(step[icol]) + icol += 1 + self.runs.append(current_run) + yamllog = "" + + elif re.match(r'^------* Step ', line): + if not in_thermo: + current_run = {'Step': [], 'CPU': []} + in_thermo = True + in_data_section = True + style = LogFile.STYLE_MULTI + str_step, str_cpu = line.strip('-\n').split('-----') + step = float(str_step.split()[1]) + cpu = float(str_cpu.split('=')[1].split()[0]) + current_run["Step"].append(step) + current_run["CPU"].append(cpu) + + elif line.startswith('Loop time of'): + in_thermo = False + if style != LogFile.STYLE_YAML: + self.runs.append(current_run) + + elif in_thermo and in_data_section: + if style == LogFile.STYLE_DEFAULT: + if alpha.search(line): + continue + for k, v in zip(keys, map(float, line.split())): + current_run[k].append(v) + + elif style == LogFile.STYLE_MULTI: + if '=' not in line: + in_data_section = False + continue + for k,v in kvpairs.findall(line): + if k not in current_run: + current_run[k] = [float(v)] + else: + current_run[k].append(float(v)) + +class AvgChunkFile: + """Reads files generated by fix ave/chunk + + :param filename: path to ave/chunk file + :type filename: str + + :ivar timesteps: List of timesteps stored in file + :ivar total_count: total count over time + :ivar chunks: List of chunks. Each chunk is a dictionary containing its ID, the coordinates, and the averaged quantities + """ + def __init__(self, filename): + with open(filename, 'rt') as f: + timestep = None + chunks_read = 0 + + self.timesteps = [] + self.total_count = [] + self.chunks = [] + + for lineno, line in enumerate(f): + if lineno == 0: + if not line.startswith("# Chunk-averaged data for fix"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + parts = line.split() + self.fix_name = parts[5] + self.group_name = parts[8] + continue + elif lineno == 1: + if not line.startswith("# Timestep Number-of-chunks Total-count"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + continue + elif lineno == 2: + if not line.startswith("#"): + raise Exception("Chunk data reader only supports default avg/chunk headers!") + columns = line.split()[1:] + ndim = line.count("Coord") + compress = 'OrigID' in line + if ndim > 0: + coord_start = columns.index("Coord1") + coord_end = columns.index("Coord%d" % ndim) + ncount_start = coord_end + 1 + data_start = ncount_start + 1 + else: + coord_start = None + coord_end = None + ncount_start = 2 + data_start = 3 + continue + + parts = line.split() + + if timestep is None: + timestep = int(parts[0]) + num_chunks = int(parts[1]) + total_count = float(parts[2]) + + self.timesteps.append(timestep) + self.total_count.append(total_count) + + for i in range(num_chunks): + self.chunks.append({ + 'coord' : [], + 'ncount' : [] + }) + elif chunks_read < num_chunks: + chunk = int(parts[0]) + ncount = float(parts[ncount_start]) + + if compress: + chunk_id = int(parts[1]) + else: + chunk_id = chunk + + current = self.chunks[chunk_id - 1] + current['id'] = chunk_id + current['ncount'].append(ncount) + + if ndim > 0: + coord = tuple(map(float, parts[coord_start:coord_end+1])) + current['coord'].append(coord) + + for i, data_column in list(enumerate(columns))[data_start:]: + value = float(parts[i]) + + if data_column in current: + current[data_column].append(value) + else: + current[data_column] = [value] + + chunks_read += 1 + assert chunk == chunks_read + else: + # do not support changing number of chunks + if not (num_chunks == int(parts[1])): + raise Exception("Currently, changing numbers of chunks are not supported.") + + timestep = int(parts[0]) + total_count = float(parts[2]) + chunks_read = 0 + + self.timesteps.append(timestep) + self.total_count.append(total_count) diff --git a/python/lammps/mliap/__init__.py b/python/lammps/mliap/__init__.py new file mode 100644 index 0000000000..57fe97d803 --- /dev/null +++ b/python/lammps/mliap/__init__.py @@ -0,0 +1,20 @@ + +# Check compatiblity of this build with the python shared library. +# If this fails, lammps will segfault because its library will +# try to improperly start up a new interpreter. +import sysconfig +import ctypes +library = sysconfig.get_config_vars('INSTSONAME')[0] +try: + pylib = ctypes.CDLL(library) +except OSError as e: + if pylib.endswith(".a"): + pylib.strip(".a") + ".so" + pylib = ctypes.CDLL(library) + else: + raise e +if not pylib.Py_IsInitialized(): + raise RuntimeError("This interpreter is not compatible with python-based mliap for LAMMPS.") +del sysconfig, ctypes, library, pylib + +from .loader import load_model, activate_mliappy diff --git a/python/lammps/mliap/loader.py b/python/lammps/mliap/loader.py new file mode 100644 index 0000000000..dff791bfc1 --- /dev/null +++ b/python/lammps/mliap/loader.py @@ -0,0 +1,52 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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: Nicholas Lubbers (LANL) +# ------------------------------------------------------------------------- + + +import sys +import importlib.util +import importlib.machinery + +def activate_mliappy(lmp): + try: + # Begin Importlib magic to find the embedded python module + # This is needed because the filename for liblammps does not + # match the spec for normal python modules, wherein + # file names match with PyInit function names. + # Also, python normally doesn't look for extensions besides '.so' + # We fix both of these problems by providing an explict + # path to the extension module 'mliap_model_python_couple' in + + path = lmp.lib._name + loader = importlib.machinery.ExtensionFileLoader('mliap_model_python_couple', path) + spec = importlib.util.spec_from_loader('mliap_model_python_couple', loader) + module = importlib.util.module_from_spec(spec) + sys.modules['mliap_model_python_couple'] = module + spec.loader.exec_module(module) + # End Importlib magic to find the embedded python module + + except Exception as ee: + raise ImportError("Could not load ML-IAP python coupling module.") from ee + +def load_model(model): + try: + import mliap_model_python_couple + except ImportError as ie: + raise ImportError("ML-IAP python module must be activated before loading\n" + "the pair style. Call lammps.mliap.activate_mliappy(lmp)." + ) from ie + mliap_model_python_couple.load_from_python(model) + diff --git a/python/lammps/mliap/pytorch.py b/python/lammps/mliap/pytorch.py new file mode 100644 index 0000000000..9aa2da80f4 --- /dev/null +++ b/python/lammps/mliap/pytorch.py @@ -0,0 +1,326 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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: Nicholas Lubbers (LANL) +# ------------------------------------------------------------------------- + +import numpy as np +import torch + +def calc_n_params(model): + """ + Returns the sum of two decimal numbers in binary digits. + + Parameters: + model (torch.nn.Module): Network model that maps descriptors to a per atom attribute + + Returns: + n_params (int): Number of NN model parameters + """ + return sum(p.nelement() for p in model.parameters()) + +class TorchWrapper(torch.nn.Module): + """ + A class to wrap Modules to ensure lammps mliap compatability. + + ... + + Attributes + ---------- + model : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + device : torch.nn.Module (None) + Accelerator device + + dtype : torch.dtype (torch.float64) + Dtype to use on device + + n_params : torch.nn.Module (None) + Number of NN model parameters + + n_descriptors : int + Max number of per atom descriptors + + n_elements : int + Max number of elements + + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model to produce per atom energies and forces. + """ + + def __init__(self, model, n_descriptors, n_elements, n_params=None, device=None, dtype=torch.float64): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + model : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + n_descriptors : int + Max number of per atom descriptors + + n_elements : int + Max number of elements + + n_params : torch.nn.Module (None) + Number of NN model parameters + + device : torch.nn.Module (None) + Accelerator device + + dtype : torch.dtype (torch.float64) + Dtype to use on device + """ + + super().__init__() + + self.model = model + self.device = device + self.dtype = dtype + + # Put model on device and convert to dtype + self.to(self.dtype) + self.to(self.device) + + if n_params is None: + n_params = calc_n_params(model) + + self.n_params = n_params + self.n_descriptors = n_descriptors + self.n_elements = n_elements + + def forward(self, elems, descriptors, beta, energy): + """ + Takes element types and descriptors calculated via lammps and + calculates the per atom energies and forces. + + Parameters + ---------- + elems : numpy.array + Per atom element types + + descriptors : numpy.array + Per atom descriptors + + beta : numpy.array + Expired beta array to be filled with new betas + + energy : numpy.array + Expired per atom energy array to be filled with new per atom energy + (Note: This is a pointer to the lammps per atom energies) + + + Returns + ------- + None + """ + + descriptors = torch.from_numpy(descriptors).to(dtype=self.dtype, device=self.device).requires_grad_(True) + elems = torch.from_numpy(elems).to(dtype=torch.long, device=self.device) - 1 + + with torch.autograd.enable_grad(): + + energy_nn = self.model(descriptors, elems) + if energy_nn.ndim > 1: + energy_nn = energy_nn.flatten() + + beta_nn = torch.autograd.grad(energy_nn.sum(), descriptors)[0] + + beta[:] = beta_nn.detach().cpu().numpy().astype(np.float64) + energy[:] = energy_nn.detach().cpu().numpy().astype(np.float64) + + +class IgnoreElems(torch.nn.Module): + """ + A class to represent a NN model agnostic of element typing. + + ... + + Attributes + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model + """ + + def __init__(self, subnet): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + """ + + super().__init__() + self.subnet = subnet + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnet(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + return self.subnet(descriptors) + + +class UnpackElems(torch.nn.Module): + """ + A class to represent a NN model pseudo-agnostic of element typing for + systems with multiple element typings. + + ... + + Attributes + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute + + n_types : int + Number of atom types used in training the NN model. + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + """ + + def __init__(self, subnet, n_types): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnet : torch.nn.Module + Network model that maps descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + """ + super().__init__() + self.subnet = subnet + self.n_types = n_types + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnet(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + unpacked_descriptors = torch.zeros(elems.shape[0], self.n_types, descriptors.shape[1], dtype=torch.float64) + for i, ind in enumerate(elems): + unpacked_descriptors[i, ind, :] = descriptors[i] + return self.subnet(torch.reshape(unpacked_descriptors, (elems.shape[0], -1)), elems) + + +class ElemwiseModels(torch.nn.Module): + """ + A class to represent a NN model dependent on element typing. + + ... + + Attributes + ---------- + subnets : list of torch.nn.Modules + Per element type network models that maps per element type + descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + + Methods + ------- + forward(descriptors, elems): + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + """ + + def __init__(self, subnets, n_types): + """ + Constructs all the necessary attributes for the network module. + + Parameters + ---------- + subnets : list of torch.nn.Modules + Per element type network models that maps per element + type descriptors to a per atom attribute. + + n_types : int + Number of atom types used in training the NN model. + """ + + super().__init__() + self.subnets = subnets + self.n_types = n_types + + def forward(self, descriptors, elems): + """ + Feeds descriptors to network model after adding zeros into + descriptor columns relating to different atom types + + Parameters + ---------- + descriptors : torch.tensor + Per atom descriptors + + elems : torch.tensor + Per atom element types + + Returns + ------- + self.subnets(descriptors) : torch.tensor + Per atom attribute computed by the network model + """ + + per_atom_attributes = torch.zeros(elems.size[0]) + given_elems, elem_indices = torch.unique(elems, return_inverse=True) + for i, elem in enumerate(given_elems): + per_atom_attribute[elem_indices == i] = self.subnets[elem](descriptors[elem_indices == i]) + return per_atom_attributes diff --git a/python/lammps/numpy_wrapper.py b/python/lammps/numpy_wrapper.py new file mode 100644 index 0000000000..ce0cb35e47 --- /dev/null +++ b/python/lammps/numpy_wrapper.py @@ -0,0 +1,483 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# NumPy additions +# Written by Richard Berger +################################################################################ + +import warnings +from ctypes import POINTER, c_void_p, c_char_p, c_double, c_int, c_int32, c_int64, cast + + +from .constants import * # lgtm [py/polluting-import] +from .data import NeighList + + +class numpy_wrapper: + """lammps API NumPy Wrapper + + This is a wrapper class that provides additional methods on top of an + existing :py:class:`lammps` instance. The methods transform raw ctypes + pointers into NumPy arrays, which give direct access to the + original data while protecting against out-of-bounds accesses. + + There is no need to explicitly instantiate this class. Each instance + of :py:class:`lammps` has a :py:attr:`numpy ` property + that returns an instance. + + :param lmp: instance of the :py:class:`lammps` class + :type lmp: lammps + """ + def __init__(self, lmp): + self.lmp = lmp + + # ------------------------------------------------------------------------- + + def _ctype_to_numpy_int(self, ctype_int): + import numpy as np + if ctype_int == c_int32: + return np.int32 + elif ctype_int == c_int64: + return np.int64 + return np.intc + + # ------------------------------------------------------------------------- + + def extract_atom(self, name, dtype=LAMMPS_AUTODETECT, nelem=LAMMPS_AUTODETECT, dim=LAMMPS_AUTODETECT): + """Retrieve per-atom properties from LAMMPS as NumPy arrays + + This is a wrapper around the :py:meth:`lammps.extract_atom()` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + .. note:: + + While the returned arrays of per-atom data are dimensioned + for the range [0:nmax] - as is the underlying storage - + the data is usually only valid for the range of [0:nlocal], + unless the property of interest is also updated for ghost + atoms. In some cases, this depends on a LAMMPS setting, see + for example :doc:`comm_modify vel yes `. + + :param name: name of the property + :type name: string + :param dtype: type of the returned data (see :ref:`py_datatype_constants`) + :type dtype: int, optional + :param nelem: number of elements in array + :type nelem: int, optional + :param dim: dimension of each element + :type dim: int, optional + :return: requested data as NumPy array with direct access to C data or None + :rtype: numpy.array or NoneType + """ + if dtype == LAMMPS_AUTODETECT: + dtype = self.lmp.extract_atom_datatype(name) + + if nelem == LAMMPS_AUTODETECT: + if name == "mass": + nelem = self.lmp.extract_global("ntypes") + 1 + else: + nelem = self.lmp.extract_global("nlocal") + if dim == LAMMPS_AUTODETECT: + if dtype in (LAMMPS_INT_2D, LAMMPS_DOUBLE_2D, LAMMPS_INT64_2D): + # TODO add other fields + if name in ("x", "v", "f", "x0","omega", "angmom", "torque", "csforce", "vforce", "vest"): + dim = 3 + elif name == "smd_data_9": + dim = 9 + elif name == "smd_stress": + dim = 6 + else: + dim = 2 + else: + dim = 1 + + raw_ptr = self.lmp.extract_atom(name, dtype) + + if dtype in (LAMMPS_DOUBLE, LAMMPS_DOUBLE_2D): + return self.darray(raw_ptr, nelem, dim) + elif dtype in (LAMMPS_INT, LAMMPS_INT_2D): + return self.iarray(c_int32, raw_ptr, nelem, dim) + elif dtype in (LAMMPS_INT64, LAMMPS_INT64_2D): + return self.iarray(c_int64, raw_ptr, nelem, dim) + return raw_ptr + + # ------------------------------------------------------------------------- + + def extract_atom_iarray(self, name, nelem, dim=1): + warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) + + if name in ['id', 'molecule']: + c_int_type = self.lmp.c_tagint + elif name in ['image']: + c_int_type = self.lmp.c_imageint + else: + c_int_type = c_int + + if dim == 1: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT) + else: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_INT_2D) + + return self.iarray(c_int_type, raw_ptr, nelem, dim) + + # ------------------------------------------------------------------------- + + def extract_atom_darray(self, name, nelem, dim=1): + warnings.warn("deprecated, use extract_atom instead", DeprecationWarning) + + if dim == 1: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE) + else: + raw_ptr = self.lmp.extract_atom(name, LAMMPS_DOUBLE_2D) + + return self.darray(raw_ptr, nelem, dim) + + # ------------------------------------------------------------------------- + + def extract_compute(self, cid, cstyle, ctype): + """Retrieve data from a LAMMPS compute + + This is a wrapper around the + :py:meth:`lammps.extract_compute() ` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param cid: compute ID + :type cid: string + :param cstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type cstyle: int + :param ctype: type of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ctype: int + :return: requested data either as float, as NumPy array with direct access to C data, or None + :rtype: float, numpy.array, or NoneType + """ + value = self.lmp.extract_compute(cid, cstyle, ctype) + + if cstyle == LMP_STYLE_GLOBAL: + if ctype == LMP_TYPE_VECTOR: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_VECTOR) + return self.darray(value, nrows) + elif ctype == LMP_TYPE_ARRAY: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + return self.darray(value, nrows, ncols) + elif cstyle == LMP_STYLE_LOCAL: + nrows = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_ROWS) + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + if ncols == 0: + return self.darray(value, nrows) + else: + return self.darray(value, nrows, ncols) + elif cstyle == LMP_STYLE_ATOM: + if ctype == LMP_TYPE_VECTOR: + nlocal = self.lmp.extract_global("nlocal") + return self.darray(value, nlocal) + elif ctype == LMP_TYPE_ARRAY: + nlocal = self.lmp.extract_global("nlocal") + ncols = self.lmp.extract_compute(cid, cstyle, LMP_SIZE_COLS) + return self.darray(value, nlocal, ncols) + return value + + # ------------------------------------------------------------------------- + + def extract_fix(self, fid, fstyle, ftype, nrow=0, ncol=0): + """Retrieve data from a LAMMPS fix + + This is a wrapper around the :py:meth:`lammps.extract_fix() ` method. + It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param fid: fix ID + :type fid: string + :param fstyle: style of the data retrieve (global, atom, or local), see :ref:`py_style_constants` + :type fstyle: int + :param ftype: type or size of the returned data (scalar, vector, or array), see :ref:`py_type_constants` + :type ftype: int + :param nrow: index of global vector element or row index of global array element + :type nrow: int + :param ncol: column index of global array element + :type ncol: int + :return: requested data + :rtype: integer or double value, pointer to 1d or 2d double array or None + + """ + value = self.lmp.extract_fix(fid, fstyle, ftype, nrow, ncol) + if fstyle == LMP_STYLE_ATOM: + if ftype == LMP_TYPE_VECTOR: + nlocal = self.lmp.extract_global("nlocal") + return self.darray(value, nlocal) + elif ftype == LMP_TYPE_ARRAY: + nlocal = self.lmp.extract_global("nlocal") + ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) + return self.darray(value, nlocal, ncols) + elif fstyle == LMP_STYLE_LOCAL: + if ftype == LMP_TYPE_VECTOR: + nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) + return self.darray(value, nrows) + elif ftype == LMP_TYPE_ARRAY: + nrows = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_ROWS, 0, 0) + ncols = self.lmp.extract_fix(fid, fstyle, LMP_SIZE_COLS, 0, 0) + return self.darray(value, nrows, ncols) + return value + + # ------------------------------------------------------------------------- + + def extract_variable(self, name, group=None, vartype=LMP_VAR_EQUAL): + """ Evaluate a LAMMPS variable and return its data + + This function is a wrapper around the function + :py:meth:`lammps.extract_variable() ` + method. It behaves the same as the original method, but returns NumPy arrays + instead of ``ctypes`` pointers. + + :param name: name of the variable to execute + :type name: string + :param group: name of group for atom-style variable (ignored for equal-style variables) + :type group: string + :param vartype: type of variable, see :ref:`py_vartype_constants` + :type vartype: int + :return: the requested data or None + :rtype: c_double, numpy.array, or NoneType + """ + import numpy as np + value = self.lmp.extract_variable(name, group, vartype) + if vartype == LMP_VAR_ATOM: + return np.ctypeslib.as_array(value) + return value + + # ------------------------------------------------------------------------- + + def gather_bonds(self): + """Retrieve global list of bonds as NumPy array + + This is a wrapper around :py:meth:`lammps.gather_bonds() ` + It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` list. + + .. versionadded:: 28Jul2021 + + :return: the requested data as a 2d-integer numpy array + :rtype: numpy.array(nbonds,3) + """ + import numpy as np + nbonds, value = self.lmp.gather_bonds() + return np.ctypeslib.as_array(value).reshape(nbonds,3) + + # ------------------------------------------------------------------------- + + def fix_external_get_force(self, fix_id): + """Get access to the array with per-atom forces of a fix external instance with a given fix ID. + + This function is a wrapper around the + :py:meth:`lammps.fix_external_get_force() ` + method. It behaves the same as the original method, but returns a NumPy array instead + of a ``ctypes`` pointer. + + .. versionchanged:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :return: requested data + :rtype: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + value = self.lmp.fix_external_get_force(fix_id) + return self.darray(value,nlocal,3) + + # ------------------------------------------------------------------------- + + def fix_external_set_energy_peratom(self, fix_id, eatom): + """Set the per-atom energy contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_energy_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + if len(eatom) < nlocal: + raise Exception('per-atom energy dimension must be at least nlocal') + + c_double_p = POINTER(c_double) + value = eatom.astype(np.double) + return self.lmp.lib.lammps_fix_external_set_energy_peratom(self.lmp.lmp, fix_id.encode(), + value.ctypes.data_as(c_double_p)) + + # ------------------------------------------------------------------------- + + def fix_external_set_virial_peratom(self, fix_id, vatom): + """Set the per-atom virial contribution for a fix external instance with the given ID. + + This function is an alternative to + :py:meth:`lammps.fix_external_set_virial_peratom() ` + method. It behaves the same as the original method, but accepts a NumPy array + instead of a list as argument. + + .. versionadded:: 28Jul2021 + + :param fix_id: Fix-ID of a fix external instance + :type: string + :param eatom: per-atom potential energy + :type: numpy.array + """ + import numpy as np + nlocal = self.lmp.extract_setting('nlocal') + if len(vatom) < nlocal: + raise Exception('per-atom virial first dimension must be at least nlocal') + if len(vatom[0]) != 6: + raise Exception('per-atom virial second dimension must be 6') + + c_double_pp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C') + + # recast numpy array to be compatible with library interface + value = (vatom.__array_interface__['data'][0] + + np.arange(vatom.shape[0])*vatom.strides[0]).astype(np.uintp) + + # change prototype to our custom type + self.lmp.lib.lammps_fix_external_set_virial_peratom.argtypes = [ c_void_p, c_char_p, c_double_pp ] + + self.lmp.lib.lammps_fix_external_set_virial_peratom(self.lmp.lmp, fix_id.encode(), value) + + # ------------------------------------------------------------------------- + + def get_neighlist(self, idx): + """Returns an instance of :class:`NumPyNeighList` which wraps access to the neighbor list with the given index + + :param idx: index of neighbor list + :type idx: int + :return: an instance of :class:`NumPyNeighList` wrapping access to neighbor list data + :rtype: NumPyNeighList + """ + if idx < 0: + return None + return NumPyNeighList(self.lmp, idx) + + # ------------------------------------------------------------------------- + + def get_neighlist_element_neighbors(self, idx, element): + """Return data of neighbor list entry + + This function is a wrapper around the function + :py:meth:`lammps.get_neighlist_element_neighbors() ` + method. It behaves the same as the original method, but returns a NumPy array containing the neighbors + instead of a ``ctypes`` pointer. + + :param element: neighbor list index + :type element: int + :param element: neighbor list element index + :type element: int + :return: tuple with atom local index and numpy array of neighbor local atom indices + :rtype: (int, numpy.array) + """ + iatom, numneigh, c_neighbors = self.lmp.get_neighlist_element_neighbors(idx, element) + neighbors = self.iarray(c_int, c_neighbors, numneigh, 1) + return iatom, neighbors + + # ------------------------------------------------------------------------- + + def iarray(self, c_int_type, raw_ptr, nelem, dim=1): + if raw_ptr is None: + return None + + import numpy as np + np_int_type = self._ctype_to_numpy_int(c_int_type) + + if dim == 1: + ptr = cast(raw_ptr, POINTER(c_int_type * nelem)) + else: + ptr = cast(raw_ptr[0], POINTER(c_int_type * nelem * dim)) + + a = np.frombuffer(ptr.contents, dtype=np_int_type) + + if dim > 1: + a.shape = (nelem, dim) + else: + a.shape = (nelem) + return a + + # ------------------------------------------------------------------------- + + def darray(self, raw_ptr, nelem, dim=1): + if raw_ptr is None: + return None + + import numpy as np + + if dim == 1: + ptr = cast(raw_ptr, POINTER(c_double * nelem)) + else: + ptr = cast(raw_ptr[0], POINTER(c_double * nelem * dim)) + + a = np.frombuffer(ptr.contents) + + if dim > 1: + a.shape = (nelem, dim) + else: + a.shape = (nelem) + return a + +# ------------------------------------------------------------------------- + +class NumPyNeighList(NeighList): + """This is a wrapper class that exposes the contents of a neighbor list. + + It can be used like a regular Python list. Each element is a tuple of: + + * the atom local index + * a NumPy array containing the local atom indices of its neighbors + + Internally it uses the lower-level LAMMPS C-library interface. + + :param lmp: reference to instance of :py:class:`lammps` + :type lmp: lammps + :param idx: neighbor list index + :type idx: int + """ + def __init__(self, lmp, idx): + super(NumPyNeighList, self).__init__(lmp, idx) + + def get(self, element): + """ + Access a specific neighbor list entry. "element" must be a number from 0 to the size-1 of the list + + :return: tuple with atom local index, numpy array of neighbor local atom indices + :rtype: (int, numpy.array) + """ + iatom, neighbors = self.lmp.numpy.get_neighlist_element_neighbors(self.idx, element) + return iatom, neighbors + + def find(self, iatom): + """ + Find the neighbor list for a specific (local) atom iatom. + If there is no list for iatom, None is returned. + + :return: numpy array of neighbor local atom indices + :rtype: numpy.array or None + """ + inum = self.size + for ii in range(inum): + idx, neighbors = self.get(ii) + if idx == iatom: + return neighbors + return None diff --git a/python/lammps/pylammps.py b/python/lammps/pylammps.py new file mode 100644 index 0000000000..1fe1f2452b --- /dev/null +++ b/python/lammps/pylammps.py @@ -0,0 +1,990 @@ +# ---------------------------------------------------------------------- +# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator +# https://www.lammps.org/ Sandia National Laboratories +# Steve Plimpton, sjplimp@sandia.gov +# +# 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. +# ------------------------------------------------------------------------- + +################################################################################ +# Alternative Python Wrapper +# Written by Richard Berger +################################################################################ + +# for python2/3 compatibility + +from __future__ import print_function + +import io +import os +import re +import sys +import tempfile +from collections import namedtuple + +from .core import lammps + +# ------------------------------------------------------------------------- + +class OutputCapture(object): + """ Utility class to capture LAMMPS library output """ + def __init__(self): + self.stdout_fd = 1 + self.captured_output = "" + + def __enter__(self): + self.tmpfile = tempfile.TemporaryFile(mode='w+b') + + sys.stdout.flush() + + # make copy of original stdout + self.stdout_orig = os.dup(self.stdout_fd) + + # replace stdout and redirect to temp file + os.dup2(self.tmpfile.fileno(), self.stdout_fd) + return self + + def __exit__(self, exc_type, exc_value, traceback): + os.dup2(self.stdout_orig, self.stdout_fd) + os.close(self.stdout_orig) + self.tmpfile.close() + + @property + def output(self): + sys.stdout.flush() + self.tmpfile.flush() + self.tmpfile.seek(0, io.SEEK_SET) + self.captured_output = self.tmpfile.read().decode('utf-8') + return self.captured_output + +# ------------------------------------------------------------------------- + +class Variable(object): + def __init__(self, pylammps_instance, name, style, definition): + self._pylmp = pylammps_instance + self.name = name + self.style = style + self.definition = definition.split() + + @property + def value(self): + if self.style == 'atom': + return list(self._pylmp.lmp.extract_variable(self.name, "all", 1)) + else: + value = self._pylmp.lmp_print('"${%s}"' % self.name).strip() + try: + return float(value) + except ValueError: + return value + +# ------------------------------------------------------------------------- + +class AtomList(object): + """ + A dynamic list of atoms that returns either an :py:class:`Atom` or + :py:class:`Atom2D` instance for each atom. Instances are only allocated + when accessed. + + :ivar natoms: total number of atoms + :ivar dimensions: number of dimensions in system + """ + def __init__(self, pylammps_instance): + self._pylmp = pylammps_instance + self.natoms = self._pylmp.system.natoms + self.dimensions = self._pylmp.system.dimensions + self._loaded = {} + + def __getitem__(self, index): + """ + Return Atom with given local index + + :param index: Local index of atom + :type index: int + :rtype: Atom or Atom2D + """ + if index not in self._loaded: + if self.dimensions == 2: + atom = Atom2D(self._pylmp, index) + else: + atom = Atom(self._pylmp, index) + self._loaded[index] = atom + return self._loaded[index] + + def __len__(self): + return self.natoms + + +# ------------------------------------------------------------------------- + +class Atom(object): + """ + A wrapper class then represents a single atom inside of LAMMPS + + It provides access to properties of the atom and allows you to change some of them. + """ + def __init__(self, pylammps_instance, index): + self._pylmp = pylammps_instance + self.index = index + + def __dir__(self): + return [k for k in super().__dir__() if not k.startswith('_')] + + def get(self, name, index): + prop = self._pylmp.lmp.numpy.extract_atom(name) + if prop is not None: + return prop[index] + return None + + @property + def id(self): + """ + Return the atom ID + + :type: int + """ + return self.get("id", self.index) + + @property + def type(self): + """ + Return the atom type + + :type: int + """ + return self.get("type", self.index) + + @property + def mol(self): + """ + Return the atom molecule index + + :type: int + """ + return self.get("mol", self.index) + + @property + def mass(self): + """ + Return the atom mass + + :type: float + """ + return self.get("mass", self.index) + + @property + def radius(self): + """ + Return the particle radius + + :type: float + """ + return self.get("radius", self.index) + + @property + def position(self): + """ + :getter: Return position of atom + :setter: Set position of atom + :type: numpy.array (float, float, float) + """ + return self.get("x", self.index) + + @position.setter + def position(self, value): + current = self.position + current[:] = value + + @property + def velocity(self): + """ + :getter: Return velocity of atom + :setter: Set velocity of atom + :type: numpy.array (float, float, float) + """ + return self.get("v", self.index) + + @velocity.setter + def velocity(self, value): + current = self.velocity + current[:] = value + + @property + def force(self): + """ + Return the total force acting on the atom + + :type: numpy.array (float, float, float) + """ + return self.get("f", self.index) + + @force.setter + def force(self, value): + current = self.force + current[:] = value + + @property + def torque(self): + """ + Return the total torque acting on the atom + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @force.setter + def torque(self, value): + current = self.torque + current[:] = value + + @property + def omega(self): + """ + Return the rotational velocity of the particle + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @omega.setter + def omega(self, value): + current = self.torque + current[:] = value + + @property + def torque(self): + """ + Return the total torque acting on the particle + + :type: numpy.array (float, float, float) + """ + return self.get("torque", self.index) + + @torque.setter + def torque(self, value): + current = self.torque + current[:] = value + + @property + def angular_momentum(self): + """ + Return the angular momentum of the particle + + :type: numpy.array (float, float, float) + """ + return self.get("angmom", self.index) + + @angular_momentum.setter + def angular_momentum(self, value): + current = self.angular_momentum + current[:] = value + + @property + def charge(self): + """ + Return the atom charge + + :type: float + """ + return self.get("q", self.index) + +# ------------------------------------------------------------------------- + +class Atom2D(Atom): + """ + A wrapper class then represents a single 2D atom inside of LAMMPS + + Inherits all properties from the :py:class:`Atom` class, but returns 2D versions + of position, velocity, and force. + + It provides access to properties of the atom and allows you to change some of them. + """ + def __init__(self, pylammps_instance, index): + super(Atom2D, self).__init__(pylammps_instance, index) + + @property + def position(self): + """Access to coordinates of an atom + + :getter: Return position of atom + :setter: Set position of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).position[0:2] + + @position.setter + def position(self, value): + current = self.position + current[:] = value + + @property + def velocity(self): + """Access to velocity of an atom + :getter: Return velocity of atom + :setter: Set velocity of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).velocity[0:2] + + @velocity.setter + def velocity(self, value): + current = self.velocity + current[:] = value + + @property + def force(self): + """Access to force of an atom + :getter: Return force of atom + :setter: Set force of atom + :type: numpy.array (float, float) + """ + return super(Atom2D, self).force[0:2] + + @force.setter + def force(self, value): + current = self.force + current[:] = value + +# ------------------------------------------------------------------------- + +class variable_set: + def __init__(self, name, variable_dict): + self._name = name + array_pattern = re.compile(r"(?P.+)\[(?P[0-9]+)\]") + + for key, value in variable_dict.items(): + m = array_pattern.match(key) + if m: + g = m.groupdict() + varname = g['arr'] + idx = int(g['index']) + if varname not in self.__dict__: + self.__dict__[varname] = {} + self.__dict__[varname][idx] = value + else: + self.__dict__[key] = value + + def __str__(self): + return "{}({})".format(self._name, ','.join(["{}={}".format(k, self.__dict__[k]) for k in self.__dict__.keys() if not k.startswith('_')])) + + def __dir__(self): + return [k for k in self.__dict__.keys() if not k.startswith('_')] + + def __repr__(self): + return self.__str__() + +# ------------------------------------------------------------------------- + +def get_thermo_data(output): + """ traverse output of runs and extract thermo data columns """ + if isinstance(output, str): + lines = output.splitlines() + else: + lines = output + + runs = [] + columns = [] + in_run = False + current_run = {} + + for line in lines: + if line.startswith("Per MPI rank memory allocation"): + in_run = True + elif in_run and len(columns) == 0: + # first line after memory usage are column names + columns = line.split() + + current_run = {} + + for col in columns: + current_run[col] = [] + + elif line.startswith("Loop time of "): + in_run = False + columns = [] + thermo_data = variable_set('ThermoData', current_run) + r = {'thermo' : thermo_data } + runs.append(namedtuple('Run', list(r.keys()))(*list(r.values()))) + elif in_run and len(columns) > 0: + items = line.split() + # Convert thermo output and store it. + # It must have the same number of columns and + # all of them must be convertible to floats. + # Otherwise we ignore the line + if len(items) == len(columns): + try: + values = [float(x) for x in items] + for i, col in enumerate(columns): + current_run[col].append(values[i]) + except ValueError: + # cannot convert. must be a non-thermo output. ignore. + pass + + return runs + +# ------------------------------------------------------------------------- +# ------------------------------------------------------------------------- + +class PyLammps(object): + """ + This is a Python wrapper class around the lower-level + :py:class:`lammps` class, exposing a more Python-like, + object-oriented interface for prototyping system inside of IPython and + Jupyter notebooks. + + It either creates its own instance of :py:class:`lammps` or can be + initialized with an existing instance. The arguments are the same of the + lower-level interface. The original interface can still be accessed via + :py:attr:`PyLammps.lmp`. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + :param verbose: print all LAMMPS output to stdout + :type verbose: bool + + :ivar lmp: instance of original LAMMPS Python interface + :vartype lmp: :py:class:`lammps` + + :ivar runs: list of completed runs, each storing the thermo output + :vartype run: list + """ + + def __init__(self, name="", cmdargs=None, ptr=None, comm=None, verbose=False): + self.has_echo = False + self.verbose = verbose + + if cmdargs: + if '-echo' in cmdargs: + idx = cmdargs.index('-echo') + # ensures that echo line is ignored during output capture + self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both') + + if ptr: + if isinstance(ptr,PyLammps): + self.lmp = ptr.lmp + elif isinstance(ptr,lammps): + self.lmp = ptr + else: + self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) + else: + self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm) + print("LAMMPS output is captured by PyLammps wrapper") + self._cmd_history = [] + self._enable_cmd_history = False + self.runs = [] + + def __enter__(self): + return self + + def __exit__(self, ex_type, ex_value, ex_traceback): + self.close() + + def __del__(self): + if self.lmp: self.lmp.close() + self.lmp = None + + def close(self): + """Explicitly delete a LAMMPS instance + + This is a wrapper around the :py:meth:`lammps.close` of the Python interface. + """ + if self.lmp: self.lmp.close() + self.lmp = None + + def version(self): + """Return a numerical representation of the LAMMPS version in use. + + This is a wrapper around the :py:meth:`lammps.version` function of the Python interface. + + :return: version number + :rtype: int + """ + return self.lmp.version() + + def file(self, file): + """Read LAMMPS commands from a file. + + This is a wrapper around the :py:meth:`lammps.file` function of the Python interface. + + :param path: Name of the file/path with LAMMPS commands + :type path: string + """ + self.lmp.file(file) + + @property + def enable_cmd_history(self): + """ + :getter: Return whether command history is saved + :setter: Set if command history should be saved + :type: bool + """ + return self._enable_cmd_history + + @enable_cmd_history.setter + def enable_cmd_history(self, value): + """ + :getter: Return whether command history is saved + :setter: Set if command history should be saved + :type: bool + """ + self._enable_cmd_history = (value == True) + + def write_script(self, filepath): + """ + Write LAMMPS script file containing all commands executed up until now + + :param filepath: path to script file that should be written + :type filepath: string + """ + with open(filepath, "w") as f: + for cmd in self._cmd_history: + print(cmd, file=f) + + def clear_cmd_history(self): + """ + Clear LAMMPS command history up to this point + """ + self._cmd_history = [] + + def command(self, cmd): + """ + Execute LAMMPS command + + If :py:attr:`PyLammps.enable_cmd_history` is set to ``True``, commands executed + will be recorded. The entire command history can be written to a file using + :py:meth:`PyLammps.write_script()`. To clear the command history, use + :py:meth:`PyLammps.clear_cmd_history()`. + + :param cmd: command string that should be executed + :type: cmd: string + """ + self.lmp.command(cmd) + + if self.enable_cmd_history: + self._cmd_history.append(cmd) + + def run(self, *args, **kwargs): + """ + Execute LAMMPS run command with given arguments + + All thermo output during the run is captured and saved as new entry in + :py:attr:`PyLammps.runs`. The latest run can be retrieved by + :py:attr:`PyLammps.last_run`. + """ + output = self.__getattr__('run')(*args, **kwargs) + self.runs += get_thermo_data(output) + return output + + @property + def last_run(self): + """ + Return data produced of last completed run command + + :getter: Returns an object containing information about the last run command + :type: dict + """ + if len(self.runs) > 0: + return self.runs[-1] + return None + + @property + def atoms(self): + """ + All atoms of this LAMMPS instance + + :getter: Returns a list of atoms currently in the system + :type: AtomList + """ + return AtomList(self) + + @property + def system(self): + """ + The system state of this LAMMPS instance + + :getter: Returns an object with properties storing the current system state + :type: namedtuple + """ + output = self.lmp_info("system") + output = output[output.index("System information:")+1:] + d = self._parse_info_system(output) + return namedtuple('System', d.keys())(*d.values()) + + @property + def communication(self): + """ + The communication state of this LAMMPS instance + + :getter: Returns an object with properties storing the current communication state + :type: namedtuple + """ + output = self.lmp_info("communication") + output = output[output.index("Communication information:")+1:] + d = self._parse_info_communication(output) + return namedtuple('Communication', d.keys())(*d.values()) + + @property + def computes(self): + """ + The list of active computes of this LAMMPS instance + + :getter: Returns a list of computes that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("computes") + output = output[output.index("Compute information:")+1:] + return self._parse_element_list(output) + + @property + def dumps(self): + """ + The list of active dumps of this LAMMPS instance + + :getter: Returns a list of dumps that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("dumps") + output = output[output.index("Dump information:")+1:] + return self._parse_element_list(output) + + @property + def fixes(self): + """ + The list of active fixes of this LAMMPS instance + + :getter: Returns a list of fixes that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("fixes") + output = output[output.index("Fix information:")+1:] + return self._parse_element_list(output) + + @property + def groups(self): + """ + The list of active atom groups of this LAMMPS instance + + :getter: Returns a list of atom groups that are currently active in this LAMMPS instance + :type: list + """ + output = self.lmp_info("groups") + output = output[output.index("Group information:")+1:] + return self._parse_groups(output) + + @property + def variables(self): + """ + Returns a dictionary of all variables defined in the current LAMMPS instance + + :getter: Returns a dictionary of all variables that are defined in this LAMMPS instance + :type: dict + """ + output = self.lmp_info("variables") + output = output[output.index("Variable information:")+1:] + variables = {} + for v in self._parse_element_list(output): + variables[v['name']] = Variable(self, v['name'], v['style'], v['def']) + return variables + + def eval(self, expr): + """ + Evaluate expression + + :param expr: the expression string that should be evaluated inside of LAMMPS + :type expr: string + + :return: the value of the evaluated expression + :rtype: float if numeric, string otherwise + """ + value = self.lmp_print('"$(%s)"' % expr).strip() + try: + return float(value) + except ValueError: + return value + + def _split_values(self, line): + return [x.strip() for x in line.split(',')] + + def _get_pair(self, value): + return [x.strip() for x in value.split('=')] + + def _parse_info_system(self, output): + system = {} + + for line in output: + if line.startswith("Units"): + system['units'] = self._get_pair(line)[1] + elif line.startswith("Atom style"): + system['atom_style'] = self._get_pair(line)[1] + elif line.startswith("Atom map"): + system['atom_map'] = self._get_pair(line)[1] + elif line.startswith("Atoms"): + parts = self._split_values(line) + system['natoms'] = int(self._get_pair(parts[0])[1]) + system['ntypes'] = int(self._get_pair(parts[1])[1]) + system['style'] = self._get_pair(parts[2])[1] + elif line.startswith("Kspace style"): + system['kspace_style'] = self._get_pair(line)[1] + elif line.startswith("Dimensions"): + system['dimensions'] = int(self._get_pair(line)[1]) + elif line.startswith("Orthogonal box"): + system['orthogonal_box'] = [float(x) for x in self._get_pair(line)[1].split('x')] + elif line.startswith("Boundaries"): + system['boundaries'] = self._get_pair(line)[1] + elif line.startswith("xlo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("ylo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("zlo"): + keys, values = [self._split_values(x) for x in self._get_pair(line)] + for key, value in zip(keys, values): + system[key] = float(value) + elif line.startswith("Molecule type"): + system['molecule_type'] = self._get_pair(line)[1] + elif line.startswith("Bonds"): + parts = self._split_values(line) + system['nbonds'] = int(self._get_pair(parts[0])[1]) + system['nbondtypes'] = int(self._get_pair(parts[1])[1]) + system['bond_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Angles"): + parts = self._split_values(line) + system['nangles'] = int(self._get_pair(parts[0])[1]) + system['nangletypes'] = int(self._get_pair(parts[1])[1]) + system['angle_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Dihedrals"): + parts = self._split_values(line) + system['ndihedrals'] = int(self._get_pair(parts[0])[1]) + system['ndihedraltypes'] = int(self._get_pair(parts[1])[1]) + system['dihedral_style'] = self._get_pair(parts[2])[1] + elif line.startswith("Impropers"): + parts = self._split_values(line) + system['nimpropers'] = int(self._get_pair(parts[0])[1]) + system['nimpropertypes'] = int(self._get_pair(parts[1])[1]) + system['improper_style'] = self._get_pair(parts[2])[1] + + return system + + def _parse_info_communication(self, output): + comm = {} + + for line in output: + if line.startswith("MPI library"): + comm['mpi_version'] = line.split(':')[1].strip() + elif line.startswith("Comm style"): + parts = self._split_values(line) + comm['comm_style'] = self._get_pair(parts[0])[1] + comm['comm_layout'] = self._get_pair(parts[1])[1] + elif line.startswith("Processor grid"): + comm['proc_grid'] = [int(x) for x in self._get_pair(line)[1].split('x')] + elif line.startswith("Communicate velocities for ghost atoms"): + comm['ghost_velocity'] = (self._get_pair(line)[1] == "yes") + elif line.startswith("Nprocs"): + parts = self._split_values(line) + comm['nprocs'] = int(self._get_pair(parts[0])[1]) + comm['nthreads'] = int(self._get_pair(parts[1])[1]) + return comm + + def _parse_element_list(self, output): + elements = [] + + for line in output: + if not line or (":" not in line): continue + element_info = self._split_values(line.split(':')[1].strip()) + element = {'name': element_info[0]} + for key, value in [self._get_pair(x) for x in element_info[1:]]: + element[key] = value + elements.append(element) + return elements + + def _parse_groups(self, output): + groups = [] + group_pattern = re.compile(r"(?P.+) \((?P.+)\)") + + for line in output: + m = group_pattern.match(line.split(':')[1].strip()) + group = {'name': m.group('name'), 'type': m.group('type')} + groups.append(group) + return groups + + def lmp_print(self, s): + """ needed for Python2 compatibility, since print is a reserved keyword """ + return self.__getattr__("print")(s) + + def __dir__(self): + return sorted(set(['angle_coeff', 'angle_style', 'atom_modify', 'atom_style', 'atom_style', + 'bond_coeff', 'bond_style', 'boundary', 'change_box', 'communicate', 'compute', + 'create_atoms', 'create_box', 'delete_atoms', 'delete_bonds', 'dielectric', + 'dihedral_coeff', 'dihedral_style', 'dimension', 'dump', 'fix', 'fix_modify', + 'group', 'improper_coeff', 'improper_style', 'include', 'kspace_modify', + 'kspace_style', 'lattice', 'mass', 'minimize', 'min_style', 'neighbor', + 'neigh_modify', 'newton', 'nthreads', 'pair_coeff', 'pair_modify', + 'pair_style', 'processors', 'read', 'read_data', 'read_restart', 'region', + 'replicate', 'reset_timestep', 'restart', 'run', 'run_style', 'thermo', + 'thermo_modify', 'thermo_style', 'timestep', 'undump', 'unfix', 'units', + 'variable', 'velocity', 'write_restart'] + self.lmp.available_styles("command"))) + + def lmp_info(self, s): + # skip anything before and after Info-Info-Info + # also skip timestamp line + output = self.__getattr__("info")(s) + indices = [index for index, line in enumerate(output) if line.startswith("Info-Info-Info-Info")] + start = indices[0] + end = indices[1] + return [line for line in output[start+2:end] if line] + + def __getattr__(self, name): + """ + This method is where the Python 'magic' happens. If a method is not + defined by the class PyLammps, it assumes it is a LAMMPS command. It takes + all the arguments, concatinates them to a single string, and executes it using + :py:meth:`lammps.PyLammps.command()`. + + :param verbose: Print output of command + :type verbose: bool + :return: line or list of lines of output, None if no output + :rtype: list or string + """ + def handler(*args, **kwargs): + cmd_args = [name] + [str(x) for x in args] + self.lmp.flush_buffers() + + with OutputCapture() as capture: + cmd = ' '.join(cmd_args) + self.command(cmd) + self.lmp.flush_buffers() + output = capture.output + + comm = self.lmp.get_mpi_comm() + if comm: + output = self.lmp.comm.bcast(output, root=0) + + if self.verbose or ('verbose' in kwargs and kwargs['verbose']): + print(output, end = '') + + lines = output.splitlines() + + if self.has_echo: + lines = lines[1:] + + if len(lines) > 1: + return lines + elif len(lines) == 1: + return lines[0] + return None + + return handler + + +class IPyLammps(PyLammps): + """ + IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammmps interface + + It either creates its own instance of :py:class:`lammps` or can be + initialized with an existing instance. The arguments are the same of the + lower-level interface. The original interface can still be accessed via + :py:attr:`PyLammps.lmp`. + + :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) + :type name: string + :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. + :type cmdargs: list + :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. + :type ptr: pointer + :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. + :type comm: MPI_Comm + """ + + def __init__(self,name="",cmdargs=None,ptr=None,comm=None): + super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) + + def image(self, filename="snapshot.png", group="all", color="type", diameter="type", + size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"): + """ Generate image using write_dump command and display it + + See :doc:`dump image ` for more information. + + :param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG + :type filename: string + :param group: the group of atoms write_image should use + :type group: string + :param color: name of property used to determine color + :type color: string + :param diameter: name of property used to determine atom diameter + :type diameter: string + :param size: dimensions of image + :type size: tuple (width, height) + :param view: view parameters + :type view: tuple (theta, phi) + :param center: center parameters + :type center: tuple (flag, center_x, center_y, center_z) + :param up: vector pointing to up direction + :type up: tuple (up_x, up_y, up_z) + :param zoom: zoom factor + :type zoom: float + :param background_color: background color of scene + :type background_color: string + + :return: Image instance used to display image in notebook + :rtype: :py:class:`IPython.core.display.Image` + """ + cmd_args = [group, "image", filename, color, diameter] + + if size is not None: + width = size[0] + height = size[1] + cmd_args += ["size", width, height] + + if view is not None: + theta = view[0] + phi = view[1] + cmd_args += ["view", theta, phi] + + if center is not None: + flag = center[0] + Cx = center[1] + Cy = center[2] + Cz = center[3] + cmd_args += ["center", flag, Cx, Cy, Cz] + + if up is not None: + Ux = up[0] + Uy = up[1] + Uz = up[2] + cmd_args += ["up", Ux, Uy, Uz] + + if zoom is not None: + cmd_args += ["zoom", zoom] + + cmd_args.append("modify backcolor " + background_color) + + self.write_dump(*cmd_args) + from IPython.core.display import Image + return Image(filename) + + def video(self, filename): + """ + Load video from file + + Can be used to visualize videos from :doc:`dump movie `. + + :param filename: Path to video file + :type filename: string + :return: HTML Video Tag used by notebook to embed a video + :rtype: :py:class:`IPython.display.HTML` + """ + from IPython.display import HTML + return HTML("") From de12ea976222872e657fd11aac60c3b3e8bf0734 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:21:19 -0600 Subject: [PATCH 68/75] Clean up --- lammps | 1 - 1 file changed, 1 deletion(-) delete mode 120000 lammps diff --git a/lammps b/lammps deleted file mode 120000 index 6ce10c039d..0000000000 --- a/lammps +++ /dev/null @@ -1 +0,0 @@ -python/lammps/ \ No newline at end of file From 9e93eda19a29e8b78d91281b6ecc989addf30b55 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Thu, 7 Jul 2022 17:23:58 -0600 Subject: [PATCH 69/75] Format --- src/ML-SNAP/compute_snap.h | 1 - src/ML-SNAP/sna.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ML-SNAP/compute_snap.h b/src/ML-SNAP/compute_snap.h index 4ef714190c..0a9af58519 100644 --- a/src/ML-SNAP/compute_snap.h +++ b/src/ML-SNAP/compute_snap.h @@ -58,7 +58,6 @@ class ComputeSnap : public Compute { Compute *c_virial; void dbdotr_compute(); - }; } // namespace LAMMPS_NS diff --git a/src/ML-SNAP/sna.cpp b/src/ML-SNAP/sna.cpp index 30ae30ae4f..123c16e2ac 100644 --- a/src/ML-SNAP/sna.cpp +++ b/src/ML-SNAP/sna.cpp @@ -1334,7 +1334,6 @@ double SNA::memory_usage() void SNA::create_twojmax_arrays() { - int jdimpq = twojmax + 2; memory->create(rootpqarray, jdimpq, jdimpq, "sna:rootpqarray"); From 45a02e52395d7c5b9c2aa25f58e1d579acd82dca Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 8 Jul 2022 13:07:49 -0600 Subject: [PATCH 70/75] Cosmetic changes --- src/ML-SNAP/compute_snap.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 3200ecd756..15a2c6ebed 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -13,6 +13,7 @@ ------------------------------------------------------------------------- */ #include "compute_snap.h" + #include "sna.h" #include "atom.h" #include "update.h" @@ -145,12 +146,12 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : } else if (strcmp(arg[iarg],"dgradflag") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute snap command"); - dgradflag = atoi(arg[iarg+1]); + dgradflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal compute snap command"); - switchinnerflag = atoi(arg[iarg+1]); + switchinnerflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg], "sinner") == 0) { iarg++; @@ -228,7 +229,6 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : ComputeSnap::~ComputeSnap() { - memory->destroy(snap); memory->destroy(snapall); memory->destroy(snap_peratom); @@ -252,9 +252,8 @@ void ComputeSnap::init() if (force->pair == nullptr) error->all(FLERR,"Compute snap requires a pair style be defined"); - if (cutmax > force->pair->cutforce){ + if (cutmax > force->pair->cutforce) error->all(FLERR,"Compute snap cutoff is longer than pairwise cutoff"); - } // need an occasional full neighbor list @@ -321,17 +320,15 @@ void ComputeSnap::compute_array() // clear global array - for (int irow = 0; irow < size_array_rows; irow++){ - for (int icoeff = 0; icoeff < size_array_cols; icoeff++){ + for (int irow = 0; irow < size_array_rows; irow++) + for (int icoeff = 0; icoeff < size_array_cols; icoeff++) snap[irow][icoeff] = 0.0; - } - } // clear local peratom array + for (int i = 0; i < ntotal; i++) - for (int icoeff = 0; icoeff < size_peratom; icoeff++) { + for (int icoeff = 0; icoeff < size_peratom; icoeff++) snap_peratom[i][icoeff] = 0.0; - } // invoke full neighbor list (will copy or build if necessary) @@ -537,7 +534,6 @@ void ComputeSnap::compute_array() snap[bik_rows + ((atom->tag[i]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][icoeff+3] += snaptr->dblist[icoeff][2]; } } - } // loop over jj inside // accumulate Bi @@ -569,9 +565,7 @@ void ComputeSnap::compute_array() snap[irow][k++] += snaptr->blist[icoeff]; numneigh_sum += ninside; } - - } // if (mask[i] & groupbit) - + } } // for (int ii = 0; ii < inum; ii++) { // accumulate bispectrum force contributions to global array @@ -604,7 +598,6 @@ void ComputeSnap::compute_array() snap[irow++][lastcol] = atom->f[i][1]; snap[irow][lastcol] = atom->f[i][2]; } - } else { // for dgradflag=1, put forces at first 3 columns of bik rows @@ -632,7 +625,6 @@ void ComputeSnap::compute_array() int irow = 0; double reference_energy = c_pe->compute_scalar(); snapall[irow][lastcol] = reference_energy; - } else { // assign reference energy right after the dgrad rows, first column From e7f11a82ac6a63eb3ff953056acaf5d7868cd18c Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 8 Jul 2022 13:24:26 -0600 Subject: [PATCH 71/75] More cosmetic changes --- src/ML-SNAP/compute_snap.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 15a2c6ebed..86edcf99fe 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -205,7 +205,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : ndims_force = 3; ndims_virial = 6; yoffset = nvalues; - zoffset = 2 * nvalues; + zoffset = 2*nvalues; natoms = atom->natoms; bik_rows = 1; if (bikflag) bik_rows = natoms; @@ -236,13 +236,13 @@ ComputeSnap::~ComputeSnap() memory->destroy(wjelem); memory->destroy(cutsq); delete snaptr; + if (chemflag) memory->destroy(map); if (switchinnerflag) { memory->destroy(sinnerelem); memory->destroy(dinnerelem); } - } /* ---------------------------------------------------------------------- */ @@ -304,7 +304,6 @@ void ComputeSnap::init_list(int /*id*/, NeighList *ptr) void ComputeSnap::compute_array() { - int ntotal = atom->nlocal + atom->nghost; invoked_array = update->ntimestep; @@ -345,9 +344,7 @@ void ComputeSnap::compute_array() double** const x = atom->x; const int* const mask = atom->mask; - int ninside; - int numneigh_sum = 0; - int dgrad_row_indx; + for (int ii = 0; ii < inum; ii++) { int irow = 0; if (bikflag) irow = atom->tag[ilist[ii] & NEIGHMASK]-1; @@ -407,7 +404,7 @@ void ComputeSnap::compute_array() // assign quantities in snaptr - ninside=0; + int ninside=0; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; @@ -563,7 +560,6 @@ void ComputeSnap::compute_array() int k = 3; for (int icoeff = 0; icoeff < ncoeff; icoeff++) snap[irow][k++] += snaptr->blist[icoeff]; - numneigh_sum += ninside; } } } // for (int ii = 0; ii < inum; ii++) { @@ -571,7 +567,6 @@ void ComputeSnap::compute_array() // accumulate bispectrum force contributions to global array if (!dgradflag) { - for (int itype = 0; itype < atom->ntypes; itype++) { const int typeoffset_local = ndims_peratom*nvalues*itype; const int typeoffset_global = nvalues*itype; From b4424e77b11fa2f1240cbd303937184b38a43350 Mon Sep 17 00:00:00 2001 From: rohskopf Date: Fri, 8 Jul 2022 13:32:51 -0600 Subject: [PATCH 72/75] More cosmetic changes 2 --- src/ML-SNAP/compute_snap.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 86edcf99fe..7865829a01 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -144,13 +144,11 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : bikflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg],"dgradflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); + if (iarg + 2 > narg) error->all(FLERR,"Illegal compute snap command"); dgradflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg],"switchinnerflag") == 0) { - if (iarg+2 > narg) - error->all(FLERR,"Illegal compute snap command"); + if (iarg + 2 > narg) error->all(FLERR,"Illegal compute snap command"); switchinnerflag = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg], "sinner") == 0) { @@ -191,10 +189,8 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (dgradflag && quadraticflag) error->all(FLERR,"Illegal compute snap command: dgradflag=1 not implemented for quadratic SNAP"); - snaptr = new SNA(lmp, rfac0, twojmax, - rmin0, switchflag, bzeroflag, - chemflag, bnormflag, wselfallflag, - nelements, switchinnerflag); + snaptr = new SNA(lmp, rfac0, twojmax, rmin0, switchflag, bzeroflag, chemflag, bnormflag, + wselfallflag, nelements, switchinnerflag); ncoeff = snaptr->ncoeff; nvalues = ncoeff; From 9f309d56fef96b1ba47f44da106e87828c9be80b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 9 Jul 2022 16:06:21 -0400 Subject: [PATCH 73/75] a few more programming style changes --- src/ML-SNAP/compute_snap.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ML-SNAP/compute_snap.cpp b/src/ML-SNAP/compute_snap.cpp index 7865829a01..5d2f3666bc 100644 --- a/src/ML-SNAP/compute_snap.cpp +++ b/src/ML-SNAP/compute_snap.cpp @@ -14,17 +14,17 @@ #include "compute_snap.h" -#include "sna.h" #include "atom.h" -#include "update.h" -#include "modify.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "force.h" -#include "pair.h" #include "comm.h" -#include "memory.h" #include "error.h" +#include "force.h" +#include "memory.h" +#include "modify.h" +#include "neigh_list.h" +#include "neighbor.h" +#include "pair.h" +#include "sna.h" +#include "update.h" #include @@ -207,7 +207,7 @@ ComputeSnap::ComputeSnap(LAMMPS *lmp, int narg, char **arg) : if (bikflag) bik_rows = natoms; dgrad_rows = ndims_force*natoms; size_array_rows = bik_rows+dgrad_rows + ndims_virial; - if (dgradflag){ + if (dgradflag) { size_array_rows = bik_rows + 3*natoms*natoms + 1; size_array_cols = nvalues + 3; error->warning(FLERR,"dgradflag=1 creates a N^2 array, beware of large systems."); @@ -360,7 +360,7 @@ void ComputeSnap::compute_array() const int typeoffset_local = ndims_peratom*nvalues*(itype-1); const int typeoffset_global = nvalues*(itype-1); - if (dgradflag){ + if (dgradflag) { // dBi/dRi tags @@ -376,7 +376,7 @@ void ComputeSnap::compute_array() // dBi/dRj tags - for (int j=0; jtag[i]-1) + 0][0] = atom->tag[i]-1; snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][1] = j; snap[bik_rows + ((j)*3*natoms) + 3*(atom->tag[i]-1) + 0][2] = 0; @@ -593,7 +593,7 @@ void ComputeSnap::compute_array() // for dgradflag=1, put forces at first 3 columns of bik rows - for (int i=0; inlocal; i++){ + for (int i=0; inlocal; i++) { int iglobal = atom->tag[i]; snap[iglobal-1][0+0] = atom->f[i][0]; snap[iglobal-1][0+1] = atom->f[i][1]; From ee8fa353556a3ea35ba8e85970dbf2feab56cb01 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 9 Jul 2022 16:07:55 -0400 Subject: [PATCH 74/75] fix typo --- doc/src/compute_sna_atom.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/compute_sna_atom.rst b/doc/src/compute_sna_atom.rst index 5efecd6738..1b6b200685 100644 --- a/doc/src/compute_sna_atom.rst +++ b/doc/src/compute_sna_atom.rst @@ -536,7 +536,7 @@ columns corresponding to the type of that atom. This is not true in the case of *dgradflag* keyword = 1 (see below). If the *dgradflag* keyword is set to 1, this changes the structure of the -gloabl array completely. +global array completely. Here the *snad/atom* quantities are replaced with rows corresponding to descriptor gradient components on single atoms: From bf6ad1bb453a36791ca9844f9f36386faa97fbeb Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 10 Jul 2022 05:00:03 -0400 Subject: [PATCH 75/75] add option to read_dump that prevents resetting the timestep --- doc/src/dump.rst | 5 +++++ doc/src/read_dump.rst | 35 +++++++++++++++++++---------------- src/read_dump.cpp | 7 ++++++- src/read_dump.h | 3 ++- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/doc/src/dump.rst b/doc/src/dump.rst index 4417e186ec..843d2e4883 100644 --- a/doc/src/dump.rst +++ b/doc/src/dump.rst @@ -783,6 +783,11 @@ To write gzipped dump files, you must either compile LAMMPS with the -DLAMMPS_GZIP option or use the styles from the COMPRESS package. See the :doc:`Build settings ` page for details. +While a dump command is active (i.e. has not been stopped by using +the undump command), no commands may be used that will change the +timestep (e.g. :doc:`reset_timestep `). LAMMPS +will terminate with an error otherwise. + The *atom/gz*, *cfg/gz*, *custom/gz*, and *xyz/gz* styles are part of the COMPRESS package. They are only enabled if LAMMPS was built with that package. See the :doc:`Build package ` page for diff --git a/doc/src/read_dump.rst b/doc/src/read_dump.rst index 3a771b9c2d..c98347914b 100644 --- a/doc/src/read_dump.rst +++ b/doc/src/read_dump.rst @@ -24,12 +24,13 @@ Syntax *fx*,\ *fy*,\ *fz* = force components * zero or more keyword/value pairs may be appended -* keyword = *nfile* or *box* or *replace* or *purge* or *trim* or *add* or *label* or *scaled* or *wrapped* or *format* +* keyword = *nfile* or *box* or *timestep* or *replace* or *purge* or *trim* or *add* or *label* or *scaled* or *wrapped* or *format* .. parsed-literal:: *nfile* value = Nfiles = how many parallel dump files exist *box* value = *yes* or *no* = replace simulation box with dump box + *timestep* value = *yes* or *no* = reset simulation timestep with dump timestep *replace* value = *yes* or *no* = overwrite atoms with dump atoms *purge* value = *yes* or *no* = delete all atoms before adding dump atoms *trim* value = *yes* or *no* = trim atoms not in dump snapshot @@ -60,6 +61,7 @@ Examples read_dump dump.dcd 0 x y z box yes format molfile dcd read_dump dump.file 1000 x y z vx vy vz box yes format molfile lammpstrj /usr/local/lib/vmd/plugins/LINUXAMD64/plugins/molfile read_dump dump.file 5000 x y vx vy trim yes + read_dump dump.file 5000 x y vx vy add yes box no timestep no read_dump ../run7/dump.file.gz 10000 x y z box yes read_dump dump.xyz 10 x y z box no format molfile xyz ../plugins read_dump dump.dcd 0 x y z format molfile dcd @@ -71,9 +73,9 @@ Description """"""""""" Read atom information from a dump file to overwrite the current atom -coordinates, and optionally the atom velocities and image flags and -the simulation box dimensions. This is useful for restarting a run -from a particular snapshot in a dump file. See the +coordinates, and optionally the atom velocities and image flags, the +simulation timestep, and the simulation box dimensions. This is useful +for restarting a run from a particular snapshot in a dump file. See the :doc:`read_restart ` and :doc:`read_data ` commands for alternative methods to do this. Also see the :doc:`rerun ` command for a means of reading multiple snapshots @@ -89,9 +91,9 @@ Also note that reading per-atom information from a dump snapshot is limited to the atom coordinates, velocities and image flags, as explained below. Other atom properties, which may be necessary to run a valid simulation, such as atom charge, or bond topology information -for a molecular system, are not read from (or even contained in) dump -files. Thus this auxiliary information should be defined in the usual -way, e.g. in a data file read in by a :doc:`read_data ` +for a molecular system, are not read from (or may not even be contained +in) dump files. Thus this auxiliary information should be defined in +the usual way, e.g. in a data file read in by a :doc:`read_data ` command, before using the read_dump command, or by the :doc:`set ` command, after the dump snapshot is read. @@ -165,11 +167,10 @@ variable *ntimestep*: uint64_t ntimestep 5*scalar (0) 0 50 100 150 200 -Note that the *xyz* -and *molfile* formats do not store the timestep. For these formats, -timesteps are numbered logically, in a sequential manner, starting -from 0. Thus to access the 10th snapshot in an *xyz* or *mofile* -formatted dump file, use *Nstep* = 9. +Note that the *xyz* and *molfile* formats do not store the timestep. +For these formats, timesteps are numbered logically, in a sequential +manner, starting from 0. Thus to access the 10th snapshot in an *xyz* +or *mofile* formatted dump file, use *Nstep* = 9. The dimensions of the simulation box for the selected snapshot are also read; see the *box* keyword discussion below. For the *native* @@ -266,8 +267,10 @@ for how this is done, determined by the specified fields and optional keywords. The timestep of the snapshot becomes the current timestep for the -simulation. See the :doc:`reset_timestep ` command if -you wish to change this after the dump snapshot is read. +simulation unless the *timestep* keyword is specified with a *no* value +(default setting is *yes*). See the :doc:`reset_timestep ` +command if you wish to change this to a different value after the dump +snapshot is read. If the *box* keyword is specified with a *yes* value, then the current simulation box dimensions are replaced by the dump snapshot box @@ -391,7 +394,7 @@ Related commands Default """"""" -The option defaults are box = yes, replace = yes, purge = no, trim = -no, add = no, scaled = no, wrapped = yes, and format = native. +The option defaults are box = yes, timestep = yes, replace = yes, purge = no, +trim = no, add = no, scaled = no, wrapped = yes, and format = native. .. _vmd: http://www.ks.uiuc.edu/Research/vmd diff --git a/src/read_dump.cpp b/src/read_dump.cpp index 8af19ee601..aa9ec0bbf0 100644 --- a/src/read_dump.cpp +++ b/src/read_dump.cpp @@ -121,7 +121,7 @@ void ReadDump::command(int narg, char **arg) // reset timestep to nstep - update->reset_timestep(nstep, true); + if (timestepflag) update->reset_timestep(nstep, true); // counters @@ -1202,6 +1202,7 @@ int ReadDump::fields_and_keywords(int narg, char **arg) multiproc_nfile = 0; boxflag = 1; + timestepflag = 1; replaceflag = 1; purgeflag = 0; trimflag = 0; @@ -1219,6 +1220,10 @@ int ReadDump::fields_and_keywords(int narg, char **arg) if (iarg+2 > narg) error->all(FLERR,"Illegal read_dump command"); boxflag = utils::logical(FLERR,arg[iarg+1],false,lmp); iarg += 2; + } else if (strcmp(arg[iarg],"timestep") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal read_dump command"); + timestepflag = utils::logical(FLERR,arg[iarg+1],false,lmp); + iarg += 2; } else if (strcmp(arg[iarg],"replace") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal read_dump command"); replaceflag = utils::logical(FLERR,arg[iarg+1],false,lmp); diff --git a/src/read_dump.h b/src/read_dump.h index 1a94cd1cd3..506cd197c1 100644 --- a/src/read_dump.h +++ b/src/read_dump.h @@ -63,7 +63,8 @@ class ReadDump : public Command { int dimension; // same as in Domain int triclinic; - int boxflag; // overwrite simulation with dump file box params + int boxflag; // overwrite simulation box with dump file box params + int timestepflag; // overwrite simulation timestep with dump file timestep int replaceflag, addflag; // flags for processing dump snapshot atoms int trimflag, purgeflag; int scaleflag; // user 0/1 if dump file coords are unscaled/scaled