From 85765a2bf3242a2f6deb14a2d8c7084ec13076b4 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 12 Jun 2023 12:45:52 +0300 Subject: [PATCH 001/305] Include born_matrix() definition in bond_gaussian.h --- src/EXTRA-MOLECULE/bond_gaussian.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.h b/src/EXTRA-MOLECULE/bond_gaussian.h index 7af6f1f4d9..e466df47d4 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.h +++ b/src/EXTRA-MOLECULE/bond_gaussian.h @@ -35,6 +35,7 @@ class BondGaussian : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: int *nterms; From a05fcc326efd1f7ebef08516ddf15b262c155215 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 12 Jun 2023 12:47:21 +0300 Subject: [PATCH 002/305] Implement born_matrix() in bond_gaussian.cpp --- src/EXTRA-MOLECULE/bond_gaussian.cpp | 45 +++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.cpp b/src/EXTRA-MOLECULE/bond_gaussian.cpp index baca0b6e1a..816ab51516 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.cpp +++ b/src/EXTRA-MOLECULE/bond_gaussian.cpp @@ -12,7 +12,7 @@ ------------------------------------------------------------------------- */ #include "bond_gaussian.h" - +#include #include "atom.h" #include "comm.h" #include "error.h" @@ -35,6 +35,7 @@ BondGaussian::BondGaussian(LAMMPS *lmp) : Bond(lmp), nterms(nullptr), bond_temperature(nullptr), alpha(nullptr), width(nullptr), r0(nullptr) { + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -294,3 +295,45 @@ double BondGaussian::single(int type, double rsq, int /*i*/, int /*j*/, double & return -(force->boltz * bond_temperature[type]) * log(sum_g_i); } + +/* ---------------------------------------------------------------------- */ + +void BondGaussian::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + double r = sqrt(rsq); + + // first derivative of energy with respect to distance + double sum_g_i = 0.0; + double sum_numerator = 0.0; + for (int i = 0; i < nterms[type]; i++) { + double dr = r - r0[type][i]; + double prefactor = (alpha[type][i] / (width[type][i] * sqrt(MY_PI2))); + double exponent = -2 * dr * dr / (width[type][i] * width[type][i]); + double g_i = prefactor * exp(exponent); + sum_g_i += g_i; + sum_numerator += g_i * dr / (width[type][i] * width[type][i]); + } + + if (sum_g_i < SMALL) sum_g_i = SMALL; + du = 4.0 * (force->boltz * bond_temperature[type]) * (sum_numerator / sum_g_i); + + // second derivative of energy with respect to distance + sum_g_i = 0.0; + double sum_dg_i = 0.0; + double sum_d2g_i = 0.0; + for (int i = 0; i < nterms[type]; i++) { + double dr = r - r0[type][i]; + double prefactor = (alpha[type][i] / (width[type][i] * sqrt(MY_PI2))); + double exponent = -2 * dr * dr / (width[type][i] * width[type][i]); + double g_i = prefactor * exp(exponent); + sum_g_i += g_i; + sum_dg_i -= 4.0 * g_i * dr / pow(width[type][i], 2); + sum_d2g_i += 4.0 * g_i * (4.0 * pow(r0[type][i], 2) - 8.0 * r0[type][i] * r - pow(width[type][i], 2) + 4.0 * r * r) / pow(width[type][i], 4) ; + } + + if (sum_g_i < SMALL) sum_g_i = SMALL; + double numerator = sum_d2g_i*sum_g_i - sum_dg_i*sum_dg_i; + double denominator = sum_g_i * sum_g_i; + + du2 = - (force->boltz * bond_temperature[type]) * numerator / denominator; +} From 42c843ff4f783069d409404be42fa89c43f60ccc Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:24:24 +0300 Subject: [PATCH 003/305] remove iostream from bond_gaussian.cpp --- src/EXTRA-MOLECULE/bond_gaussian.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_gaussian.cpp b/src/EXTRA-MOLECULE/bond_gaussian.cpp index 816ab51516..9a8546e278 100644 --- a/src/EXTRA-MOLECULE/bond_gaussian.cpp +++ b/src/EXTRA-MOLECULE/bond_gaussian.cpp @@ -12,7 +12,7 @@ ------------------------------------------------------------------------- */ #include "bond_gaussian.h" -#include + #include "atom.h" #include "comm.h" #include "error.h" From 4e17cc551e93faf1433bb002bfbde9838684c2eb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:25:35 +0300 Subject: [PATCH 004/305] inlcude born_matrix() definition in in angle_quartic.h --- src/EXTRA-MOLECULE/angle_quartic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_quartic.h b/src/EXTRA-MOLECULE/angle_quartic.h index 3f0396f27b..7de51b24d1 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.h +++ b/src/EXTRA-MOLECULE/angle_quartic.h @@ -35,6 +35,7 @@ class AngleQuartic : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *k2, *k3, *k4, *theta0; From eb8512ba2a988b9baea384fe96aa935c28e6005a Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:26:48 +0300 Subject: [PATCH 005/305] implementation of born_matrix() for angle_quartic.cpp --- src/EXTRA-MOLECULE/angle_quartic.cpp | 41 +++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/angle_quartic.cpp b/src/EXTRA-MOLECULE/angle_quartic.cpp index f28e209a77..eaccdbe608 100644 --- a/src/EXTRA-MOLECULE/angle_quartic.cpp +++ b/src/EXTRA-MOLECULE/angle_quartic.cpp @@ -37,7 +37,10 @@ using namespace MathConst; /* ---------------------------------------------------------------------- */ -AngleQuartic::AngleQuartic(LAMMPS *lmp) : Angle(lmp) {} +AngleQuartic::AngleQuartic(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -286,3 +289,39 @@ double AngleQuartic::single(int type, int i1, int i2, int i3) double dtheta4 = dtheta3 * dtheta; return k2[type] * dtheta2 + k3[type] * dtheta3 + k4[type] * dtheta4; } + +/* ---------------------------------------------------------------------- */ + +void AngleQuartic::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + + double dtheta = theta - theta0[type]; + double dtheta2 = dtheta * dtheta; + double dtheta3 = dtheta2 * dtheta; + + du = -(2.0 * k2[type] * dtheta + 3.0 * k3[type] * dtheta2 + 4.0 * k4[type] * dtheta3) / s; + du2 = (2.0 * k2[type] + 6.0 * k3[type] * dtheta + 12.0 * k4[type] * dtheta2) / (s*s) - + (2.0 * k2[type] * dtheta + 3.0 * k3[type] * dtheta2 + 4.0 * k4[type] * dtheta3) * c / (s*s*s); +} From 365f4bc55945506de57e44dc0c6ec7102fa2d000 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Sun, 18 Jun 2023 18:44:28 +0300 Subject: [PATCH 006/305] non-zero born_matrix_enable flag in angle_fourier.cpp --- src/EXTRA-MOLECULE/angle_fourier.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_fourier.cpp b/src/EXTRA-MOLECULE/angle_fourier.cpp index 549da0c196..c7eb3d4fe4 100644 --- a/src/EXTRA-MOLECULE/angle_fourier.cpp +++ b/src/EXTRA-MOLECULE/angle_fourier.cpp @@ -39,6 +39,7 @@ using namespace MathConst; AngleFourier::AngleFourier(LAMMPS *lmp) : Angle(lmp) { + born_matrix_enable = 1; k = nullptr; C0 = nullptr; C1 = nullptr; From 2f227614615219d13538481646563c2a8111f251 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 16:38:34 +0300 Subject: [PATCH 007/305] born_matrix() method in bond_mm3.h --- src/YAFF/bond_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/bond_mm3.h b/src/YAFF/bond_mm3.h index 302c4052d0..ea89ac826d 100644 --- a/src/YAFF/bond_mm3.h +++ b/src/YAFF/bond_mm3.h @@ -35,6 +35,7 @@ class BondMM3 : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: double *r0, *k2; From bfc969d5c5ee4da4e9062bffe7f96d55ffb318ab Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 16:39:49 +0300 Subject: [PATCH 008/305] implementation of born_matrix in bond_mm3.cpp --- src/YAFF/bond_mm3.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/YAFF/bond_mm3.cpp b/src/YAFF/bond_mm3.cpp index a5ef6fb8bc..31ce2dad3e 100644 --- a/src/YAFF/bond_mm3.cpp +++ b/src/YAFF/bond_mm3.cpp @@ -31,7 +31,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -BondMM3::BondMM3(LAMMPS *lmp) : Bond(lmp) {} +BondMM3::BondMM3(LAMMPS *lmp) : Bond(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -219,3 +222,19 @@ double BondMM3::single(int type, double rsq, else fforce = 0.0; return k2[type]*dr2*(1.0+K3*dr+K4*dr2); } + +/* ---------------------------------------------------------------------- */ + +void BondMM3::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + double r = sqrt(rsq); + double dr = r - r0[type]; + double dr2 = dr * dr; + double dr3 = dr2 * dr; + + double K3 = -2.55 * k2[type] /force->angstrom; + double K4 = 7.0 * k2[type] * 2.55 * 2.55 / (12.0 * force->angstrom * force->angstrom); + + du = 2.0 * k2[type] * dr + 3.0 * K3 * dr2 + 4.0 * K4 * dr3; + du2 = 2.0 * k2[type] + 6.0 * K3 * dr + 12.0 * K4 * dr2; +} From 6c9d42b7c363a66fa56739f7d3ba3dbc5a1952c2 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:38:50 +0300 Subject: [PATCH 009/305] Include born_matrix() definition in bond_harmonic_shift_cut.h --- src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h index 752ac010d9..09d6ab5330 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.h @@ -35,6 +35,7 @@ class BondHarmonicShiftCut : public Bond { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, double, int, int, double &) override; + void born_matrix(int, double, int, int, double &, double &) override; protected: double *k, *r0, *r1; From f6b259b1866d9d5316d6464eae7d551fea9fc760 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:40:11 +0300 Subject: [PATCH 010/305] Implementing born_matrix in bond_harmonic_shift_cut.cpp --- .../bond_harmonic_shift_cut.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp index fedcb95ee8..ebcfdb0258 100644 --- a/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp +++ b/src/EXTRA-MOLECULE/bond_harmonic_shift_cut.cpp @@ -31,7 +31,10 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -BondHarmonicShiftCut::BondHarmonicShiftCut(LAMMPS *lmp) : Bond(lmp) {} +BondHarmonicShiftCut::BondHarmonicShiftCut(LAMMPS *lmp) : Bond(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -219,3 +222,19 @@ double BondHarmonicShiftCut::single(int type, double rsq, int /*i*/, int /*j*/, fforce = -2.0*k[type]*dr/r; return k[type]*(dr*dr - dr2*dr2); } + +/* ---------------------------------------------------------------------- */ + +void BondHarmonicShiftCut::born_matrix(int type, double rsq, int /*i*/, int /*j*/, double &du, double &du2) +{ + du = 0.0; + du2 = 0.0; + + double r = sqrt(rsq); + if (r>r1[type]) return; + + double dr = r - r0[type]; + + du2 = 2 * k[type]; + if (r > 0.0) du = du2 * dr; +} From ad3752431fe9711ef40f2d53cd28d7ab83153b73 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:42:01 +0300 Subject: [PATCH 011/305] Regular pointer for coord and coordp in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 2b0ffef0f8..1b5b3a911f 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -49,7 +49,7 @@ class ComputeStressMopProfile : public Compute { int originflag; double origin, delta, offset, invdelta; int nbins; - double **coord, **coordp; + double *coord, *coordp; double **values_local, **values_global; double **bond_local, **bond_global; double **local_contribution; From dc1eb43cf2f15c61c0b44bb872407c4b5091e135 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Mon, 19 Jun 2023 19:47:34 +0300 Subject: [PATCH 012/305] Cleaning coord and coordp vectors in compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 1f1dc30733..3a85869f3c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -258,7 +258,7 @@ void ComputeStressMopProfile::compute_array() MPI_Allreduce(&bond_local[0][0],&bond_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); for (int ibin=0; ibin ((hi-lo) * invdelta + 1.5); //allocate bin arrays - memory->create(coord,nbins,1,"stress/mop/profile:coord"); - memory->create(coordp,nbins,1,"stress/mop/profile:coordp"); + memory->create(coord,nbins,"stress/mop/profile:coord"); + memory->create(coordp,nbins,"stress/mop/profile:coordp"); memory->create(values_local,nbins,nvalues,"stress/mop/profile:values_local"); memory->create(values_global,nbins,nvalues,"stress/mop/profile:values_global"); memory->create(bond_local,nbins,nvalues,"stress/mop/profile:bond_local"); @@ -652,11 +652,11 @@ void ComputeStressMopProfile::setup_bins() // set bin coordinates for (i = 0; i < nbins; i++) { - coord[i][0] = offset + i*delta; - if (coord[i][0] < (domain->boxlo[dir]+domain->prd_half[dir])) { - coordp[i][0] = coord[i][0] + domain->prd[dir]; + coord[i] = offset + i*delta; + if (coord[i] < (domain->boxlo[dir]+domain->prd_half[dir])) { + coordp[i] = coord[i] + domain->prd[dir]; } else { - coordp[i][0] = coord[i][0] - domain->prd[dir]; + coordp[i] = coord[i] - domain->prd[dir]; } } } From 2631a159affeedef3dd2eeaaa1c3ca0529396b97 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 15:41:09 +0300 Subject: [PATCH 013/305] define born_matrix in angle_mm3.h --- src/YAFF/angle_mm3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/YAFF/angle_mm3.h b/src/YAFF/angle_mm3.h index 95009a9cf6..22f5bd746c 100644 --- a/src/YAFF/angle_mm3.h +++ b/src/YAFF/angle_mm3.h @@ -35,6 +35,7 @@ class AngleMM3 : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *theta0, *k2; From bb2d691e7863e5b4746da6fd8f6a951581cbde51 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 15:42:47 +0300 Subject: [PATCH 014/305] implement born_matrix in angle_mm3.cpp --- src/YAFF/angle_mm3.cpp | 45 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/YAFF/angle_mm3.cpp b/src/YAFF/angle_mm3.cpp index c75a0d8308..af199f6fe9 100644 --- a/src/YAFF/angle_mm3.cpp +++ b/src/YAFF/angle_mm3.cpp @@ -36,7 +36,10 @@ using namespace MathConst; /* ---------------------------------------------------------------------- */ -AngleMM3::AngleMM3(LAMMPS *lmp) : Angle(lmp) {} +AngleMM3::AngleMM3(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -284,3 +287,43 @@ double AngleMM3::single(int type, int i1, int i2, int i3) return energy; } + +/* ---------------------------------------------------------------------- */ + +void AngleMM3::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + double dtheta = theta - theta0[type]; + double dtheta2 = dtheta*dtheta; + double dtheta3 = dtheta2*dtheta; + double dtheta4 = dtheta3*dtheta; + double dtheta5 = dtheta4*dtheta; + double df = 2.0 * dtheta - 2.406423 * dtheta2 + 0.735348 * dtheta3 - 0.65832 * dtheta4 + 1.42254 * dtheta5; + double d2f = 2.0 - 4.812846 * dtheta + 2.206044 * dtheta2 - 2.63328 * dtheta3 + 7.1127 * dtheta4; + + du = -k2[type] * df / s; + du2 = k2[type] * (d2f - df * c / s) / (s * s) ; +} From 345a834c7e0693770e37a13005118ad29e19ff7b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 16:20:57 +0300 Subject: [PATCH 015/305] Include definition of born_matrix() in angle_cosine_periodic.h --- src/EXTRA-MOLECULE/angle_cosine_periodic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.h b/src/EXTRA-MOLECULE/angle_cosine_periodic.h index 4e584b4543..f04ed04784 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.h +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.h @@ -35,6 +35,7 @@ class AngleCosinePeriodic : public Angle { void read_restart(FILE *) override; void write_data(FILE *) override; double single(int, int, int, int) override; + void born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) override; protected: double *k; From 7f3a930d8923a13fb14c02e85d56edb703b1c2cd Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 20 Jun 2023 16:21:57 +0300 Subject: [PATCH 016/305] Implement born_matrix() in angle_cosine_periodic.cpp --- src/EXTRA-MOLECULE/angle_cosine_periodic.cpp | 40 +++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp index 15d0575f6d..34a8e9d8e5 100644 --- a/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp +++ b/src/EXTRA-MOLECULE/angle_cosine_periodic.cpp @@ -38,7 +38,10 @@ using namespace MathSpecial; /* ---------------------------------------------------------------------- */ -AngleCosinePeriodic::AngleCosinePeriodic(LAMMPS *lmp) : Angle(lmp) {} +AngleCosinePeriodic::AngleCosinePeriodic(LAMMPS *lmp) : Angle(lmp) +{ + born_matrix_enable = 1; +} /* ---------------------------------------------------------------------- */ @@ -298,3 +301,38 @@ double AngleCosinePeriodic::single(int type, int i1, int i2, int i3) c = cos(acos(c)*multiplicity[type]); return 2.0*k[type]*(1.0-b[type]*powsign(multiplicity[type])*c); } + +/* ---------------------------------------------------------------------- */ + +void AngleCosinePeriodic::born_matrix(int type, int i1, int i2, int i3, double &du, double &du2) +{ + double **x = atom->x; + + double delx1 = x[i1][0] - x[i2][0]; + double dely1 = x[i1][1] - x[i2][1]; + double delz1 = x[i1][2] - x[i2][2]; + domain->minimum_image(delx1,dely1,delz1); + double r1 = sqrt(delx1*delx1 + dely1*dely1 + delz1*delz1); + + double delx2 = x[i3][0] - x[i2][0]; + double dely2 = x[i3][1] - x[i2][1]; + double delz2 = x[i3][2] - x[i2][2]; + domain->minimum_image(delx2,dely2,delz2); + double r2 = sqrt(delx2*delx2 + dely2*dely2 + delz2*delz2); + + double c = delx1*delx2 + dely1*dely2 + delz1*delz2; + c /= r1*r2; + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + double theta = acos(c); + + double s = sqrt(1.0 - c*c); + if (s < SMALL) s = SMALL; + s = 1.0/s; + + double m_angle = multiplicity[type] * theta; + double prefactor = -2.0 * k[type] * b[type] * powsign(multiplicity[type]) * multiplicity[type]; + + du = prefactor * sin(m_angle) / s; + du2 = prefactor * (c * sin(m_angle) - s * cos(m_angle) * multiplicity[type]) / (s * s * s); +} From fb31ffe17cbff6854bc60870a8be54258df7ed07 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 21 Jun 2023 10:56:53 +0300 Subject: [PATCH 017/305] Definition of born_matrix() in dihedral_helix.h --- src/EXTRA-MOLECULE/dihedral_helix.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/dihedral_helix.h b/src/EXTRA-MOLECULE/dihedral_helix.h index 436895c5c3..172a8c3469 100644 --- a/src/EXTRA-MOLECULE/dihedral_helix.h +++ b/src/EXTRA-MOLECULE/dihedral_helix.h @@ -33,6 +33,7 @@ class DihedralHelix : public Dihedral { void write_restart(FILE *) override; void read_restart(FILE *) override; void write_data(FILE *) override; + void born_matrix(int, int, int, int, int, double &, double &) override; protected: double *aphi, *bphi, *cphi; From 7ab9da0212f1f3601f7238fe2997f9313d8cc923 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 21 Jun 2023 10:59:13 +0300 Subject: [PATCH 018/305] Implementation of born_matrix in dihedral_helix.cpp --- src/EXTRA-MOLECULE/dihedral_helix.cpp | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/EXTRA-MOLECULE/dihedral_helix.cpp b/src/EXTRA-MOLECULE/dihedral_helix.cpp index 059bef74a4..1d99de6ba9 100644 --- a/src/EXTRA-MOLECULE/dihedral_helix.cpp +++ b/src/EXTRA-MOLECULE/dihedral_helix.cpp @@ -41,6 +41,7 @@ using namespace MathConst; DihedralHelix::DihedralHelix(LAMMPS *lmp) : Dihedral(lmp) { writedata = 1; + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -324,3 +325,108 @@ void DihedralHelix::write_data(FILE *fp) for (int i = 1; i <= atom->ndihedraltypes; i++) fprintf(fp,"%d %g %g %g\n",i,aphi[i],bphi[i],cphi[i]); } + +/* ----------------------------------------------------------------------*/ + +void DihedralHelix::born_matrix(int nd, int i1, int i2, int i3, int i4, + double &du, double &du2) +{ + double vb1x,vb1y,vb1z,vb2x,vb2y,vb2z,vb3x,vb3y,vb3z,vb2xm,vb2ym,vb2zm; + double sb1,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s12,c; + double cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + int **dihedrallist = neighbor->dihedrallist; + double **x = atom->x; + + int type = dihedrallist[nd][4]; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // 3rd bond + + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) problem(FLERR, i1, i2, i3, i4); + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx > 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + du = -aphi[type] + 3.0*bphi[type]*sin(3.0*phi)*siinv + + cphi[type]*sin(phi + MY_PI4)*siinv; + du2 = -(9.0*bphi[type]*cos(3.0*phi) + cphi[type]*cos(phi + MY_PI4))*siinv*siinv + + (3.0*bphi[type]*sin(3.0*phi) + cphi[type]*sin(phi + MY_PI4))*c*siinv*siinv*siinv; +} From ae96c9bd47e52d3c3066b40914b8e27ab6b042eb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 22 Jun 2023 16:58:00 +0300 Subject: [PATCH 019/305] Define born_matrix() in dihedral_quadratic.h --- src/EXTRA-MOLECULE/dihedral_quadratic.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-MOLECULE/dihedral_quadratic.h b/src/EXTRA-MOLECULE/dihedral_quadratic.h index 90d8c3be6e..89f6fa3b25 100644 --- a/src/EXTRA-MOLECULE/dihedral_quadratic.h +++ b/src/EXTRA-MOLECULE/dihedral_quadratic.h @@ -33,6 +33,7 @@ class DihedralQuadratic : public Dihedral { void write_restart(FILE *) override; void read_restart(FILE *) override; void write_data(FILE *) override; + void born_matrix(int, int, int, int, int, double &, double &) override; protected: double *k, *phi0; From 8e1711c8031699756ba3b5d84fa4582ad161a357 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 22 Jun 2023 17:00:10 +0300 Subject: [PATCH 020/305] Implement born_matrix in dihedral_quadratic.cpp --- src/EXTRA-MOLECULE/dihedral_quadratic.cpp | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/EXTRA-MOLECULE/dihedral_quadratic.cpp b/src/EXTRA-MOLECULE/dihedral_quadratic.cpp index cbe9e3e3a2..f576e6efdd 100644 --- a/src/EXTRA-MOLECULE/dihedral_quadratic.cpp +++ b/src/EXTRA-MOLECULE/dihedral_quadratic.cpp @@ -41,6 +41,7 @@ using namespace MathConst; DihedralQuadratic::DihedralQuadratic(LAMMPS *lmp) : Dihedral(lmp) { writedata = 1; + born_matrix_enable = 1; } /* ---------------------------------------------------------------------- */ @@ -327,3 +328,112 @@ void DihedralQuadratic::write_data(FILE *fp) for (int i = 1; i <= atom->ndihedraltypes; i++) fprintf(fp,"%d %g %g \n",i,k[i],phi0[i]*180.0/MY_PI); } + +/* ----------------------------------------------------------------------*/ + +void DihedralQuadratic::born_matrix(int nd, int i1, int i2, int i3, int i4, + double &du, double &du2) +{ + double vb1x,vb1y,vb1z,vb2x,vb2y,vb2z,vb3x,vb3y,vb3z,vb2xm,vb2ym,vb2zm; + double sb1,sb3,rb1,rb3,c0,b1mag2,b1mag,b2mag2; + double b2mag,b3mag2,b3mag,ctmp,r12c1,c1mag,r12c2; + double c2mag,sc1,sc2,s12,c; + double s1,s2,cx,cy,cz,cmag,dx,phi,si,siinv,sin2; + + int **dihedrallist = neighbor->dihedrallist; + double **x = atom->x; + + int type = dihedrallist[nd][4]; + + // 1st bond + + vb1x = x[i1][0] - x[i2][0]; + vb1y = x[i1][1] - x[i2][1]; + vb1z = x[i1][2] - x[i2][2]; + + // 2nd bond + + vb2x = x[i3][0] - x[i2][0]; + vb2y = x[i3][1] - x[i2][1]; + vb2z = x[i3][2] - x[i2][2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // 3rd bond + vb3x = x[i4][0] - x[i3][0]; + vb3y = x[i4][1] - x[i3][1]; + vb3z = x[i4][2] - x[i3][2]; + + // c0 calculation + + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + + // 1st and 2nd angle + + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + cx = vb1y*vb2z - vb1z*vb2y; + cy = vb1z*vb2x - vb1x*vb2z; + cz = vb1x*vb2y - vb1y*vb2x; + cmag = sqrt(cx*cx + cy*cy + cz*cz); + dx = (cx*vb3x + cy*vb3y + cz*vb3z)/cmag/b3mag; + + // error check + + if (c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) + problem(FLERR, i1, i2, i3, i4); + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + phi = acos(c); + if (dx > 0.0) phi *= -1.0; + si = sin(phi); + if (fabs(si) < SMALLER) si = SMALLER; + siinv = 1.0/si; + + double dphi = phi-phi0[type]; + if (dphi > MY_PI) dphi -= 2*MY_PI; + else if (dphi < -MY_PI) dphi += 2*MY_PI; + + du = - 2.0 * k[type] * dphi * siinv; + du2 = 2.0 * k[type] * siinv * siinv * ( 1.0 - dphi * c * siinv) ; +} From 6e32d2932275271ced51152b3c849318c8956601 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 29 Jun 2023 08:53:50 +0300 Subject: [PATCH 021/305] clean up of originflag variable in compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 3a85869f3c..d37a941fde 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -38,7 +38,6 @@ using namespace LAMMPS_NS; enum { X, Y, Z }; -enum { LOWER, CENTER, UPPER, COORD }; enum { TOTAL, CONF, KIN, PAIR, BOND }; // clang-format off @@ -63,11 +62,15 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a // bin parameters - if (strcmp(arg[4],"lower") == 0) originflag = LOWER; - else if (strcmp(arg[4],"center") == 0) originflag = CENTER; - else if (strcmp(arg[4],"upper") == 0) originflag = UPPER; - else originflag = COORD; - if (originflag == COORD) origin = utils::numeric(FLERR,arg[4],false,lmp); + if (strcmp(arg[4],"lower") == 0) { + origin = domain->boxlo[dir]; + } else if (strcmp(arg[4],"center") == 0) { + origin = 0.5 * (domain->boxlo[dir] + domain->boxhi[dir]); + } else if (strcmp(arg[4],"upper") == 0) { + origin = domain->boxhi[dir]; + } else { + origin = utils::numeric(FLERR,arg[4],false,lmp); + } delta = utils::numeric(FLERR,arg[5],false,lmp); invdelta = 1.0/delta; @@ -620,23 +623,14 @@ void ComputeStressMopProfile::setup_bins() boxlo = domain->boxlo; boxhi = domain->boxhi; - if (originflag == LOWER) origin = boxlo[dir]; - else if (originflag == UPPER) origin = boxhi[dir]; - else if (originflag == CENTER) - origin = 0.5 * (boxlo[dir] + boxhi[dir]); + if ((origin > domain->boxhi[dir]) || (origin < domain->boxlo[dir])) + error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - if (origin < boxlo[dir]) { - error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - } else { - n = static_cast ((origin - boxlo[dir]) * invdelta); - lo = origin - n*delta; - } - if (origin < boxhi[dir]) { - n = static_cast ((boxhi[dir] - origin) * invdelta); - hi = origin + n*delta; - } else { - error->all(FLERR,"Origin of bins for compute stress/mop/profile is out of bounds" ); - } + n = static_cast ((origin - boxlo[dir]) * invdelta); + lo = origin - n*delta; + + n = static_cast ((boxhi[dir] - origin) * invdelta); + hi = origin + n*delta; offset = lo; nbins = static_cast ((hi-lo) * invdelta + 1.5); From 484e7ad0e3c295514f063e1e7933f3683e93f383 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 29 Jun 2023 08:54:30 +0300 Subject: [PATCH 022/305] clean up of originflag from compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 1b5b3a911f..5890505d71 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -46,7 +46,6 @@ class ComputeStressMopProfile : public Compute { int bondflag; - int originflag; double origin, delta, offset, invdelta; int nbins; double *coord, *coordp; From 005c15c07b3d4dd445ba276e8f3615852fdb7e71 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 13:26:47 +0300 Subject: [PATCH 023/305] compute kinetic contribution without assuming orthogonal geometry --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 98e3bf7043..18e6ec6115 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -422,6 +422,11 @@ void ComputeStressMop::compute_pairs() xi[1] = atom->x[i][1]; xi[2] = atom->x[i][2]; + // minimum image of xi with respect to the plane + xi[dir] -= pos; + domain->minimum_image(xi[0], xi[1], xi[2]); + xi[dir] += pos; + //velocities at t vi[0] = atom->v[i][0]; vi[1] = atom->v[i][1]; @@ -447,10 +452,8 @@ void ComputeStressMop::compute_pairs() // at each timestep, must check atoms going through the // image of the plane that is closest to the box - double pos_temp = pos+copysign(1.0,domain->prd_half[dir]-pos)*domain->prd[dir]; - if (fabs(xi[dir]-pos)= 0)) { // sgn = copysign(1.0,vi[dir]-vcm[dir]); sgn = copysign(1.0,vi[dir]); From 94fa2f51c9189874ab84661519f0047cd1f0c9db Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 13:38:56 +0300 Subject: [PATCH 024/305] compute kinetic contribution without assuming orthogonal geometry --- .../compute_stress_mop_profile.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index d37a941fde..c8793586ea 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -453,7 +453,22 @@ void ComputeStressMopProfile::compute_pairs() pos = coord[ibin]; pos1 = coordp[ibin]; - if (((xi[dir]-pos)*(xj[dir]-pos)*(xi[dir]-pos1)*(xj[dir]-pos1)<0)) { + // minimum image of xi with respect to the plane + xi[dir] -= pos; + domain->minimum_image(xi[0], xi[1], xi[2]); + xi[dir] += pos; + + // minimum image of xj with respect to xi + xj[0] -= xi[0]; + xj[1] -= xi[1]; + xj[2] -= xi[2]; + domain->minimum_image(xi[0], xi[1], xi[2]); + xj[0] += xi[0]; + xj[1] += xi[1]; + xj[2] += xi[2]; + + double tau = (xi[dir] - pos) / (xi[dir] - xj[dir]); + if ((tau <= 1) && (tau >= 0)) { sgn = copysign(1.0,vi[dir]); From 79ed2d9e8bb1bd5fefbf6fcd24eff5fd2d452381 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:35:25 +0300 Subject: [PATCH 025/305] Definition of compute_angle and related variables in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index 5890505d71..c7214c055c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -39,18 +39,20 @@ class ComputeStressMopProfile : public Compute { private: void compute_pairs(); void compute_bonds(); + void compute_angles(); void setup_bins(); int nvalues, dir; int *which; - int bondflag; + int bondflag, angleflag; double origin, delta, offset, invdelta; int nbins; double *coord, *coordp; double **values_local, **values_global; double **bond_local, **bond_global; + double **angle_local, **angle_global; double **local_contribution; double dt, nktv2p, ftm2v; From 9aa9bdd3ba4bd6f59cbe2723bedc46936cf77b62 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:45:53 +0300 Subject: [PATCH 026/305] Implementation of compute_angles in compute_stress_mop_profile.cpp and related adjustments to flags/memory allocations --- .../compute_stress_mop_profile.cpp | 227 +++++++++++++++++- 1 file changed, 221 insertions(+), 6 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index c8793586ea..dabbbc4781 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -13,7 +13,7 @@ /*------------------------------------------------------------------------ Contributing Authors : Romain Vermorel (LFCR), Laurent Joly (ULyon) - Support for bonds added by : Evangelos Voyiatzis (NovaMechanics) + Support for bonds and angles added by : Evangelos Voyiatzis (NovaMechanics) --------------------------------------------------------------------------*/ #include "compute_stress_mop_profile.h" @@ -38,7 +38,7 @@ using namespace LAMMPS_NS; enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; // clang-format off /* ---------------------------------------------------------------------- */ @@ -107,6 +107,11 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a which[nvalues] = BOND; nvalues++; } + } else if (strcmp(arg[iarg],"angle") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = ANGLE; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop/profile command"); //break; iarg++; @@ -131,6 +136,8 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a values_local = values_global = array = nullptr; bond_local = nullptr; bond_global = nullptr; + angle_local = nullptr; + angle_global = nullptr; local_contribution = nullptr; // bin setup @@ -159,6 +166,8 @@ ComputeStressMopProfile::~ComputeStressMopProfile() memory->destroy(values_global); memory->destroy(bond_local); memory->destroy(bond_global); + memory->destroy(angle_local); + memory->destroy(angle_global); memory->destroy(local_contribution); memory->destroy(array); } @@ -206,9 +215,14 @@ void ComputeStressMopProfile::init() if (force->bond) bondflag = 1; - if (force->angle) - if ((strcmp(force->angle_style, "zero") != 0) && (strcmp(force->angle_style, "none") != 0)) - error->all(FLERR,"compute stress/mop/profile does not account for angle potentials"); + if (force->angle) { + if (force->angle->born_matrix_enable == 0) { + if ((strcmp(force->angle_style, "zero") != 0) && (strcmp(force->angle_style, "none") != 0)) + error->all(FLERR,"compute stress/mop/profile does not account for angle potentials"); + } else { + angleflag = 1; + } + } if (force->dihedral) if ((strcmp(force->dihedral_style, "zero") != 0) && (strcmp(force->dihedral_style, "none") != 0)) error->all(FLERR,"compute stress/mop/profile does not account for dihedral potentials"); @@ -260,13 +274,27 @@ void ComputeStressMopProfile::compute_array() // sum bond contribution over all procs MPI_Allreduce(&bond_local[0][0],&bond_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + if (angleflag) { + //Compute angle contribution on separate procs + compute_angles(); + } else { + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + angle_local[m][i] = 0.0; + } + } + } + + // sum angle contribution over all procs + MPI_Allreduce(&angle_local[0][0],&angle_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + for (int ibin=0; ibinx; + tagint *tag = atom->tag; + int *num_angle = atom->num_angle; + tagint **angle_atom1 = atom->angle_atom1; + tagint **angle_atom2 = atom->angle_atom2; + tagint **angle_atom3 = atom->angle_atom3; + int **angle_type = atom->angle_type; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their angles + Angle *angle = force->angle; + + double duang, du2ang; + double dx[3] = {0.0, 0.0, 0.0}; + double dx_left[3] = {0.0, 0.0, 0.0}; + double dx_right[3] = {0.0, 0.0, 0.0}; + double x_angle_left[3] = {0.0, 0.0, 0.0}; + double x_angle_middle[3] = {0.0, 0.0, 0.0}; + double x_angle_right[3] = {0.0, 0.0, 0.0}; + double dcos_theta[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + angle_local[m][i] = 0.0; + } + local_contribution[m][0] = 0.0; + local_contribution[m][1] = 0.0; + local_contribution[m][2] = 0.0; + } + + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == 1) + na = num_angle[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + na = onemols[imol]->num_angle[iatom]; + } + + for (int i = 0; i < na; i++) { + if (molecular == 1) { + if (tag[atom2] != angle_atom2[atom2][i]) continue; + atype = angle_type[atom2][i]; + atom1 = atom->map(angle_atom1[atom2][i]); + atom3 = atom->map(angle_atom3[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->angle_atom2[atom2][i]) continue; + atype = onemols[imol]->angle_type[atom2][i]; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->angle_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->angle_atom3[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atype <= 0) continue; + + for (int ibin = 0; ibinminimum_image(dx[0], dx[1], dx[2]); + x_angle_left[0] = dx[0]; + x_angle_left[1] = dx[1]; + x_angle_left[2] = dx[2]; + x_angle_left[dir] += pos; + + // minimum image of atom2 with respect to atom1 + dx_left[0] = x[atom2][0] - x_angle_left[0]; + dx_left[1] = x[atom2][1] - x_angle_left[1]; + dx_left[2] = x[atom2][2] - x_angle_left[2]; + domain->minimum_image(dx_left[0], dx_left[1], dx_left[2]); + x_angle_middle[0] = x_angle_left[0] + dx_left[0]; + x_angle_middle[1] = x_angle_left[1] + dx_left[1]; + x_angle_middle[2] = x_angle_left[2] + dx_left[2]; + + // minimum image of atom3 with respect to atom2 + dx_right[0] = x[atom3][0] - x_angle_middle[0]; + dx_right[1] = x[atom3][1] - x_angle_middle[1]; + dx_right[2] = x[atom3][2] - x_angle_middle[2]; + domain->minimum_image(dx_right[0], dx_right[1], dx_right[2]); + x_angle_right[0] = x_angle_middle[0] + dx_right[0]; + x_angle_right[1] = x_angle_middle[1] + dx_right[1]; + x_angle_right[2] = x_angle_middle[2] + dx_right[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_angle_right[dir] - pos) / (x_angle_right[dir] - x_angle_middle[dir]); + double tau_left = (x_angle_middle[dir] - pos) / (x_angle_middle[dir] - x_angle_left[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !left_cross) continue; + + // compute the cos(theta) of the angle + r1 = sqrt(dx_left[0]*dx_left[0] + dx_left[1]*dx_left[1] + dx_left[2]*dx_left[2]); + r2 = sqrt(dx_right[0]*dx_right[0] + dx_right[1]*dx_right[1] + dx_right[2]*dx_right[2]); + cos_theta = -(dx_right[0]*dx_left[0] + dx_right[1]*dx_left[1] + dx_right[2]*dx_left[2])/(r1*r2); + + if (cos_theta > 1.0) cos_theta = 1.0; + if (cos_theta < -1.0) cos_theta = -1.0; + + // The method returns derivative with regards to cos(theta) + angle->born_matrix(atype, atom1, atom2, atom3, duang, du2ang); + // only right bond crossing the plane + if (right_cross && !left_cross) + { + double sgn = copysign(1.0, x_angle_right[dir] - pos); + dcos_theta[0] = sgn*(dx_right[0]*cos_theta/r2 + dx_left[0]/r1)/r2; + dcos_theta[1] = sgn*(dx_right[1]*cos_theta/r2 + dx_left[1]/r1)/r2; + dcos_theta[2] = sgn*(dx_right[2]*cos_theta/r2 + dx_left[2]/r1)/r2; + } + + // only left bond crossing the plane + if (!right_cross && left_cross) + { + double sgn = copysign(1.0, x_angle_left[dir] - pos); + dcos_theta[0] = -sgn*(dx_left[0]*cos_theta/r1 + dx_right[0]/r2)/r1; + dcos_theta[1] = -sgn*(dx_left[1]*cos_theta/r1 + dx_right[1]/r2)/r1; + dcos_theta[2] = -sgn*(dx_left[2]*cos_theta/r1 + dx_right[2]/r2)/r1; + } + + // both bonds crossing the plane + if (right_cross && left_cross) + { + // due to right bond + double sgn = copysign(1.0, x_angle_middle[dir] - pos); + dcos_theta[0] = -sgn*(dx_right[0]*cos_theta/r2 + dx_left[0]/r1)/r2; + dcos_theta[1] = -sgn*(dx_right[1]*cos_theta/r2 + dx_left[1]/r1)/r2; + dcos_theta[2] = -sgn*(dx_right[2]*cos_theta/r2 + dx_left[2]/r1)/r2; + + // due to left bond + dcos_theta[0] += sgn*(dx_left[0]*cos_theta/r1 + dx_right[0]/r2)/r1; + dcos_theta[1] += sgn*(dx_left[1]*cos_theta/r1 + dx_right[1]/r2)/r1; + dcos_theta[2] += sgn*(dx_left[2]*cos_theta/r1 + dx_right[2]/r2)/r1; + } + + // final contribution of the given angle term + local_contribution[ibin][0] += duang*dcos_theta[0]/area*nktv2p; + local_contribution[ibin][1] += duang*dcos_theta[1]/area*nktv2p; + local_contribution[ibin][2] += duang*dcos_theta[2]/area*nktv2p; + } + } + } + + // loop over the keywords and if necessary add the angle contribution + int m = 0; + while (m < nvalues) { + if (which[m] == CONF || which[m] == TOTAL || which[m] == ANGLE) { + for (int ibin = 0; ibin < nbins; ibin++) { + angle_local[ibin][m] = local_contribution[ibin][0]; + angle_local[ibin][m+1] = local_contribution[ibin][1]; + angle_local[ibin][m+2] = local_contribution[ibin][2]; + } + } + m += 3; + } +} + /* ---------------------------------------------------------------------- setup 1d bins and their extent and coordinates called at init() @@ -657,6 +870,8 @@ void ComputeStressMopProfile::setup_bins() memory->create(values_global,nbins,nvalues,"stress/mop/profile:values_global"); memory->create(bond_local,nbins,nvalues,"stress/mop/profile:bond_local"); memory->create(bond_global,nbins,nvalues,"stress/mop/profile:bond_global"); + memory->create(angle_local,nbins,nvalues,"stress/mop/profile:angle_local"); + memory->create(angle_global,nbins,nvalues,"stress/mop/profile:angle_global"); memory->create(local_contribution,nbins,3,"stress/mop/profile:local_contribution"); // set bin coordinates From 78f4e4f1a15061991a7a0073566df2ed43712715 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:51:41 +0300 Subject: [PATCH 027/305] Update compute_stress_mop.rst --- doc/src/compute_stress_mop.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 21c2963545..70986862fe 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -74,9 +74,7 @@ Between one and six keywords can be used to indicate which contributions to the stress must be computed: total stress (total), kinetic stress (kin), configurational stress (conf), stress due to bond stretching (bond), stress due to angle bending (angle) and/or due to pairwise -non-bonded interactions (pair). The angle keyword is currently -available only for the *stress/mop* command and **not** the -*stress/mop/profile* command. +non-bonded interactions (pair). NOTE 1: The configurational stress is computed considering all pairs of atoms where at least one atom belongs to group group-ID. @@ -136,7 +134,7 @@ requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute *stress/mop/profile* does not work with more than two-body pair interactions, long range (kspace) interactions and -angle/dihedral/improper intramolecular interactions. Similarly, compute +dihedral/improper intramolecular interactions. Similarly, compute *stress/mop* does not work with more than two-body pair interactions, long range (kspace) interactions and dihedral/improper intramolecular interactions but works with all bond interactions with the class method From 9fde61fc4e358e44854cf73f8d5cb230b3bf7e7c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Wed, 5 Jul 2023 16:59:02 +0300 Subject: [PATCH 028/305] Update compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index dabbbc4781..2dee2bf60f 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -18,6 +18,7 @@ #include "compute_stress_mop_profile.h" +#include "angle.h" #include "atom.h" #include "atom_vec.h" #include "bond.h" From e3792616ad1d749f526e9e9ae2d4a0b1e76e4214 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 1 Aug 2023 12:54:23 -0600 Subject: [PATCH 029/305] Moving line break in BPM doc pages for link, fixing prop/atom syntax --- doc/src/bond_bpm_rotational.rst | 4 ++-- doc/src/bond_bpm_spring.rst | 4 ++-- src/BPM/bond_bpm.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/src/bond_bpm_rotational.rst b/doc/src/bond_bpm_rotational.rst index 7459d491d6..6734bd7bfe 100644 --- a/doc/src/bond_bpm_rotational.rst +++ b/doc/src/bond_bpm_rotational.rst @@ -147,8 +147,8 @@ By default, pair forces are not calculated between bonded particles. Pair forces can alternatively be overlaid on top of bond forces by setting the *overlay/pair* keyword to *yes*. These settings require specific :doc:`special_bonds ` settings described in the -restrictions. Further details can be found in the :doc:`how to -` page on BPMs. +restrictions. Further details can be found in the :doc:`how to ` +page on BPMs. .. versionadded:: 28Mar2023 diff --git a/doc/src/bond_bpm_spring.rst b/doc/src/bond_bpm_spring.rst index 04ff4d5991..a03c832249 100644 --- a/doc/src/bond_bpm_spring.rst +++ b/doc/src/bond_bpm_spring.rst @@ -113,8 +113,8 @@ By default, pair forces are not calculated between bonded particles. Pair forces can alternatively be overlaid on top of bond forces by setting the *overlay/pair* keyword to *yes*. These settings require specific :doc:`special_bonds ` settings described in the -restrictions. Further details can be found in the :doc:`how to -` page on BPMs. +restrictions. Further details can be found in the :doc:`how to ` +page on BPMs. .. versionadded:: 28Mar2023 diff --git a/src/BPM/bond_bpm.cpp b/src/BPM/bond_bpm.cpp index 3ebeed3f1d..b484df7fab 100644 --- a/src/BPM/bond_bpm.cpp +++ b/src/BPM/bond_bpm.cpp @@ -224,7 +224,7 @@ void BondBPM::settings(int narg, char **arg) ifix = modify->get_fix_by_id(id_fix_prop_atom); if (!ifix) - ifix = modify->add_fix(fmt::format("{} all property/atom {} {} {} ghost yes", + ifix = modify->add_fix(fmt::format("{} all property/atom d_{} d_{} d_{} ghost yes", id_fix_prop_atom, x_ref_id, y_ref_id, z_ref_id)); int type_flag; From cd5ebb86c870ebdceb842130aeea6b9bdf067a30 Mon Sep 17 00:00:00 2001 From: jrgissing Date: Fri, 25 Aug 2023 21:37:57 -0400 Subject: [PATCH 030/305] inserting atoms: correct logic for per-atom mass --- src/REACTION/fix_bond_react.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index d124b06dc2..9254c3f5d1 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -3895,7 +3895,8 @@ int FixBondReact::insert_atoms(tagint **my_update_mega_glove, int iupdate) // guess a somewhat reasonable initial velocity based on reaction site // further control is possible using bond_react_MASTER_group // compute |velocity| corresponding to a given temperature t, using specific atom's mass - double vtnorm = sqrt(t / (force->mvv2e / (dimension * force->boltz)) / atom->mass[twomol->type[m]]); + double mymass = atom->rmass ? atom->rmass[n] : atom->mass[twomol->type[m]]; + double vtnorm = sqrt(t / (force->mvv2e / (dimension * force->boltz)) / mymass); v[n][0] = random[rxnID]->uniform(); v[n][1] = random[rxnID]->uniform(); v[n][2] = random[rxnID]->uniform(); From 999c364b83d69195dfbfeaeb47397feb2d6d7dc7 Mon Sep 17 00:00:00 2001 From: jrgissing Date: Fri, 25 Aug 2023 21:44:42 -0400 Subject: [PATCH 031/305] better way to list rxn counts --- .../reaction/create_atoms_polystyrene/in.grow_styrene | 2 +- examples/PACKAGES/reaction/nylon,6-6_melt/in.large_nylon_melt | 4 ++-- .../PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized | 2 +- .../PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized | 2 +- .../tiny_nylon/in.tiny_nylon.stabilized_variable_probability | 2 +- .../PACKAGES/reaction/tiny_nylon/in.tiny_nylon.unstabilized | 2 +- .../reaction/tiny_polystyrene/in.tiny_polystyrene.stabilized | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/PACKAGES/reaction/create_atoms_polystyrene/in.grow_styrene b/examples/PACKAGES/reaction/create_atoms_polystyrene/in.grow_styrene index 7860db4e55..dcca29c026 100644 --- a/examples/PACKAGES/reaction/create_atoms_polystyrene/in.grow_styrene +++ b/examples/PACKAGES/reaction/create_atoms_polystyrene/in.grow_styrene @@ -40,7 +40,7 @@ fix 1 statted_grp_REACT nvt temp $T $T 100 fix 4 bond_react_MASTER_group temp/rescale 1 $T $T 1 1 -thermo_style custom step temp press density f_myrxns[1] +thermo_style custom step temp press density f_myrxns[*] thermo 100 diff --git a/examples/PACKAGES/reaction/nylon,6-6_melt/in.large_nylon_melt b/examples/PACKAGES/reaction/nylon,6-6_melt/in.large_nylon_melt index 9678a714d6..635b2c9750 100644 --- a/examples/PACKAGES/reaction/nylon,6-6_melt/in.large_nylon_melt +++ b/examples/PACKAGES/reaction/nylon,6-6_melt/in.large_nylon_melt @@ -26,7 +26,7 @@ read_data large_nylon_melt.data.gz & extra/angle/per/atom 15 & extra/dihedral/per/atom 15 & extra/improper/per/atom 25 & - extra/special/per/atom 25 + extra/special/per/atom 25 velocity all create 800.0 4928459 dist gaussian @@ -50,7 +50,7 @@ fix 1 statted_grp_REACT nvt temp 800 800 100 # you can use the internally created 'bond_react_MASTER_group', like so: # fix 2 bond_react_MASTER_group temp/rescale 1 800 800 10 1 -thermo_style custom step temp press density f_myrxns[1] f_myrxns[2] # cumulative reaction counts +thermo_style custom step temp press density f_myrxns[*] # cumulative reaction counts # restart 100 restart1 restart2 diff --git a/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized b/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized index 57b03b630f..ea09d06893 100644 --- a/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized +++ b/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized @@ -44,7 +44,7 @@ fix rxns all bond/react stabilization yes statted_grp .03 & fix 1 statted_grp_REACT nvt temp 300 300 100 -thermo_style custom step temp f_rxns[1] f_rxns[2] f_rxns[3] f_rxns[4] +thermo_style custom step temp f_rxns[*] run 2000 diff --git a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized index 95b39033db..853bc45f1e 100644 --- a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized +++ b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized @@ -50,7 +50,7 @@ fix 1 statted_grp_REACT nvt temp 300 300 100 # by using the internally-created 'bond_react_MASTER_group', like so: fix 4 bond_react_MASTER_group temp/rescale 1 300 300 10 1 -thermo_style custom step temp press density f_myrxns[1] f_myrxns[2] +thermo_style custom step temp press density f_myrxns[*] # restart 100 restart1 restart2 diff --git a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized_variable_probability b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized_variable_probability index 88b5a95a41..f3c32f3cbd 100644 --- a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized_variable_probability +++ b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.stabilized_variable_probability @@ -54,7 +54,7 @@ fix 1 statted_grp_REACT nvt temp 300 300 100 # by using the internally-created 'bond_react_MASTER_group', like so: fix 4 bond_react_MASTER_group temp/rescale 1 300 300 10 1 -thermo_style custom step temp press density v_prob1 v_prob2 f_myrxns[1] f_myrxns[2] +thermo_style custom step temp press density v_prob1 v_prob2 f_myrxns[*] # restart 100 restart1 restart2 diff --git a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.unstabilized b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.unstabilized index a569e28d43..e5cbaaaf86 100644 --- a/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.unstabilized +++ b/examples/PACKAGES/reaction/tiny_nylon/in.tiny_nylon.unstabilized @@ -47,7 +47,7 @@ fix myrxns all bond/react stabilization no & fix 1 all nve/limit .03 -thermo_style custom step temp press density f_myrxns[1] f_myrxns[2] +thermo_style custom step temp press density f_myrxns[*] # restart 100 restart1 restart2 diff --git a/examples/PACKAGES/reaction/tiny_polystyrene/in.tiny_polystyrene.stabilized b/examples/PACKAGES/reaction/tiny_polystyrene/in.tiny_polystyrene.stabilized index 4ecc481719..230998fcd3 100644 --- a/examples/PACKAGES/reaction/tiny_polystyrene/in.tiny_polystyrene.stabilized +++ b/examples/PACKAGES/reaction/tiny_polystyrene/in.tiny_polystyrene.stabilized @@ -51,7 +51,7 @@ fix 1 statted_grp_REACT nvt temp $T $T 100 fix 4 bond_react_MASTER_group temp/rescale 1 $T $T 1 1 -thermo_style custom step temp press density f_rxn1[1] f_rxn1[2] f_rxn1[3] +thermo_style custom step temp press density f_rxn1[*] run 10000 From 1039f86037f2870062a0a79108a933ab96063494 Mon Sep 17 00:00:00 2001 From: jrgissing Date: Sat, 26 Aug 2023 14:29:07 -0400 Subject: [PATCH 032/305] remove unnecessary restriction do not check for comm cutoff when initiator atoms are directly bonded --- src/REACTION/fix_bond_react.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index 9254c3f5d1..1da26e32a1 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -827,11 +827,10 @@ void FixBondReact::init() nlevels_respa = (dynamic_cast(update->integrate))->nlevels; // check cutoff for iatomtype,jatomtype - for (int i = 0; i < nreacts; i++) { - if (!utils::strmatch(force->pair_style,"^hybrid")) - if (force->pair == nullptr || cutsq[i][1] > force->pair->cutsq[iatomtype[i]][jatomtype[i]]) + if (!utils::strmatch(force->pair_style,"^hybrid")) + for (int i = 0; i < nreacts; i++) + if (force->pair == nullptr || (closeneigh[i] < 0 && cutsq[i][1] > force->pair->cutsq[iatomtype[i]][jatomtype[i]])) error->all(FLERR,"Fix bond/react: Fix bond/react cutoff is longer than pairwise cutoff"); - } // need a half neighbor list, built every Nevery steps neighbor->add_request(this, NeighConst::REQ_OCCASIONAL); From b81df7c21b6be9739f234d4dc9c73272b38cde9c Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:12:09 +0300 Subject: [PATCH 033/305] Include methods and variables for dihedral contribution to compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index c7214c055c..b9b97617c0 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,12 +40,13 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double origin, delta, offset, invdelta; int nbins; @@ -53,6 +54,7 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; + double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From e819b38a1831a56839cc13be2877099244ca4afe Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:15:12 +0300 Subject: [PATCH 034/305] undo dihedral changes in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index b9b97617c0..c7214c055c 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,13 +40,12 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); - void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag, dihedralflag; + int bondflag, angleflag; double origin, delta, offset, invdelta; int nbins; @@ -54,7 +53,6 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; - double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From 381d8de017c37794682e77a3825f4e1b9ddd4458 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:22:09 +0300 Subject: [PATCH 035/305] initialization of angleflag in constructor compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 2a82cc0f8b..3dfc9326ac 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -49,6 +49,7 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a if (narg < 7) utils::missing_cmd_args(FLERR, "compute stress/mop/profile", error); bondflag = 0; + angleflag = 0; // set compute mode and direction of plane(s) for pressure calculation From ec458e2861b5b4950bfcd9a9dfb67bf1c636c319 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:32:26 +0300 Subject: [PATCH 036/305] method and member variables definition for dihedrals in compute_stress_mop_profile.h --- src/EXTRA-COMPUTE/compute_stress_mop_profile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h index c7214c055c..b9b97617c0 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.h @@ -40,12 +40,13 @@ class ComputeStressMopProfile : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); void setup_bins(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double origin, delta, offset, invdelta; int nbins; @@ -53,6 +54,7 @@ class ComputeStressMopProfile : public Compute { double **values_local, **values_global; double **bond_local, **bond_global; double **angle_local, **angle_global; + double **dihedral_local, **dihedral_global; double **local_contribution; double dt, nktv2p, ftm2v; From d40fb4a337c67c2f0c3bc79bf29b87353cd80ceb Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:45:46 +0300 Subject: [PATCH 037/305] method implementation for dihedral contribution to compute_stress_mop_profile.cpp --- .../compute_stress_mop_profile.cpp | 361 +++++++++++++++++- 1 file changed, 354 insertions(+), 7 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 3dfc9326ac..27e420e71d 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -13,7 +13,7 @@ /*------------------------------------------------------------------------ Contributing Authors : Romain Vermorel (LFCR), Laurent Joly (ULyon) - Support for bonds and angles added by : Evangelos Voyiatzis (NovaMechanics) + Support for bonds, angles and dihedrals added by : Evangelos Voyiatzis (NovaMechanics) --------------------------------------------------------------------------*/ #include "compute_stress_mop_profile.h" @@ -23,6 +23,7 @@ #include "atom_vec.h" #include "bond.h" #include "comm.h" +#include "dihedral.h" #include "domain.h" #include "error.h" #include "force.h" @@ -38,8 +39,10 @@ using namespace LAMMPS_NS; +#define SMALL 0.001 + enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE, DIHEDRAL }; /* ---------------------------------------------------------------------- */ @@ -50,6 +53,7 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a bondflag = 0; angleflag = 0; + dihedralflag = 0; // set compute mode and direction of plane(s) for pressure calculation @@ -114,6 +118,11 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a which[nvalues] = ANGLE; nvalues++; } + } else if (strcmp(arg[iarg],"dihedral") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = DIHEDRAL; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop/profile command"); //break; @@ -141,6 +150,8 @@ ComputeStressMopProfile::ComputeStressMopProfile(LAMMPS *lmp, int narg, char **a bond_global = nullptr; angle_local = nullptr; angle_global = nullptr; + dihedral_local = nullptr; + dihedral_global = nullptr; local_contribution = nullptr; // bin setup @@ -171,6 +182,8 @@ ComputeStressMopProfile::~ComputeStressMopProfile() memory->destroy(bond_global); memory->destroy(angle_local); memory->destroy(angle_global); + memory->destroy(dihedral_local); + memory->destroy(dihedral_global); memory->destroy(local_contribution); memory->destroy(array); } @@ -227,10 +240,16 @@ void ComputeStressMopProfile::init() } } - if (force->dihedral) - if ((strcmp(force->dihedral_style, "zero") != 0) && - (strcmp(force->dihedral_style, "none") != 0)) - error->all(FLERR, "compute stress/mop/profile does not account for dihedral potentials"); + if (force->dihedral) { + if (force->dihedral->born_matrix_enable == 0) { + if ((strcmp(force->dihedral_style, "zero") != 0) && + (strcmp(force->dihedral_style, "none") != 0)) + error->all(FLERR, "compute stress/mop/profile does not account for dihedral potentials"); + } else { + dihedralflag = 1; + } + } + if (force->improper) if ((strcmp(force->improper_style, "zero") != 0) && (strcmp(force->improper_style, "none") != 0)) @@ -294,6 +313,17 @@ void ComputeStressMopProfile::compute_array() // sum angle contribution over all procs MPI_Allreduce(&angle_local[0][0],&angle_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + + if (dihedralflag) { + //Compute dihedral contribution on separate procs + compute_dihedrals(); + } else { + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + dihedral_local[m][i] = 0.0; + } + } + } for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; @@ -301,7 +331,7 @@ void ComputeStressMopProfile::compute_array() int mo = 1; int m = 0; while (m < nvalues) { - array[ibin][m + mo] = values_global[ibin][m] + bond_global[ibin][m] + angle_global[ibin][m]; + array[ibin][m + mo] = values_global[ibin][m] + bond_global[ibin][m] + angle_global[ibin][m] + dihedral_global[ibin][m]; m++; } } @@ -835,6 +865,321 @@ void ComputeStressMopProfile::compute_angles() } } +/*------------------------------------------------------------------------ + compute dihedral contribution to pressure of local proc + -------------------------------------------------------------------------*/ + +void ComputeStressMopProfile::compute_dihedrals() +{ + int i, nd, atom1, atom2, atom3, atom4, imol, iatom; + tagint tagprev; + double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z; + double vb2xm, vb2ym, vb2zm; + double sb1, sb2, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; + double b2mag, b3mag2, b3mag, c2mag, ctmp, r12c1, c1mag, r12c2; + double s1, s2, s12, sc1, sc2, a11, a22, a33, a12, a13, a23; + double df[3], f1[3], f2[3], f3[3], f4[3]; + double c, sx2, sy2, sz2, sin2; + + double **x = atom->x; + tagint *tag = atom->tag; + int *num_dihedral = atom->num_dihedral; + tagint **dihedral_atom1 = atom->dihedral_atom1; + tagint **dihedral_atom2 = atom->dihedral_atom2; + tagint **dihedral_atom3 = atom->dihedral_atom3; + tagint **dihedral_atom4 = atom->dihedral_atom4; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their dihedrals + + Dihedral *dihedral = force->dihedral; + + double dudih, du2dih; + + double diffx[3] = {0.0, 0.0, 0.0}; + double x_atom_1[3] = {0.0, 0.0, 0.0}; + double x_atom_2[3] = {0.0, 0.0, 0.0}; + double x_atom_3[3] = {0.0, 0.0, 0.0}; + double x_atom_4[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int m = 0; m < nbins; m++) { + for (int i = 0; i < nvalues; i++) { + dihedral_local[m][i] = 0.0; + } + local_contribution[m][0] = 0.0; + local_contribution[m][1] = 0.0; + local_contribution[m][2] = 0.0; + } + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == Atom::MOLECULAR) + nd = num_dihedral[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + nd = onemols[imol]->num_dihedral[iatom]; + } + + for (i = 0; i < nd; i++) { + if (molecular == 1) { + if (tag[atom2] != dihedral_atom2[atom2][i]) continue; + atom1 = atom->map(dihedral_atom1[atom2][i]); + atom3 = atom->map(dihedral_atom3[atom2][i]); + atom4 = atom->map(dihedral_atom4[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->dihedral_atom2[atom2][i]) continue; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->dihedral_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->dihedral_atom3[atom2][i] + tagprev); + atom4 = atom->map(onemols[imol]->dihedral_atom4[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atom4 < 0 || !(mask[atom4] & groupbit)) continue; + + for (int ibin = 0; ibinminimum_image(x_atom_1[0], x_atom_1[1], x_atom_1[2]); + x_atom_1[dir] += pos; + + // minimum image of atom2 with respect to atom1 + diffx[0] = x[atom2][0] - x_atom_1[0]; + diffx[1] = x[atom2][1] - x_atom_1[1]; + diffx[2] = x[atom2][2] - x_atom_1[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_2[0] = x_atom_1[0] + diffx[0]; + x_atom_2[1] = x_atom_1[1] + diffx[1]; + x_atom_2[2] = x_atom_1[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom3][0] - x_atom_2[0]; + diffx[1] = x[atom3][1] - x_atom_2[1]; + diffx[2] = x[atom3][2] - x_atom_2[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_3[0] = x_atom_2[0] + diffx[0]; + x_atom_3[1] = x_atom_2[1] + diffx[1]; + x_atom_3[2] = x_atom_2[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom4][0] - x_atom_3[0]; + diffx[1] = x[atom4][1] - x_atom_3[1]; + diffx[2] = x[atom4][2] - x_atom_3[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_4[0] = x_atom_3[0] + diffx[0]; + x_atom_4[1] = x_atom_3[1] + diffx[1]; + x_atom_4[2] = x_atom_3[2] + diffx[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_atom_2[dir] - pos) / (x_atom_2[dir] - x_atom_1[dir]); + double tau_middle = (x_atom_3[dir] - pos) / (x_atom_3[dir] - x_atom_2[dir]); + double tau_left = (x_atom_4[dir] - pos) / (x_atom_4[dir] - x_atom_3[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool middle_cross = ((tau_middle >= 0) && (tau_middle <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !middle_cross && !left_cross) continue; + + dihedral->born_matrix(i, atom1, atom2, atom3, atom4, dudih, du2dih); + + // first bond + vb1x = x_atom_1[0] - x_atom_2[0]; + vb1y = x_atom_1[1] - x_atom_2[1]; + vb1z = x_atom_1[2] - x_atom_2[2]; + + // second bond + vb2x = x_atom_3[0] - x_atom_2[0]; + vb2y = x_atom_3[1] - x_atom_2[1]; + vb2z = x_atom_3[2] - x_atom_2[2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // third bond + vb3x = x_atom_4[0] - x_atom_3[0]; + vb3y = x_atom_4[1] - x_atom_3[1]; + vb3z = x_atom_4[2] - x_atom_3[2]; + + // c0 calculation + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + // 1st and 2nd angle + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // forces on each particle + double a = dudih; + c = c * a; + s12 = s12 * a; + a11 = c*sb1*s1; + a22 = -sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = c*sb3*s2; + a12 = -r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = -rb1*rb3*s12; + a23 = r12c2 * (c2mag*c*s2 + c1mag*s12); + + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + + f1[0] = a11*vb1x + a12*vb2x + a13*vb3x; + f1[1] = a11*vb1y + a12*vb2y + a13*vb3y; + f1[2] = a11*vb1z + a12*vb2z + a13*vb3z; + + f2[0] = -sx2 - f1[0]; + f2[1] = -sy2 - f1[1]; + f2[2] = -sz2 - f1[2]; + + f4[0] = a13*vb1x + a23*vb2x + a33*vb3x; + f4[1] = a13*vb1y + a23*vb2y + a33*vb3y; + f4[2] = a13*vb1z + a23*vb2z + a33*vb3z; + + f3[0] = sx2 - f4[0]; + f3[1] = sy2 - f4[1]; + f3[2] = sz2 - f4[2]; + + // only right bond crossing the plane + if (right_cross && !middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * f1[0]; + df[1] = sgn * f1[1]; + df[2] = sgn * f1[2]; + } + + // only middle bond crossing the plane + if (!right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * (f2[0] + f1[0]); + df[1] = sgn * (f2[1] + f1[1]); + df[2] = sgn * (f2[2] + f1[2]); + } + + // only left bond crossing the plane + if (!right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_4[dir] - pos); + df[0] = sgn * f4[0]; + df[1] = sgn * f4[1]; + df[2] = sgn * f4[2]; + } + + // only right & middle bonds crossing the plane + if (right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * f2[0]; + df[1] = sgn * f2[1]; + df[2] = sgn * f2[2]; + } + + // only right & left bonds crossing the plane + if (right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f4[0]); + df[1] = sgn * (f1[1] + f4[1]); + df[2] = sgn * (f1[2] + f4[2]); + } + + // only middle & left bonds crossing the plane + if (!right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_3[dir] - pos); + df[0] = sgn * f3[0]; + df[1] = sgn * f3[1]; + df[2] = sgn * f3[2]; + } + + // all three bonds crossing the plane + if (right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f3[0]); + df[1] = sgn * (f1[1] + f3[1]); + df[2] = sgn * (f1[2] + f3[2]); + } + + local_contribution[ibin][0] += df[0]/area*nktv2p; + local_contribution[ibin][1] += df[1]/area*nktv2p; + local_contribution[ibin][2] += df[2]/area*nktv2p; + } + } + } + + // loop over the keywords and if necessary add the dihedral contribution + int m = 0; + while (m < nvalues) { + if ((which[m] == CONF) || (which[m] == TOTAL) || (which[m] == DIHEDRAL)) { + for (int ibin = 0; ibin < nbins; ibin++) { + dihedral_local[ibin][m] = local_contribution[ibin][0]; + dihedral_local[ibin][m+1] = local_contribution[ibin][1]; + dihedral_local[ibin][m+2] = local_contribution[ibin][2]; + } + } + m += 3; + } + +} + /* ---------------------------------------------------------------------- setup 1d bins and their extent and coordinates called at init() @@ -870,6 +1215,8 @@ void ComputeStressMopProfile::setup_bins() memory->create(bond_global, nbins, nvalues, "stress/mop/profile:bond_global"); memory->create(angle_local, nbins, nvalues, "stress/mop/profile:angle_local"); memory->create(angle_global, nbins, nvalues, "stress/mop/profile:angle_global"); + memory->create(dihedral_local,nbins,nvalues,"stress/mop/profile:dihedral_local"); + memory->create(dihedral_global,nbins,nvalues,"stress/mop/profile:dihedral_global"); memory->create(local_contribution, nbins, 3, "stress/mop/profile:local_contribution"); // set bin coordinates From b86d1f655381c076a6b378bc8ea8f46aedf5c6de Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:48:45 +0300 Subject: [PATCH 038/305] Update compute_stress_mop.rst for dihedral interactions --- doc/src/compute_stress_mop.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 70986862fe..3cd7a67c9c 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -18,7 +18,7 @@ Syntax * style = *stress/mop* or *stress/mop/profile* * dir = *x* or *y* or *z* is the direction normal to the plane * args = argument specific to the compute style -* keywords = *kin* or *conf* or *total* or *pair* or *bond* or *angle* (one or more can be specified) +* keywords = *kin* or *conf* or *total* or *pair* or *bond* or *angle* or *dihedral* (one or more can be specified) .. parsed-literal:: @@ -68,13 +68,13 @@ Verlet algorithm. .. versionadded:: 15Jun2023 - contributions from bond and angle potentials + contributions from bond, angle and dihedral potentials -Between one and six keywords can be used to indicate which contributions +Between one and seven keywords can be used to indicate which contributions to the stress must be computed: total stress (total), kinetic stress (kin), configurational stress (conf), stress due to bond stretching -(bond), stress due to angle bending (angle) and/or due to pairwise -non-bonded interactions (pair). +(bond), stress due to angle bending (angle), stress due to dihedral terms (dihedral) +and/or due to pairwise non-bonded interactions (pair). NOTE 1: The configurational stress is computed considering all pairs of atoms where at least one atom belongs to group group-ID. @@ -134,7 +134,7 @@ requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute *stress/mop/profile* does not work with more than two-body pair interactions, long range (kspace) interactions and -dihedral/improper intramolecular interactions. Similarly, compute +improper intramolecular interactions. Similarly, compute *stress/mop* does not work with more than two-body pair interactions, long range (kspace) interactions and dihedral/improper intramolecular interactions but works with all bond interactions with the class method From 1591b21617a196607306955e7d108f38b330ed1d Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Tue, 26 Sep 2023 16:53:16 +0300 Subject: [PATCH 039/305] remove whitespaces from compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 27e420e71d..223e4da66a 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -324,7 +324,7 @@ void ComputeStressMopProfile::compute_array() } } } - + for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; @@ -540,7 +540,7 @@ void ComputeStressMopProfile::compute_pairs() vcross[0] = vi[0] - fi[0] * iterm; vcross[1] = vi[1] - fi[1] * iterm; vcross[2] = vi[2] - fi[2] * iterm; - + values_local[ibin][m] += imass * vcross[0] * sgn / dt / area * nktv2p / ftm2v; values_local[ibin][m + 1] += imass * vcross[1] * sgn / dt / area * nktv2p / ftm2v; values_local[ibin][m + 2] += imass * vcross[2] * sgn / dt / area * nktv2p / ftm2v; From 3445330cf1679addfdce5ab3e678b54669dc933d Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:13:13 +0300 Subject: [PATCH 040/305] remove whitespace from compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index a1077660e5..3f1ae008ea 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -433,7 +433,7 @@ void ComputeStressMop::compute_pairs() xi[dir] -= pos; domain->minimum_image(xi[0], xi[1], xi[2]); xi[dir] += pos; - + //velocities at t vi[0] = atom->v[i][0]; From ac435319fdf8b127ac33cfdf24ea6b8bfcc4649b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:15:03 +0300 Subject: [PATCH 041/305] Definition of compute_dihedral and related variables in compute_stress_mop.h --- src/EXTRA-COMPUTE/compute_stress_mop.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.h b/src/EXTRA-COMPUTE/compute_stress_mop.h index 86140dc278..0a0ea8b55a 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.h +++ b/src/EXTRA-COMPUTE/compute_stress_mop.h @@ -40,15 +40,17 @@ class ComputeStressMop : public Compute { void compute_pairs(); void compute_bonds(); void compute_angles(); + void compute_dihedrals(); int nvalues, dir; int *which; - int bondflag, angleflag; + int bondflag, angleflag, dihedralflag; double *values_local, *values_global; double *bond_local, *bond_global; double *angle_local, *angle_global; + double *dihedral_local, *dihedral_global; double pos, pos1, dt, nktv2p, ftm2v; double area; class NeighList *list; From ca449f1ea859d47e665ae98e5a58cf8aa9781e60 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:25:52 +0300 Subject: [PATCH 042/305] Prepare for inclusion of dihedral contribution in compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 47 +++++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 3f1ae008ea..d94ba61d71 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -23,6 +23,7 @@ #include "atom_vec.h" #include "bond.h" #include "comm.h" +#include "dihedral.h" #include "domain.h" #include "error.h" #include "force.h" @@ -38,8 +39,10 @@ using namespace LAMMPS_NS; +#define SMALL 0.001 + enum { X, Y, Z }; -enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE }; +enum { TOTAL, CONF, KIN, PAIR, BOND, ANGLE, DIHEDRAL }; /* ---------------------------------------------------------------------- */ @@ -49,6 +52,7 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( bondflag = 0; angleflag = 0; + dihedralflag = 0; // set compute mode and direction of plane(s) for pressure calculation @@ -129,6 +133,11 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( which[nvalues] = ANGLE; nvalues++; } + } else if (strcmp(arg[iarg],"dihedral") == 0) { + for (i=0; i<3; i++) { + which[nvalues] = DIHEDRAL; + nvalues++; + } } else error->all(FLERR, "Illegal compute stress/mop command"); //break; @@ -152,6 +161,8 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( bond_global = nullptr; angle_local = nullptr; angle_global = nullptr; + dihedral_local = nullptr; + dihedral_global = nullptr; // this fix produces a global vector @@ -162,6 +173,8 @@ ComputeStressMop::ComputeStressMop(LAMMPS *lmp, int narg, char **arg) : Compute( memory->create(bond_global, nvalues, "stress/mop:bond_global"); memory->create(angle_local, nvalues, "stress/mop:angle_local"); memory->create(angle_global, nvalues, "stress/mop:angle_global"); + memory->create(dihedral_local,nvalues,"stress/mop:dihedral_local"); + memory->create(dihedral_global,nvalues,"stress/mop:dihedral_global"); size_vector = nvalues; vector_flag = 1; @@ -180,6 +193,8 @@ ComputeStressMop::~ComputeStressMop() memory->destroy(bond_global); memory->destroy(angle_local); memory->destroy(angle_global); + memory->destroy(dihedral_local); + memory->destroy(dihedral_global); memory->destroy(vector); } @@ -233,9 +248,13 @@ void ComputeStressMop::init() } } if (force->dihedral) { - if ((strcmp(force->dihedral_style, "zero") != 0) && - (strcmp(force->dihedral_style, "none") != 0)) - error->all(FLERR, "compute stress/mop does not account for dihedral potentials"); + if (force->dihedral->born_matrix_enable == 0) { + if ((strcmp(force->dihedral_style, "zero") != 0) && + (strcmp(force->dihedral_style, "none") != 0)) + error->all(FLERR, "compute stress/mop does not account for dihedral potentials"); + } else { + dihedralflag = 1; + } } if (force->improper) { if ((strcmp(force->improper_style, "zero") != 0) && @@ -297,8 +316,18 @@ void ComputeStressMop::compute_vector() MPI_Allreduce(angle_local, angle_global, nvalues, MPI_DOUBLE, MPI_SUM, world); + if (dihedralflag) { + //Compute dihedral contribution on separate procs + compute_dihedrals(); + } else { + for (int i=0; i Date: Thu, 28 Sep 2023 16:29:15 +0300 Subject: [PATCH 043/305] remove whitespace from compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index d94ba61d71..331f4e4841 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -325,7 +325,7 @@ void ComputeStressMop::compute_vector() // sum dihedral contribution over all procs MPI_Allreduce(dihedral_local,dihedral_global,nvalues,MPI_DOUBLE,MPI_SUM,world); - + for (int m = 0; m < nvalues; m++) { vector[m] = values_global[m] + bond_global[m] + angle_global[m] + dihedral_global[m]; } From bbd6b2846f83cef48ed3960fe2496c64ee37f99e Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 16:59:03 +0300 Subject: [PATCH 044/305] implementation of compute_dihedral() in compute_stress_mop.cpp --- src/EXTRA-COMPUTE/compute_stress_mop.cpp | 297 +++++++++++++++++++++++ 1 file changed, 297 insertions(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop.cpp b/src/EXTRA-COMPUTE/compute_stress_mop.cpp index 331f4e4841..6c35b4ba07 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop.cpp @@ -825,4 +825,301 @@ void ComputeStressMop::compute_angles() void ComputeStressMop::compute_dihedrals() { + int i, nd, atom1, atom2, atom3, atom4, imol, iatom; + tagint tagprev; + double vb1x, vb1y, vb1z, vb2x, vb2y, vb2z, vb3x, vb3y, vb3z; + double vb2xm, vb2ym, vb2zm; + double sb1, sb2, sb3, rb1, rb3, c0, b1mag2, b1mag, b2mag2; + double b2mag, b3mag2, b3mag, c2mag, ctmp, r12c1, c1mag, r12c2; + double s1, s2, s12, sc1, sc2, a11, a22, a33, a12, a13, a23; + double df[3], f1[3], f2[3], f3[3], f4[3]; + double c, sx2, sy2, sz2, sin2; + + double **x = atom->x; + tagint *tag = atom->tag; + int *num_dihedral = atom->num_dihedral; + tagint **dihedral_atom1 = atom->dihedral_atom1; + tagint **dihedral_atom2 = atom->dihedral_atom2; + tagint **dihedral_atom3 = atom->dihedral_atom3; + tagint **dihedral_atom4 = atom->dihedral_atom4; + int *mask = atom->mask; + + int *molindex = atom->molindex; + int *molatom = atom->molatom; + Molecule **onemols = atom->avec->onemols; + + int nlocal = atom->nlocal; + int molecular = atom->molecular; + + // loop over all atoms and their dihedrals + + Dihedral *dihedral = force->dihedral; + + double dudih, du2dih; + + double diffx[3] = {0.0, 0.0, 0.0}; + double x_atom_1[3] = {0.0, 0.0, 0.0}; + double x_atom_2[3] = {0.0, 0.0, 0.0}; + double x_atom_3[3] = {0.0, 0.0, 0.0}; + double x_atom_4[3] = {0.0, 0.0, 0.0}; + + // initialization + for (int i = 0; i < nvalues; i++) { + dihedral_local[i] = 0.0; + } + double local_contribution[3] = {0.0, 0.0, 0.0}; + + for (atom2 = 0; atom2 < nlocal; atom2++) { + if (!(mask[atom2] & groupbit)) continue; + + if (molecular == Atom::MOLECULAR) + nd = num_dihedral[atom2]; + else { + if (molindex[atom2] < 0) continue; + imol = molindex[atom2]; + iatom = molatom[atom2]; + nd = onemols[imol]->num_dihedral[iatom]; + } + + for (i = 0; i < nd; i++) { + if (molecular == 1) { + if (tag[atom2] != dihedral_atom2[atom2][i]) continue; + atom1 = atom->map(dihedral_atom1[atom2][i]); + atom3 = atom->map(dihedral_atom3[atom2][i]); + atom4 = atom->map(dihedral_atom4[atom2][i]); + } else { + if (tag[atom2] != onemols[imol]->dihedral_atom2[atom2][i]) continue; + tagprev = tag[atom2] - iatom - 1; + atom1 = atom->map(onemols[imol]->dihedral_atom1[atom2][i] + tagprev); + atom3 = atom->map(onemols[imol]->dihedral_atom3[atom2][i] + tagprev); + atom4 = atom->map(onemols[imol]->dihedral_atom4[atom2][i] + tagprev); + } + + if (atom1 < 0 || !(mask[atom1] & groupbit)) continue; + if (atom3 < 0 || !(mask[atom3] & groupbit)) continue; + if (atom4 < 0 || !(mask[atom4] & groupbit)) continue; + + // minimum image of atom1 with respect to the plane of interest + x_atom_1[0] = x[atom1][0]; + x_atom_1[1] = x[atom1][1]; + x_atom_1[2] = x[atom1][2]; + x_atom_1[dir] -= pos; + domain->minimum_image(x_atom_1[0], x_atom_1[1], x_atom_1[2]); + x_atom_1[dir] += pos; + + // minimum image of atom2 with respect to atom1 + diffx[0] = x[atom2][0] - x_atom_1[0]; + diffx[1] = x[atom2][1] - x_atom_1[1]; + diffx[2] = x[atom2][2] - x_atom_1[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_2[0] = x_atom_1[0] + diffx[0]; + x_atom_2[1] = x_atom_1[1] + diffx[1]; + x_atom_2[2] = x_atom_1[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom3][0] - x_atom_2[0]; + diffx[1] = x[atom3][1] - x_atom_2[1]; + diffx[2] = x[atom3][2] - x_atom_2[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_3[0] = x_atom_2[0] + diffx[0]; + x_atom_3[1] = x_atom_2[1] + diffx[1]; + x_atom_3[2] = x_atom_2[2] + diffx[2]; + + // minimum image of atom3 with respect to atom2 + diffx[0] = x[atom4][0] - x_atom_3[0]; + diffx[1] = x[atom4][1] - x_atom_3[1]; + diffx[2] = x[atom4][2] - x_atom_3[2]; + domain->minimum_image(diffx[0], diffx[1], diffx[2]); + x_atom_4[0] = x_atom_3[0] + diffx[0]; + x_atom_4[1] = x_atom_3[1] + diffx[1]; + x_atom_4[2] = x_atom_3[2] + diffx[2]; + + // check if any bond vector crosses the plane of interest + double tau_right = (x_atom_2[dir] - pos) / (x_atom_2[dir] - x_atom_1[dir]); + double tau_middle = (x_atom_3[dir] - pos) / (x_atom_3[dir] - x_atom_2[dir]); + double tau_left = (x_atom_4[dir] - pos) / (x_atom_4[dir] - x_atom_3[dir]); + bool right_cross = ((tau_right >=0) && (tau_right <= 1)); + bool middle_cross = ((tau_middle >= 0) && (tau_middle <= 1)); + bool left_cross = ((tau_left >=0) && (tau_left <= 1)); + + // no bonds crossing the plane + if (!right_cross && !middle_cross && !left_cross) continue; + + dihedral->born_matrix(i, atom1, atom2, atom3, atom4, dudih, du2dih); + + // first bond + vb1x = x_atom_1[0] - x_atom_2[0]; + vb1y = x_atom_1[1] - x_atom_2[1]; + vb1z = x_atom_1[2] - x_atom_2[2]; + + // second bond + vb2x = x_atom_3[0] - x_atom_2[0]; + vb2y = x_atom_3[1] - x_atom_2[1]; + vb2z = x_atom_3[2] - x_atom_2[2]; + + vb2xm = -vb2x; + vb2ym = -vb2y; + vb2zm = -vb2z; + + // third bond + vb3x = x_atom_4[0] - x_atom_3[0]; + vb3y = x_atom_4[1] - x_atom_3[1]; + vb3z = x_atom_4[2] - x_atom_3[2]; + + // c0 calculation + sb1 = 1.0 / (vb1x*vb1x + vb1y*vb1y + vb1z*vb1z); + sb2 = 1.0 / (vb2x*vb2x + vb2y*vb2y + vb2z*vb2z); + sb3 = 1.0 / (vb3x*vb3x + vb3y*vb3y + vb3z*vb3z); + + rb1 = sqrt(sb1); + rb3 = sqrt(sb3); + + c0 = (vb1x*vb3x + vb1y*vb3y + vb1z*vb3z) * rb1*rb3; + // 1st and 2nd angle + b1mag2 = vb1x*vb1x + vb1y*vb1y + vb1z*vb1z; + b1mag = sqrt(b1mag2); + b2mag2 = vb2x*vb2x + vb2y*vb2y + vb2z*vb2z; + b2mag = sqrt(b2mag2); + b3mag2 = vb3x*vb3x + vb3y*vb3y + vb3z*vb3z; + b3mag = sqrt(b3mag2); + + ctmp = vb1x*vb2x + vb1y*vb2y + vb1z*vb2z; + r12c1 = 1.0 / (b1mag*b2mag); + c1mag = ctmp * r12c1; + + ctmp = vb2xm*vb3x + vb2ym*vb3y + vb2zm*vb3z; + r12c2 = 1.0 / (b2mag*b3mag); + c2mag = ctmp * r12c2; + + // cos and sin of 2 angles and final c + sin2 = MAX(1.0 - c1mag*c1mag,0.0); + sc1 = sqrt(sin2); + if (sc1 < SMALL) sc1 = SMALL; + sc1 = 1.0/sc1; + + sin2 = MAX(1.0 - c2mag*c2mag,0.0); + sc2 = sqrt(sin2); + if (sc2 < SMALL) sc2 = SMALL; + sc2 = 1.0/sc2; + + s1 = sc1 * sc1; + s2 = sc2 * sc2; + s12 = sc1 * sc2; + c = (c0 + c1mag*c2mag) * s12; + + // error check + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + // forces on each particle + double a = dudih; + c = c * a; + s12 = s12 * a; + a11 = c*sb1*s1; + a22 = -sb2 * (2.0*c0*s12 - c*(s1+s2)); + a33 = c*sb3*s2; + a12 = -r12c1 * (c1mag*c*s1 + c2mag*s12); + a13 = -rb1*rb3*s12; + a23 = r12c2 * (c2mag*c*s2 + c1mag*s12); + + sx2 = a12*vb1x + a22*vb2x + a23*vb3x; + sy2 = a12*vb1y + a22*vb2y + a23*vb3y; + sz2 = a12*vb1z + a22*vb2z + a23*vb3z; + + f1[0] = a11*vb1x + a12*vb2x + a13*vb3x; + f1[1] = a11*vb1y + a12*vb2y + a13*vb3y; + f1[2] = a11*vb1z + a12*vb2z + a13*vb3z; + + f2[0] = -sx2 - f1[0]; + f2[1] = -sy2 - f1[1]; + f2[2] = -sz2 - f1[2]; + + f4[0] = a13*vb1x + a23*vb2x + a33*vb3x; + f4[1] = a13*vb1y + a23*vb2y + a33*vb3y; + f4[2] = a13*vb1z + a23*vb2z + a33*vb3z; + + f3[0] = sx2 - f4[0]; + f3[1] = sy2 - f4[1]; + f3[2] = sz2 - f4[2]; + + // only right bond crossing the plane + if (right_cross && !middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * f1[0]; + df[1] = sgn * f1[1]; + df[2] = sgn * f1[2]; + } + + // only middle bond crossing the plane + if (!right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * (f2[0] + f1[0]); + df[1] = sgn * (f2[1] + f1[1]); + df[2] = sgn * (f2[2] + f1[2]); + } + + // only left bond crossing the plane + if (!right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_4[dir] - pos); + df[0] = sgn * f4[0]; + df[1] = sgn * f4[1]; + df[2] = sgn * f4[2]; + } + + // only right & middle bonds crossing the plane + if (right_cross && middle_cross && !left_cross) + { + double sgn = copysign(1.0, x_atom_2[dir] - pos); + df[0] = sgn * f2[0]; + df[1] = sgn * f2[1]; + df[2] = sgn * f2[2]; + } + + // only right & left bonds crossing the plane + if (right_cross && !middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f4[0]); + df[1] = sgn * (f1[1] + f4[1]); + df[2] = sgn * (f1[2] + f4[2]); + } + + // only middle & left bonds crossing the plane + if (!right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_3[dir] - pos); + df[0] = sgn * f3[0]; + df[1] = sgn * f3[1]; + df[2] = sgn * f3[2]; + } + + // all three bonds crossing the plane + if (right_cross && middle_cross && left_cross) + { + double sgn = copysign(1.0, x_atom_1[dir] - pos); + df[0] = sgn * (f1[0] + f3[0]); + df[1] = sgn * (f1[1] + f3[1]); + df[2] = sgn * (f1[2] + f3[2]); + } + + local_contribution[0] += df[0]/area*nktv2p; + local_contribution[1] += df[1]/area*nktv2p; + local_contribution[2] += df[2]/area*nktv2p; + } + } + + // loop over the keywords and if necessary add the dihedral contribution + int m = 0; + while (m < nvalues) { + if ((which[m] == CONF) || (which[m] == TOTAL) || (which[m] == DIHEDRAL)) { + dihedral_local[m] = local_contribution[0]; + dihedral_local[m+1] = local_contribution[1]; + dihedral_local[m+2] = local_contribution[2]; + } + m += 3; + } + } From dc84ab5e5fc826e8095eb7d2c2ec38628dc4c7f7 Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 17:06:11 +0300 Subject: [PATCH 045/305] Update compute_stress_mop.rst --- doc/src/compute_stress_mop.rst | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/src/compute_stress_mop.rst b/doc/src/compute_stress_mop.rst index 3cd7a67c9c..74d4c618e7 100644 --- a/doc/src/compute_stress_mop.rst +++ b/doc/src/compute_stress_mop.rst @@ -132,14 +132,9 @@ size does not change in time, and axis-aligned planes. The method only works with two-body pair interactions, because it requires the class method ``Pair::single()`` to be implemented, which is not possible for manybody potentials. In particular, compute -*stress/mop/profile* does not work with more than two-body pair +*stress/mop/profile* and *stress/mop* do not work with more than two-body pair interactions, long range (kspace) interactions and -improper intramolecular interactions. Similarly, compute -*stress/mop* does not work with more than two-body pair interactions, -long range (kspace) interactions and dihedral/improper intramolecular -interactions but works with all bond interactions with the class method -single() implemented and all angle interactions with the class method -born_matrix() implemented. +improper intramolecular interactions. Related commands """""""""""""""" From d84ee0c4f1f97728d69a55163d8356eba16e290b Mon Sep 17 00:00:00 2001 From: Evangelos Voyiatzis Date: Thu, 28 Sep 2023 17:48:17 +0300 Subject: [PATCH 046/305] Update compute_stress_mop_profile.cpp --- src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp index 223e4da66a..41b5f64a67 100644 --- a/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp +++ b/src/EXTRA-COMPUTE/compute_stress_mop_profile.cpp @@ -325,6 +325,9 @@ void ComputeStressMopProfile::compute_array() } } + // sum dihedral contribution over all procs + MPI_Allreduce(&dihedral_local[0][0],&dihedral_global[0][0],nbins*nvalues,MPI_DOUBLE,MPI_SUM,world); + for (int ibin = 0; ibin < nbins; ibin++) { array[ibin][0] = coord[ibin]; From 5830dec742dc5c847a72b4bb1e6c7b5fb2aea756 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 15:09:10 -0600 Subject: [PATCH 047/305] new compute: reaxff/bonds/local --- src/REAXFF/compute_reaxff_bonds_local.cpp | 217 ++++++++++++++++++++++ src/REAXFF/compute_reaxff_bonds_local.h | 60 ++++++ 2 files changed, 277 insertions(+) create mode 100644 src/REAXFF/compute_reaxff_bonds_local.cpp create mode 100644 src/REAXFF/compute_reaxff_bonds_local.h diff --git a/src/REAXFF/compute_reaxff_bonds_local.cpp b/src/REAXFF/compute_reaxff_bonds_local.cpp new file mode 100644 index 0000000000..c32b404e86 --- /dev/null +++ b/src/REAXFF/compute_reaxff_bonds_local.cpp @@ -0,0 +1,217 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_local.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "pair_reaxff.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +ComputeReaxFFBondsLocal::ComputeReaxFFBondsLocal(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), + alocal(nullptr), abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) +{ + if (atom->tag_consecutive() == 0) + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + + local_flag = 1; + + nvalues = 7 + 2*MAXREAXBOND; + prev_nvalues = 0; + + // initialize output + + nlocal = atom->nlocal; + + size_local_rows = atom->nlocal; + size_local_cols = 7 + 2*MAXREAXBOND; + + allocate(nlocal); +} + + +/* ---------------------------------------------------------------------- */ + +ComputeReaxFFBondsLocal::~ComputeReaxFFBondsLocal() +{ + memory->destroy(alocal); + destroy(); +} + +void ComputeReaxFFBondsLocal::destroy() +{ + memory->destroy(abo); + memory->destroy(neighid); + memory->destroy(numneigh); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::allocate(int n) +{ + memory->create(abo,n,MAXREAXBOND,"reaxff/bonds/local:abo"); + memory->create(neighid,n,MAXREAXBOND,"reaxff/bonds/local:neighid"); + memory->create(numneigh,n,"reaxff/bonds/local:numneigh"); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax..",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds/local without " + "pair_style reaxff, reaxff/kk, or reaxff/omp"); +} + +/* ---------------------------------------------------------------------- */ + +int ComputeReaxFFBondsLocal::FindBond() +{ + int *ilist, i, ii, inum; + int j, pj, nj; + tagint jtag; + double bo_tmp,bo_cut; + + inum = reaxff->list->inum; + ilist = reaxff->list->ilist; + bond_data *bo_ij; + bo_cut = reaxff->api->control->bg_cut; + + tagint *tag = atom->tag; + int numbonds = 0; + + for (ii = 0; ii < inum; ii++) { + i = ilist[ii]; + nj = 0; + + for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { + bo_ij = &(reaxff->api->lists->select.bond_list[pj]); + j = bo_ij->nbr; + jtag = tag[j]; + bo_tmp = bo_ij->bo_data.BO; + + if (bo_tmp > bo_cut) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + nj ++; + } + } + numneigh[i] = nj; + if (nj > numbonds) numbonds = nj; + } + return numbonds; +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBondsLocal::compute_local() +{ + invoked_local = update->ntimestep; + + // count local entries and compute bond info + if (atom->nlocal > nlocal) { + destroy(); + allocate(atom->nlocal); + } + + { + const int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + numneigh[i] = 0; + for (int j = 0; j < MAXREAXBOND; j++) { + neighid[i][j] = 0; + abo[i][j] = 0.0; + } + } + } + + int maxnumbonds = FindBond(); + nvalues = 7+2*maxnumbonds; + + if(atom->nlocal > nlocal || nvalues > prev_nvalues) { + reallocate(); + } + + size_local_rows = nlocal; + size_local_cols = nvalues; + + for (int i = 0; i < nlocal; ++i) { + auto ptr = alocal[i]; + int numbonds = numneigh[i]; + ptr[0] = atom->tag[i]; + ptr[1] = atom->type[i]; + ptr[2] = numbonds; + + int j = 3; + + for (int k = 0; k < numbonds; k++) { + ptr[j++] = neighid[i][k]; + } + + ptr[j++] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; + + for (int k = 0; k < numbonds; k++) { + ptr[j++] = abo[i][k]; + } + + ptr[j++] = reaxff->api->workspace->total_bond_order[i]; + ptr[j++] = reaxff->api->workspace->nlp[i]; + ptr[j++] = atom->q[i]; + + // clear any remaining + for(; j < nvalues; ++j) { + ptr[j] = 0.0; + } + } +} + +void ComputeReaxFFBondsLocal::reallocate() +{ + nlocal = atom->nlocal; + + // grow array_local + memory->destroy(alocal); + memory->create(alocal,nlocal,nvalues,"reaxff/bonds/local:array_local"); + array_local = alocal; + + prev_nvalues = nvalues; +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +double ComputeReaxFFBondsLocal::memory_usage() +{ + double bytes = (double)nlocal*nvalues * sizeof(double); + bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + bytes += (double)(nlocal) * sizeof(int); + return bytes; +} diff --git a/src/REAXFF/compute_reaxff_bonds_local.h b/src/REAXFF/compute_reaxff_bonds_local.h new file mode 100644 index 0000000000..2347db3a26 --- /dev/null +++ b/src/REAXFF/compute_reaxff_bonds_local.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#ifdef COMPUTE_CLASS +// clang-format off +ComputeStyle(reaxff/bonds/local,ComputeReaxFFBondsLocal); +// clang-format on +#else + +#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_H +#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_H + +#include "compute.h" + +namespace LAMMPS_NS { + +class ComputeReaxFFBondsLocal : public Compute { + public: + ComputeReaxFFBondsLocal(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsLocal() override; + void init() override; + void compute_local() override; + double memory_usage() override; + + private: + int nlocal; + int nvalues; + int prev_nvalues; + + double **alocal; + tagint **neighid; + double **abo; + int *numneigh; + class PairReaxFF *reaxff; + + int FindBond(); + + void allocate(int); + void destroy(); + void reallocate(); +}; + +} // namespace LAMMPS_NS + +#endif +#endif From bbc2794df29920bc1673ac0501518669fcfb822d Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 15:09:16 -0600 Subject: [PATCH 048/305] add reaxff/bonds/local/kk --- .../compute_reaxff_bonds_local_kokkos.cpp | 125 ++++++++++++++++++ .../compute_reaxff_bonds_local_kokkos.h | 68 ++++++++++ src/KOKKOS/pair_reaxff_kokkos.cpp | 55 ++++++++ src/KOKKOS/pair_reaxff_kokkos.h | 19 +++ 4 files changed, 267 insertions(+) create mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp create mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.h diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp new file mode 100644 index 0000000000..a56d243ed6 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp @@ -0,0 +1,125 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_local_kokkos.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "memory_kokkos.h" +#include "pair_reaxff_kokkos.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsLocalKokkos::ComputeReaxFFBondsLocalKokkos(LAMMPS *lmp, int narg, char **arg) : + Compute(lmp, narg, arg), + alocal(nullptr), reaxff(nullptr) +{ + if (atom->tag_consecutive() == 0) + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + + local_flag = 1; + + nvalues = 7 + 2*MAXREAXBOND; + prev_nvalues = 0; + + // initialize output + + nlocal = atom->nlocal; + + size_local_rows = atom->nlocal; + size_local_cols = 7 + 2*MAXREAXBOND; + printf("RUNNING KOKKOS VERSION\n"); +} + + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsLocalKokkos::~ComputeReaxFFBondsLocalKokkos() +{ + memoryKK->destroy_kokkos(k_alocal, alocal); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsLocalKokkos::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use fix reaxff/bonds without " + "pair_style reaxff/kk"); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsLocalKokkos::compute_local() +{ + invoked_local = update->ntimestep; + + int maxnumbonds = 0; + if (reaxff->execution_space == Device) + device_pair()->FindBond(maxnumbonds); + else + host_pair()->FindBond(maxnumbonds); + nvalues = 7+2*maxnumbonds; + + if(atom->nlocal > nlocal || nvalues > prev_nvalues) { + nlocal = atom->nlocal; + memoryKK->destroy_kokkos(k_alocal, alocal); + memoryKK->create_kokkos(k_alocal, alocal, atom->nlocal, nvalues,"reaxff/bonds/local:alocal"); + prev_nvalues = nvalues; + array_local = alocal; + } + + size_local_rows = nlocal; + size_local_cols = nvalues; + + if (reaxff->execution_space == Device) + device_pair()->PackBondInfo(k_alocal); + else + host_pair()->PackBondInfo(k_alocal); +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +template +double ComputeReaxFFBondsLocalKokkos::memory_usage() +{ + double bytes = (double)nlocal*nvalues * sizeof(double); + return bytes; +} + +namespace LAMMPS_NS { +template class ComputeReaxFFBondsLocalKokkos; +#ifdef LMP_KOKKOS_GPU +template class ComputeReaxFFBondsLocalKokkos; +#endif +} diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h new file mode 100644 index 0000000000..bfb5521199 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h @@ -0,0 +1,68 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#ifdef COMPUTE_CLASS +// clang-format off +ComputeStyle(reaxff/bonds/local/kk,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/local/kk/device,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/local/kk/host,ComputeReaxFFBondsLocalKokkos); +// clang-format on +#else + +#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H +#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H + +#include "compute_reaxff_bonds_local.h" +#include "pair_reaxff_kokkos.h" +#include "kokkos_type.h" + +namespace LAMMPS_NS { + +template +class ComputeReaxFFBondsLocalKokkos : public Compute { + public: + using device_type = DeviceType; + using AT = ArrayTypes; + + ComputeReaxFFBondsLocalKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsLocalKokkos() override; + void init() override; + void compute_local() override; + double memory_usage() override; + + private: + int nlocal; + int nvalues; + int prev_nvalues; + + double **alocal; + typename AT::tdual_float_2d k_alocal; + PairReaxFF *reaxff; + + auto device_pair() { + return dynamic_cast*>(reaxff); + } + + auto host_pair() { + return dynamic_cast*>(reaxff); + } +}; + +} // namespace LAMMPS_NS + +#endif +#endif diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index c7d54b80cd..fbf90f140d 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4290,6 +4290,61 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo /* ---------------------------------------------------------------------- */ +template +void PairReaxFFKokkos::PackBondInfo(DAT::tdual_float_2d k_alocal) +{ + d_alocal = k_alocal.view(); + k_params_sing.template sync(); + atomKK->sync(execution_space,TAG_MASK|TYPE_MASK|Q_MASK|MOLECULE_MASK); + + tag = atomKK->k_tag.view(); + type = atomKK->k_type.view(); + q = atomKK->k_q.view(); + if (atom->molecule) + molecule = atomKK->k_molecule.view(); + + copymode = 1; + nlocal = atomKK->nlocal; + PairReaxKokkosPackBondInfoFunctor pack_bond_info_functor(this); + Kokkos::parallel_for(nlocal, pack_bond_info_functor); + copymode = 0; + + k_alocal.modify(); + k_alocal.sync(); +} + +template +KOKKOS_INLINE_FUNCTION +void PairReaxFFKokkos::pack_bond_info_item(const int i) const +{ + const int numbonds = d_numneigh_bonds[i]; + d_alocal(i,0) = tag[i]; + d_alocal(i,1) = type[i]; + d_alocal(i,2) = numbonds; + + int j = 3; + + for (int k = 0; k < numbonds; k++) { + d_alocal(i,j++) = d_neighid(i,k); + } + + d_alocal(i,j++) = molecule.data() ? molecule[i] : 0.0; + + for (int k = 0; k < numbonds; k++) { + d_alocal(i,j++) = d_abo(i,k); + } + + d_alocal(i,j++) = d_total_bo[i]; + d_alocal(i,j++) = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; + d_alocal(i,j++) = q[i]; + + for(; j < d_alocal.extent(1); ++j) { + d_alocal(i,j) = 0.0; + } +} + +/* ---------------------------------------------------------------------- */ + template void PairReaxFFKokkos::FindBondSpecies() { diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 1ad0955a1e..32007e9970 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,6 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackBondInfo(DAT::tdual_float_2d); void FindBondSpecies(); template @@ -292,6 +293,9 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION + void pack_bond_info_item(const int) const; + KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -506,6 +510,8 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_ffloat_1d d_buf; DAT::tdual_int_scalar k_nbuf_local; + typename AT::t_float_2d d_alocal; + typedef Kokkos::View t_reax_int4_2d; t_reax_int4_2d d_angular_pack, d_torsion_pack; @@ -549,6 +555,19 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; +template +struct PairReaxKokkosPackBondInfoFunctor { + using device_type = DeviceType; + using value_type = int; + PairReaxFFKokkos c; + PairReaxKokkosPackBondInfoFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + + KOKKOS_INLINE_FUNCTION + void operator()(const int i) const { + c.pack_bond_info_item(i); + } +}; + } #endif From fea5f5a2438d5ef1c563e945783f93a9560a18f4 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 16 Oct 2023 16:52:34 -0600 Subject: [PATCH 049/305] First serial version of Steve's suggestion --- examples/reaxff/in.reaxff.tatb | 9 +- ...nds_local.cpp => compute_reaxff_bonds.cpp} | 174 +++++++++--------- ...f_bonds_local.h => compute_reaxff_bonds.h} | 27 ++- 3 files changed, 107 insertions(+), 103 deletions(-) rename src/REAXFF/{compute_reaxff_bonds_local.cpp => compute_reaxff_bonds.cpp} (57%) rename src/REAXFF/{compute_reaxff_bonds_local.h => compute_reaxff_bonds.h} (73%) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index 6cf7828cf1..fd0d846154 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -31,8 +31,15 @@ neigh_modify delay 0 every 5 check no fix 1 all nve fix 2 all qeq/reaxff 1 0.0 10.0 1.0e-6 reaxff fix 4 all reaxff/bonds 5 bonds.reaxff +compute bonds all reaxff/bonds variable nqeq equal f_2 +# dumps out the local bond information +dump 1 all local 5 bonds_compute.reaxff c_bonds[1] c_bonds[2] c_bonds[3] + +# dumps out the peratom bond information +dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] + thermo 5 thermo_style custom step temp epair etotal press & v_eb v_ea v_elp v_emol v_ev v_epen v_ecoa & @@ -50,6 +57,6 @@ timestep 0.0625 # axes yes 0.8 0.02 view 60 -30 #dump_modify 3 pad 3 -fix 3 all reaxff/species 1 5 5 species.tatb +#fix 3 all reaxff/species 1 5 5 species.tatb run 25 diff --git a/src/REAXFF/compute_reaxff_bonds_local.cpp b/src/REAXFF/compute_reaxff_bonds.cpp similarity index 57% rename from src/REAXFF/compute_reaxff_bonds_local.cpp rename to src/REAXFF/compute_reaxff_bonds.cpp index c32b404e86..7f1605263c 100644 --- a/src/REAXFF/compute_reaxff_bonds_local.cpp +++ b/src/REAXFF/compute_reaxff_bonds.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds_local.h" +#include "compute_reaxff_bonds.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -33,39 +33,36 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ -ComputeReaxFFBondsLocal::ComputeReaxFFBondsLocal(LAMMPS *lmp, int narg, char **arg) : +ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), - alocal(nullptr), abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) + abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); local_flag = 1; - - nvalues = 7 + 2*MAXREAXBOND; - prev_nvalues = 0; + peratom_flag = 1; // initialize output - nlocal = atom->nlocal; + nlocal = -1; + prev_nbonds = -1; - size_local_rows = atom->nlocal; - size_local_cols = 7 + 2*MAXREAXBOND; + size_peratom_cols = 7; - allocate(nlocal); + size_local_rows = 0; + size_local_cols = 3; + + invoked_bonds = -1; } /* ---------------------------------------------------------------------- */ -ComputeReaxFFBondsLocal::~ComputeReaxFFBondsLocal() -{ - memory->destroy(alocal); - destroy(); -} - -void ComputeReaxFFBondsLocal::destroy() +ComputeReaxFFBonds::~ComputeReaxFFBonds() { + memory->destroy(array_local); + memory->destroy(array_atom); memory->destroy(abo); memory->destroy(neighid); memory->destroy(numneigh); @@ -73,25 +70,16 @@ void ComputeReaxFFBondsLocal::destroy() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBondsLocal::allocate(int n) -{ - memory->create(abo,n,MAXREAXBOND,"reaxff/bonds/local:abo"); - memory->create(neighid,n,MAXREAXBOND,"reaxff/bonds/local:neighid"); - memory->create(numneigh,n,"reaxff/bonds/local:numneigh"); -} - -/* ---------------------------------------------------------------------- */ - -void ComputeReaxFFBondsLocal::init() +void ComputeReaxFFBonds::init() { reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds/local without " - "pair_style reaxff, reaxff/kk, or reaxff/omp"); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + "pair_style reaxff, reaxff/kk, or reaxff/omp"); } /* ---------------------------------------------------------------------- */ -int ComputeReaxFFBondsLocal::FindBond() +int ComputeReaxFFBonds::FindBond() { int *ilist, i, ii, inum; int j, pj, nj; @@ -119,98 +107,108 @@ int ComputeReaxFFBondsLocal::FindBond() if (bo_tmp > bo_cut) { neighid[i][nj] = jtag; abo[i][nj] = bo_tmp; - nj ++; + nj++; } } numneigh[i] = nj; - if (nj > numbonds) numbonds = nj; + numbonds += nj; } return numbonds; } + /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBondsLocal::compute_local() +void ComputeReaxFFBonds::compute_bonds() +{ + invoked_bonds = update->ntimestep; + + if (atom->nlocal > nlocal) { + memory->destroy(abo); + memory->destroy(neighid); + memory->destroy(numneigh); + memory->destroy(array_atom); + nlocal = atom->nlocal; + memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); + memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); + memory->create(numneigh, nlocal, "reaxff/bonds:numneigh"); + memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + } + + for (int i = 0; i < nlocal; i++) { + numneigh[i] = 0; + for (int j = 0; j < MAXREAXBOND; j++) { + neighid[i][j] = 0; + abo[i][j] = 0.0; + } + } + + nbonds = FindBond(); +} + +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBonds::compute_local() { invoked_local = update->ntimestep; - // count local entries and compute bond info - if (atom->nlocal > nlocal) { - destroy(); - allocate(atom->nlocal); + if(invoked_bonds < update->ntimestep) { + compute_bonds(); } - { - const int nlocal = atom->nlocal; - - for (int i = 0; i < nlocal; i++) { - numneigh[i] = 0; - for (int j = 0; j < MAXREAXBOND; j++) { - neighid[i][j] = 0; - abo[i][j] = 0.0; - } - } + if(nbonds > prev_nbonds) { + // grow array_local + memory->destroy(array_local); + memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + prev_nbonds = nbonds; } - int maxnumbonds = FindBond(); - nvalues = 7+2*maxnumbonds; + size_local_rows = nbonds; - if(atom->nlocal > nlocal || nvalues > prev_nvalues) { - reallocate(); - } - - size_local_rows = nlocal; - size_local_cols = nvalues; + int b = 0; for (int i = 0; i < nlocal; ++i) { - auto ptr = alocal[i]; - int numbonds = numneigh[i]; - ptr[0] = atom->tag[i]; - ptr[1] = atom->type[i]; - ptr[2] = numbonds; - - int j = 3; + const int numbonds = numneigh[i]; for (int k = 0; k < numbonds; k++) { - ptr[j++] = neighid[i][k]; - } - - ptr[j++] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; - - for (int k = 0; k < numbonds; k++) { - ptr[j++] = abo[i][k]; - } - - ptr[j++] = reaxff->api->workspace->total_bond_order[i]; - ptr[j++] = reaxff->api->workspace->nlp[i]; - ptr[j++] = atom->q[i]; - - // clear any remaining - for(; j < nvalues; ++j) { - ptr[j] = 0.0; + auto bond = array_local[b++]; + bond[0] = i; + bond[1] = neighid[i][k]; + bond[2] = abo[i][k]; } } } -void ComputeReaxFFBondsLocal::reallocate() +/* ---------------------------------------------------------------------- */ + +void ComputeReaxFFBonds::compute_peratom() { - nlocal = atom->nlocal; + invoked_peratom = update->ntimestep; - // grow array_local - memory->destroy(alocal); - memory->create(alocal,nlocal,nvalues,"reaxff/bonds/local:array_local"); - array_local = alocal; + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } - prev_nvalues = nvalues; + for (int i = 0; i < nlocal; ++i) { + auto ptr = array_atom[i]; + ptr[0] = atom->tag[i]; + ptr[1] = atom->type[i]; + ptr[2] = numneigh[i]; + ptr[3] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; + ptr[4] = reaxff->api->workspace->total_bond_order[i]; + ptr[5] = reaxff->api->workspace->nlp[i]; + ptr[6] = atom->q[i]; + } } /* ---------------------------------------------------------------------- memory usage of local data ------------------------------------------------------------------------- */ -double ComputeReaxFFBondsLocal::memory_usage() +double ComputeReaxFFBonds::memory_usage() { - double bytes = (double)nlocal*nvalues * sizeof(double); + double bytes = (double)(nbonds*3) * sizeof(double); + bytes += (double)(nlocal*7) * sizeof(double); bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); return bytes; diff --git a/src/REAXFF/compute_reaxff_bonds_local.h b/src/REAXFF/compute_reaxff_bonds.h similarity index 73% rename from src/REAXFF/compute_reaxff_bonds_local.h rename to src/REAXFF/compute_reaxff_bonds.h index 2347db3a26..6b00cef7ed 100644 --- a/src/REAXFF/compute_reaxff_bonds_local.h +++ b/src/REAXFF/compute_reaxff_bonds.h @@ -17,41 +17,40 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/local,ComputeReaxFFBondsLocal); +ComputeStyle(reaxff/bonds,ComputeReaxFFBonds); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_H -#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_H +#ifndef LMP_COMPUTE_REAXFF_BONDS_H +#define LMP_COMPUTE_REAXFF_BONDS_H #include "compute.h" namespace LAMMPS_NS { -class ComputeReaxFFBondsLocal : public Compute { +class ComputeReaxFFBonds : public Compute { public: - ComputeReaxFFBondsLocal(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsLocal() override; + ComputeReaxFFBonds(class LAMMPS *, int, char **); + ~ComputeReaxFFBonds() override; void init() override; void compute_local() override; + void compute_peratom() override; + virtual void compute_bonds(); double memory_usage() override; - private: + protected: + bigint invoked_bonds; // last timestep on which compute_bonds() was invoked int nlocal; - int nvalues; - int prev_nvalues; + int nbonds; + int prev_nbonds; - double **alocal; tagint **neighid; double **abo; int *numneigh; class PairReaxFF *reaxff; + private: int FindBond(); - - void allocate(int); - void destroy(); - void reallocate(); }; } // namespace LAMMPS_NS From a72a3ed50d12365ebb41a02001454fe667036b23 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 17 Oct 2023 16:24:14 -0600 Subject: [PATCH 050/305] Kokkos version of compute reaxff/bonds --- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 192 ++++++++++++++++++ ...kokkos.h => compute_reaxff_bonds_kokkos.h} | 30 ++- .../compute_reaxff_bonds_local_kokkos.cpp | 125 ------------ src/KOKKOS/pair_reaxff_kokkos.cpp | 55 ----- src/KOKKOS/pair_reaxff_kokkos.h | 19 -- 5 files changed, 206 insertions(+), 215 deletions(-) create mode 100644 src/KOKKOS/compute_reaxff_bonds_kokkos.cpp rename src/KOKKOS/{compute_reaxff_bonds_local_kokkos.h => compute_reaxff_bonds_kokkos.h} (67%) delete mode 100644 src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp new file mode 100644 index 0000000000..921acb9193 --- /dev/null +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp @@ -0,0 +1,192 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + Contributing author: Richard Berger (LANL) +------------------------------------------------------------------------- */ + +#include "compute_reaxff_bonds_kokkos.h" +#include "atom.h" +#include "molecule.h" +#include "update.h" +#include "force.h" +#include "memory.h" +#include "error.h" +#include "neigh_list.h" + +#include "memory_kokkos.h" +#include "pair_reaxff_kokkos.h" +#include "reaxff_api.h" + +using namespace LAMMPS_NS; +using namespace ReaxFF; + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int narg, char **arg) : + ComputeReaxFFBonds(lmp, narg, arg), + nbuf(-1), buf(nullptr) +{ +} + + +/* ---------------------------------------------------------------------- */ + +template +ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() +{ + memoryKK->destroy_kokkos(k_buf, buf); +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::init() +{ + reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + "pair_style reaxff/kk"); +} + +/* ---------------------------------------------------------------------- */ +template +void ComputeReaxFFBondsKokkos::compute_bonds() +{ + if (atom->nlocal > nlocal) { + memory->destroy(array_atom); + nlocal = atom->nlocal; + memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + } + + // retrieve bond information from kokkos pair style. the data potentially + // lives on device. it is copied into buf on the host in a condensed format + // compute_local and compute_atom then expand the data from this buffer into + // appropiate arrays for consumption by others (e.g. dump local, dump custom + // or library interface) + int maxnumbonds = 0; + if (reaxff->execution_space == Device) + device_pair()->FindBond(maxnumbonds); + else + host_pair()->FindBond(maxnumbonds); + + nbuf = 1+(maxnumbonds*2 + 7)*nlocal; + + if(!buf || k_buf.extent(0) < nbuf) { + memoryKK->destroy_kokkos(k_buf, buf); + memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/bonds:buf"); + } + + // Pass information to buffer, will sync to host + int nbuf_local; + if (reaxff->execution_space == Device) + device_pair()->PackBondBuffer(k_buf, nbuf_local); + else + host_pair()->PackBondBuffer(k_buf, nbuf_local); + buf[0] = nlocal; + + // Extract number of bonds from buffer + nbonds = 0; + int j = 1; + for (int i = 0; i < nlocal; i++) { + int numbonds = static_cast(buf[j+5]); + nbonds += numbonds; + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::compute_local() +{ + invoked_local = update->ntimestep; + + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } + + if(nbonds > prev_nbonds) { + // grow array_local + memory->destroy(array_local); + memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + prev_nbonds = nbonds; + } + + size_local_rows = nbonds; + + // extract local bond information from buffer + int b = 0; + int j = 1; + + for (int i = 0; i < nlocal; ++i) { + const int numbonds = static_cast(buf[j+5]); + const int neigh_offset = j + 6; + const int bo_offset = neigh_offset + numbonds + 1; + for (int k = 0; k < numbonds; k++) { + auto bond = array_local[b++]; + bond[0] = i; + bond[1] = static_cast (buf[neigh_offset+k]); + bond[2] = buf[bo_offset+k]; + } + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- */ + +template +void ComputeReaxFFBondsKokkos::compute_peratom() +{ + invoked_peratom = update->ntimestep; + + if(invoked_bonds < update->ntimestep) { + compute_bonds(); + } + + // extract peratom bond information from buffer + int j = 1; + for (int i = 0; i < nlocal; ++i) { + auto ptr = array_atom[i]; + int numbonds = static_cast(buf[j+5]); + const int mol_offset = j + 6 + numbonds; + ptr[0] = buf[j]; // jtag + ptr[1] = buf[j+1]; // itype + ptr[2] = numbonds; + ptr[3] = buf[mol_offset]; // mol + ptr[4] = buf[j+2]; // sbo + ptr[5] = buf[j+3]; // nlp + ptr[6] = buf[j+4]; // q + j += 2*numbonds + 7; + } +} + +/* ---------------------------------------------------------------------- + memory usage of local data +------------------------------------------------------------------------- */ + +template +double ComputeReaxFFBondsKokkos::memory_usage() +{ + double bytes = (double)(nbonds*3) * sizeof(double); + bytes += (double)(nlocal*7) * sizeof(double); + return bytes; +} + +namespace LAMMPS_NS { +template class ComputeReaxFFBondsKokkos; +#ifdef LMP_KOKKOS_GPU +template class ComputeReaxFFBondsKokkos; +#endif +} diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_kokkos.h similarity index 67% rename from src/KOKKOS/compute_reaxff_bonds_local_kokkos.h rename to src/KOKKOS/compute_reaxff_bonds_kokkos.h index bfb5521199..45020ffa81 100644 --- a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.h +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.h @@ -17,41 +17,39 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/local/kk,ComputeReaxFFBondsLocalKokkos); -ComputeStyle(reaxff/bonds/local/kk/device,ComputeReaxFFBondsLocalKokkos); -ComputeStyle(reaxff/bonds/local/kk/host,ComputeReaxFFBondsLocalKokkos); +ComputeStyle(reaxff/bonds/kk,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/bonds/kk/device,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/bonds/kk/host,ComputeReaxFFBondsKokkos); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H -#define LMP_COMPUTE_REAXFF_BONDS_LOCAL_KOKKOS_H +#ifndef LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H +#define LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H -#include "compute_reaxff_bonds_local.h" +#include "compute_reaxff_bonds.h" #include "pair_reaxff_kokkos.h" #include "kokkos_type.h" namespace LAMMPS_NS { template -class ComputeReaxFFBondsLocalKokkos : public Compute { +class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { public: using device_type = DeviceType; using AT = ArrayTypes; - ComputeReaxFFBondsLocalKokkos(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsLocalKokkos() override; + ComputeReaxFFBondsKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFBondsKokkos() override; void init() override; void compute_local() override; + void compute_peratom() override; + void compute_bonds() override; double memory_usage() override; private: - int nlocal; - int nvalues; - int prev_nvalues; - - double **alocal; - typename AT::tdual_float_2d k_alocal; - PairReaxFF *reaxff; + int nbuf; + double *buf; + typename AT::tdual_float_1d k_buf; auto device_pair() { return dynamic_cast*>(reaxff); diff --git a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp deleted file mode 100644 index a56d243ed6..0000000000 --- a/src/KOKKOS/compute_reaxff_bonds_local_kokkos.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// clang-format off -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - https://www.lammps.org/, Sandia National Laboratories - LAMMPS development team: developers@lammps.org - - Copyright (2003) Sandia Corporation. Under the terms of Contract - DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains - certain rights in this software. This software is distributed under - the GNU General Public License. - - See the README file in the top-level LAMMPS directory. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - Contributing author: Richard Berger (LANL) -------------------------------------------------------------------------- */ - -#include "compute_reaxff_bonds_local_kokkos.h" -#include "atom.h" -#include "molecule.h" -#include "update.h" -#include "force.h" -#include "memory.h" -#include "error.h" -#include "neigh_list.h" - -#include "memory_kokkos.h" -#include "pair_reaxff_kokkos.h" -#include "reaxff_api.h" - -using namespace LAMMPS_NS; -using namespace ReaxFF; - -/* ---------------------------------------------------------------------- */ - -template -ComputeReaxFFBondsLocalKokkos::ComputeReaxFFBondsLocalKokkos(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), - alocal(nullptr), reaxff(nullptr) -{ - if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds/local"); - - local_flag = 1; - - nvalues = 7 + 2*MAXREAXBOND; - prev_nvalues = 0; - - // initialize output - - nlocal = atom->nlocal; - - size_local_rows = atom->nlocal; - size_local_cols = 7 + 2*MAXREAXBOND; - printf("RUNNING KOKKOS VERSION\n"); -} - - -/* ---------------------------------------------------------------------- */ - -template -ComputeReaxFFBondsLocalKokkos::~ComputeReaxFFBondsLocalKokkos() -{ - memoryKK->destroy_kokkos(k_alocal, alocal); -} - -/* ---------------------------------------------------------------------- */ - -template -void ComputeReaxFFBondsLocalKokkos::init() -{ - reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use fix reaxff/bonds without " - "pair_style reaxff/kk"); -} - -/* ---------------------------------------------------------------------- */ - -template -void ComputeReaxFFBondsLocalKokkos::compute_local() -{ - invoked_local = update->ntimestep; - - int maxnumbonds = 0; - if (reaxff->execution_space == Device) - device_pair()->FindBond(maxnumbonds); - else - host_pair()->FindBond(maxnumbonds); - nvalues = 7+2*maxnumbonds; - - if(atom->nlocal > nlocal || nvalues > prev_nvalues) { - nlocal = atom->nlocal; - memoryKK->destroy_kokkos(k_alocal, alocal); - memoryKK->create_kokkos(k_alocal, alocal, atom->nlocal, nvalues,"reaxff/bonds/local:alocal"); - prev_nvalues = nvalues; - array_local = alocal; - } - - size_local_rows = nlocal; - size_local_cols = nvalues; - - if (reaxff->execution_space == Device) - device_pair()->PackBondInfo(k_alocal); - else - host_pair()->PackBondInfo(k_alocal); -} - -/* ---------------------------------------------------------------------- - memory usage of local data -------------------------------------------------------------------------- */ - -template -double ComputeReaxFFBondsLocalKokkos::memory_usage() -{ - double bytes = (double)nlocal*nvalues * sizeof(double); - return bytes; -} - -namespace LAMMPS_NS { -template class ComputeReaxFFBondsLocalKokkos; -#ifdef LMP_KOKKOS_GPU -template class ComputeReaxFFBondsLocalKokkos; -#endif -} diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index fbf90f140d..c7d54b80cd 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4290,61 +4290,6 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo /* ---------------------------------------------------------------------- */ -template -void PairReaxFFKokkos::PackBondInfo(DAT::tdual_float_2d k_alocal) -{ - d_alocal = k_alocal.view(); - k_params_sing.template sync(); - atomKK->sync(execution_space,TAG_MASK|TYPE_MASK|Q_MASK|MOLECULE_MASK); - - tag = atomKK->k_tag.view(); - type = atomKK->k_type.view(); - q = atomKK->k_q.view(); - if (atom->molecule) - molecule = atomKK->k_molecule.view(); - - copymode = 1; - nlocal = atomKK->nlocal; - PairReaxKokkosPackBondInfoFunctor pack_bond_info_functor(this); - Kokkos::parallel_for(nlocal, pack_bond_info_functor); - copymode = 0; - - k_alocal.modify(); - k_alocal.sync(); -} - -template -KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::pack_bond_info_item(const int i) const -{ - const int numbonds = d_numneigh_bonds[i]; - d_alocal(i,0) = tag[i]; - d_alocal(i,1) = type[i]; - d_alocal(i,2) = numbonds; - - int j = 3; - - for (int k = 0; k < numbonds; k++) { - d_alocal(i,j++) = d_neighid(i,k); - } - - d_alocal(i,j++) = molecule.data() ? molecule[i] : 0.0; - - for (int k = 0; k < numbonds; k++) { - d_alocal(i,j++) = d_abo(i,k); - } - - d_alocal(i,j++) = d_total_bo[i]; - d_alocal(i,j++) = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; - d_alocal(i,j++) = q[i]; - - for(; j < d_alocal.extent(1); ++j) { - d_alocal(i,j) = 0.0; - } -} - -/* ---------------------------------------------------------------------- */ - template void PairReaxFFKokkos::FindBondSpecies() { diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 32007e9970..1ad0955a1e 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,7 +135,6 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); - void PackBondInfo(DAT::tdual_float_2d); void FindBondSpecies(); template @@ -293,9 +292,6 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; - KOKKOS_INLINE_FUNCTION - void pack_bond_info_item(const int) const; - KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -510,8 +506,6 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_ffloat_1d d_buf; DAT::tdual_int_scalar k_nbuf_local; - typename AT::t_float_2d d_alocal; - typedef Kokkos::View t_reax_int4_2d; t_reax_int4_2d d_angular_pack, d_torsion_pack; @@ -555,19 +549,6 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; -template -struct PairReaxKokkosPackBondInfoFunctor { - using device_type = DeviceType; - using value_type = int; - PairReaxFFKokkos c; - PairReaxKokkosPackBondInfoFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; - - KOKKOS_INLINE_FUNCTION - void operator()(const int i) const { - c.pack_bond_info_item(i); - } -}; - } #endif From ca143e6ba8da5fba4e44e06472a84ec10fd0f4fe Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 17 Oct 2023 16:40:03 -0600 Subject: [PATCH 051/305] undo minor change --- examples/reaxff/in.reaxff.tatb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index fd0d846154..f422943a13 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -35,7 +35,7 @@ compute bonds all reaxff/bonds variable nqeq equal f_2 # dumps out the local bond information -dump 1 all local 5 bonds_compute.reaxff c_bonds[1] c_bonds[2] c_bonds[3] +dump 1 all local 5 bonds_local.reaxff c_bonds[1] c_bonds[2] c_bonds[3] # dumps out the peratom bond information dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] @@ -57,6 +57,6 @@ timestep 0.0625 # axes yes 0.8 0.02 view 60 -30 #dump_modify 3 pad 3 -#fix 3 all reaxff/species 1 5 5 species.tatb +fix 3 all reaxff/species 1 5 5 species.tatb run 25 From 717e7b064965ec575eeb25ecbbd2d510d9e22100 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 18 Oct 2023 11:30:16 -0600 Subject: [PATCH 052/305] Address comments --- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp index 921acb9193..5c8885ce7a 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp @@ -39,6 +39,7 @@ ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int ComputeReaxFFBonds(lmp, narg, arg), nbuf(-1), buf(nullptr) { + kokkosable = 1; } @@ -113,9 +114,8 @@ void ComputeReaxFFBondsKokkos::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if(invoked_bonds < update->ntimestep) compute_bonds(); - } if(nbonds > prev_nbonds) { // grow array_local @@ -151,9 +151,8 @@ void ComputeReaxFFBondsKokkos::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if(invoked_bonds < update->ntimestep) compute_bonds(); - } // extract peratom bond information from buffer int j = 1; From 9bffeb9512b595dd7fd2db4b27c7c46d3e45a034 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 18 Oct 2023 16:21:10 -0600 Subject: [PATCH 053/305] Next iteration --- examples/reaxff/in.reaxff.tatb | 2 +- src/KOKKOS/compute_reaxff_bonds_kokkos.cpp | 47 +++++++++--------- src/KOKKOS/compute_reaxff_bonds_kokkos.h | 4 +- src/KOKKOS/pair_reaxff_kokkos.cpp | 57 ++++++++++++++++++++++ src/KOKKOS/pair_reaxff_kokkos.h | 17 +++++++ src/REAXFF/compute_reaxff_bonds.cpp | 30 +++++------- src/REAXFF/compute_reaxff_bonds.h | 2 +- 7 files changed, 114 insertions(+), 45 deletions(-) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index f422943a13..50f572a994 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -38,7 +38,7 @@ variable nqeq equal f_2 dump 1 all local 5 bonds_local.reaxff c_bonds[1] c_bonds[2] c_bonds[3] # dumps out the peratom bond information -dump 2 all custom 5 bonds_atom.reaxff c_bonds[*] +dump 2 all custom 5 bonds_atom.reaxff id type q c_bonds[*] thermo 5 thermo_style custom step temp epair etotal press & diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp index 5c8885ce7a..f36832e6ac 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp @@ -68,7 +68,7 @@ void ComputeReaxFFBondsKokkos::compute_bonds() if (atom->nlocal > nlocal) { memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); } // retrieve bond information from kokkos pair style. the data potentially @@ -76,13 +76,14 @@ void ComputeReaxFFBondsKokkos::compute_bonds() // compute_local and compute_atom then expand the data from this buffer into // appropiate arrays for consumption by others (e.g. dump local, dump custom // or library interface) + int maxnumbonds = 0; if (reaxff->execution_space == Device) device_pair()->FindBond(maxnumbonds); else host_pair()->FindBond(maxnumbonds); - nbuf = 1+(maxnumbonds*2 + 7)*nlocal; + nbuf = (maxnumbonds*2 + 3)*nlocal; if(!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); @@ -90,20 +91,21 @@ void ComputeReaxFFBondsKokkos::compute_bonds() } // Pass information to buffer, will sync to host + int nbuf_local; if (reaxff->execution_space == Device) - device_pair()->PackBondBuffer(k_buf, nbuf_local); + device_pair()->PackReducedBondBuffer(k_buf, nbuf_local); else - host_pair()->PackBondBuffer(k_buf, nbuf_local); - buf[0] = nlocal; + host_pair()->PackReducedBondBuffer(k_buf, nbuf_local); // Extract number of bonds from buffer + nbonds = 0; - int j = 1; + int j = 0; for (int i = 0; i < nlocal; i++) { - int numbonds = static_cast(buf[j+5]); + int numbonds = static_cast(buf[j+2]); nbonds += numbonds; - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -127,20 +129,21 @@ void ComputeReaxFFBondsKokkos::compute_local() size_local_rows = nbonds; // extract local bond information from buffer + int b = 0; - int j = 1; + int j = 0; for (int i = 0; i < nlocal; ++i) { - const int numbonds = static_cast(buf[j+5]); - const int neigh_offset = j + 6; - const int bo_offset = neigh_offset + numbonds + 1; + const int numbonds = static_cast(buf[j+2]); + const int neigh_offset = j + 3; + const int bo_offset = neigh_offset + numbonds; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; bond[0] = i; bond[1] = static_cast (buf[neigh_offset+k]); bond[2] = buf[bo_offset+k]; } - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -155,19 +158,15 @@ void ComputeReaxFFBondsKokkos::compute_peratom() compute_bonds(); // extract peratom bond information from buffer - int j = 1; + + int j = 0; for (int i = 0; i < nlocal; ++i) { auto ptr = array_atom[i]; - int numbonds = static_cast(buf[j+5]); - const int mol_offset = j + 6 + numbonds; - ptr[0] = buf[j]; // jtag - ptr[1] = buf[j+1]; // itype + int numbonds = static_cast(buf[j+2]); + ptr[0] = buf[j]; // sbo + ptr[1] = buf[j+1]; // nlp ptr[2] = numbonds; - ptr[3] = buf[mol_offset]; // mol - ptr[4] = buf[j+2]; // sbo - ptr[5] = buf[j+3]; // nlp - ptr[6] = buf[j+4]; // q - j += 2*numbonds + 7; + j += 2*numbonds + 3; } } @@ -179,7 +178,7 @@ template double ComputeReaxFFBondsKokkos::memory_usage() { double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*7) * sizeof(double); + bytes += (double)(nlocal*3) * sizeof(double); return bytes; } diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.h b/src/KOKKOS/compute_reaxff_bonds_kokkos.h index 45020ffa81..48f3860283 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.h +++ b/src/KOKKOS/compute_reaxff_bonds_kokkos.h @@ -52,11 +52,11 @@ class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { typename AT::tdual_float_1d k_buf; auto device_pair() { - return dynamic_cast*>(reaxff); + return static_cast*>(reaxff); } auto host_pair() { - return dynamic_cast*>(reaxff); + return static_cast*>(reaxff); } }; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index c7d54b80cd..e298eca2da 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4247,6 +4247,30 @@ void PairReaxFFKokkos::PackBondBuffer(DAT::tdual_ffloat_1d k_buf, in nbuf_local = k_nbuf_local.h_view(); } +/* ---------------------------------------------------------------------- */ + +template +void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local) +{ + d_buf = k_buf.view(); + k_params_sing.template sync(); + + copymode = 1; + nlocal = atomKK->nlocal; + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + copymode = 0; + + k_buf.modify(); + k_nbuf_local.modify(); + + k_buf.sync(); + k_nbuf_local.sync(); + nbuf_local = k_nbuf_local.h_view(); +} + +/* ---------------------------------------------------------------------- */ + template KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bool &final) const @@ -4288,6 +4312,39 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo k_nbuf_local.view()() = j - 1; } +template +KOKKOS_INLINE_FUNCTION +void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, const bool &final) const +{ + const int numbonds = d_numneigh_bonds[i]; + if (final) { + d_buf[j] = d_total_bo[i]; + d_buf[j+1] = paramssing(type[i]).nlp_opt - d_Delta_lp[i]; + d_buf[j+2] = numbonds; + } + + j += 3; + + if (final) { + for (int k = 0; k < numbonds; ++k) { + d_buf[j+k] = d_neighid(i,k); + } + } + + j += numbonds; + + if (final) { + for (int k = 0; k < numbonds; k++) { + d_buf[j+k] = d_abo(i,k); + } + } + + j += numbonds; + + if (final && i == nlocal-1) + k_nbuf_local.view()() = j - 1; +} + /* ---------------------------------------------------------------------- */ template diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 1ad0955a1e..571dd63fd1 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,6 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &); void FindBondSpecies(); template @@ -292,6 +293,9 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION + void pack_reduced_bond_buffer_item(int, int&, const bool&) const; + KOKKOS_INLINE_FUNCTION void operator()(TagPairReaxFindBondSpeciesZero, const int&) const; @@ -549,6 +553,19 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; +template +struct PairReaxKokkosPackReducedBondBufferFunctor { + typedef DeviceType device_type; + typedef int value_type; + PairReaxFFKokkos c; + PairReaxKokkosPackReducedBondBufferFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + + KOKKOS_INLINE_FUNCTION + void operator()(const int ii, int &j, const bool &final) const { + c.pack_reduced_bond_buffer_item(ii,j,final); + } +}; + } #endif diff --git a/src/REAXFF/compute_reaxff_bonds.cpp b/src/REAXFF/compute_reaxff_bonds.cpp index 7f1605263c..3b3520fda3 100644 --- a/src/REAXFF/compute_reaxff_bonds.cpp +++ b/src/REAXFF/compute_reaxff_bonds.cpp @@ -35,7 +35,7 @@ using namespace ReaxFF; ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), - abo(nullptr), neighid(nullptr), numneigh(nullptr), reaxff(nullptr) + abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); @@ -48,7 +48,7 @@ ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : nlocal = -1; prev_nbonds = -1; - size_peratom_cols = 7; + size_peratom_cols = 3; size_local_rows = 0; size_local_cols = 3; @@ -65,7 +65,7 @@ ComputeReaxFFBonds::~ComputeReaxFFBonds() memory->destroy(array_atom); memory->destroy(abo); memory->destroy(neighid); - memory->destroy(numneigh); + memory->destroy(bondcount); } /* ---------------------------------------------------------------------- */ @@ -110,7 +110,7 @@ int ComputeReaxFFBonds::FindBond() nj++; } } - numneigh[i] = nj; + bondcount[i] = nj; numbonds += nj; } return numbonds; @@ -126,17 +126,17 @@ void ComputeReaxFFBonds::compute_bonds() if (atom->nlocal > nlocal) { memory->destroy(abo); memory->destroy(neighid); - memory->destroy(numneigh); + memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); - memory->create(numneigh, nlocal, "reaxff/bonds:numneigh"); - memory->create(array_atom, nlocal, 7, "reaxff/bonds:array_atom"); + memory->create(bondcount, nlocal, "reaxff/bonds:bondcount"); + memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); } for (int i = 0; i < nlocal; i++) { - numneigh[i] = 0; + bondcount[i] = 0; for (int j = 0; j < MAXREAXBOND; j++) { neighid[i][j] = 0; abo[i][j] = 0.0; @@ -168,7 +168,7 @@ void ComputeReaxFFBonds::compute_local() int b = 0; for (int i = 0; i < nlocal; ++i) { - const int numbonds = numneigh[i]; + const int numbonds = bondcount[i]; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; @@ -191,13 +191,9 @@ void ComputeReaxFFBonds::compute_peratom() for (int i = 0; i < nlocal; ++i) { auto ptr = array_atom[i]; - ptr[0] = atom->tag[i]; - ptr[1] = atom->type[i]; - ptr[2] = numneigh[i]; - ptr[3] = (atom->molecule == nullptr) ? 0.0 : atom->molecule[i]; - ptr[4] = reaxff->api->workspace->total_bond_order[i]; - ptr[5] = reaxff->api->workspace->nlp[i]; - ptr[6] = atom->q[i]; + ptr[0] = reaxff->api->workspace->total_bond_order[i]; + ptr[1] = reaxff->api->workspace->nlp[i]; + ptr[2] = bondcount[i]; } } @@ -208,7 +204,7 @@ void ComputeReaxFFBonds::compute_peratom() double ComputeReaxFFBonds::memory_usage() { double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*7) * sizeof(double); + bytes += (double)(nlocal*3) * sizeof(double); bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); return bytes; diff --git a/src/REAXFF/compute_reaxff_bonds.h b/src/REAXFF/compute_reaxff_bonds.h index 6b00cef7ed..b876c9e02d 100644 --- a/src/REAXFF/compute_reaxff_bonds.h +++ b/src/REAXFF/compute_reaxff_bonds.h @@ -46,7 +46,7 @@ class ComputeReaxFFBonds : public Compute { tagint **neighid; double **abo; - int *numneigh; + int *bondcount; class PairReaxFF *reaxff; private: From afd0107f01ff552aba336ccc02af240f3aed33a8 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Wed, 8 Nov 2023 14:17:18 -0700 Subject: [PATCH 054/305] Add new files to makefile build system --- src/KOKKOS/Install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh index 489efc55a0..01db058d5b 100755 --- a/src/KOKKOS/Install.sh +++ b/src/KOKKOS/Install.sh @@ -165,6 +165,8 @@ action fix_qeq_reaxff_kokkos.cpp fix_qeq_reaxff.cpp action fix_qeq_reaxff_kokkos.h fix_qeq_reaxff.h action fix_reaxff_bonds_kokkos.cpp fix_reaxff_bonds.cpp action fix_reaxff_bonds_kokkos.h fix_reaxff_bonds.h +action compute_reaxff_bonds_kokkos.cpp compute_reaxff_bonds.cpp +action compute_reaxff_bonds_kokkos.h compute_reaxff_bonds.h action fix_reaxff_species_kokkos.cpp fix_reaxff_species.cpp action fix_reaxff_species_kokkos.h fix_reaxff_species.h action fix_rx_kokkos.cpp fix_rx.cpp From 16f0806da07289e3aea388b453e03ba24f042120 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 20 Nov 2023 15:36:46 -0700 Subject: [PATCH 055/305] Rename compute to reaxff/atom --- examples/reaxff/in.reaxff.tatb | 2 +- src/KOKKOS/Install.sh | 4 +- ...kos.cpp => compute_reaxff_atom_kokkos.cpp} | 46 ++++++------- ..._kokkos.h => compute_reaxff_atom_kokkos.h} | 14 ++-- src/KOKKOS/pair_reaxff_kokkos.cpp | 35 ++++++---- src/KOKKOS/pair_reaxff_kokkos.h | 7 +- ...axff_bonds.cpp => compute_reaxff_atom.cpp} | 64 ++++++++++++------- ...e_reaxff_bonds.h => compute_reaxff_atom.h} | 13 ++-- 8 files changed, 108 insertions(+), 77 deletions(-) rename src/KOKKOS/{compute_reaxff_bonds_kokkos.cpp => compute_reaxff_atom_kokkos.cpp} (76%) rename src/KOKKOS/{compute_reaxff_bonds_kokkos.h => compute_reaxff_atom_kokkos.h} (79%) rename src/REAXFF/{compute_reaxff_bonds.cpp => compute_reaxff_atom.cpp} (74%) rename src/REAXFF/{compute_reaxff_bonds.h => compute_reaxff_atom.h} (84%) diff --git a/examples/reaxff/in.reaxff.tatb b/examples/reaxff/in.reaxff.tatb index 50f572a994..967ed0a1d6 100644 --- a/examples/reaxff/in.reaxff.tatb +++ b/examples/reaxff/in.reaxff.tatb @@ -31,7 +31,7 @@ neigh_modify delay 0 every 5 check no fix 1 all nve fix 2 all qeq/reaxff 1 0.0 10.0 1.0e-6 reaxff fix 4 all reaxff/bonds 5 bonds.reaxff -compute bonds all reaxff/bonds +compute bonds all reaxff/atom bonds yes variable nqeq equal f_2 # dumps out the local bond information diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh index 01db058d5b..28fe6d5cd6 100755 --- a/src/KOKKOS/Install.sh +++ b/src/KOKKOS/Install.sh @@ -165,8 +165,8 @@ action fix_qeq_reaxff_kokkos.cpp fix_qeq_reaxff.cpp action fix_qeq_reaxff_kokkos.h fix_qeq_reaxff.h action fix_reaxff_bonds_kokkos.cpp fix_reaxff_bonds.cpp action fix_reaxff_bonds_kokkos.h fix_reaxff_bonds.h -action compute_reaxff_bonds_kokkos.cpp compute_reaxff_bonds.cpp -action compute_reaxff_bonds_kokkos.h compute_reaxff_bonds.h +action compute_reaxff_atom_kokkos.cpp compute_reaxff_atom.cpp +action compute_reaxff_atom_kokkos.h compute_reaxff_atom.h action fix_reaxff_species_kokkos.cpp fix_reaxff_species.cpp action fix_reaxff_species_kokkos.h fix_reaxff_species.h action fix_rx_kokkos.cpp fix_rx.cpp diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp similarity index 76% rename from src/KOKKOS/compute_reaxff_bonds_kokkos.cpp rename to src/KOKKOS/compute_reaxff_atom_kokkos.cpp index f36832e6ac..2485d9ae72 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds_kokkos.h" +#include "compute_reaxff_atom_kokkos.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -35,8 +35,8 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ template -ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int narg, char **arg) : - ComputeReaxFFBonds(lmp, narg, arg), +ComputeReaxFFAtomKokkos::ComputeReaxFFAtomKokkos(LAMMPS *lmp, int narg, char **arg) : + ComputeReaxFFAtom(lmp, narg, arg), nbuf(-1), buf(nullptr) { kokkosable = 1; @@ -46,7 +46,7 @@ ComputeReaxFFBondsKokkos::ComputeReaxFFBondsKokkos(LAMMPS *lmp, int /* ---------------------------------------------------------------------- */ template -ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() +ComputeReaxFFAtomKokkos::~ComputeReaxFFAtomKokkos() { memoryKK->destroy_kokkos(k_buf, buf); } @@ -54,21 +54,21 @@ ComputeReaxFFBondsKokkos::~ComputeReaxFFBondsKokkos() /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::init() +void ComputeReaxFFAtomKokkos::init() { reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff/kk"); } /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_bonds() +void ComputeReaxFFAtomKokkos::compute_bonds() { if (atom->nlocal > nlocal) { memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); + memory->create(array_atom, nlocal, 3, "reaxff/atom:array_atom"); } // retrieve bond information from kokkos pair style. the data potentially @@ -83,20 +83,20 @@ void ComputeReaxFFBondsKokkos::compute_bonds() else host_pair()->FindBond(maxnumbonds); - nbuf = (maxnumbonds*2 + 3)*nlocal; + nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; if(!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); - memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/bonds:buf"); + memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/atom:buf"); } // Pass information to buffer, will sync to host int nbuf_local; if (reaxff->execution_space == Device) - device_pair()->PackReducedBondBuffer(k_buf, nbuf_local); + device_pair()->PackReducedBondBuffer(k_buf, nbuf_local, store_bonds); else - host_pair()->PackReducedBondBuffer(k_buf, nbuf_local); + host_pair()->PackReducedBondBuffer(k_buf, nbuf_local, store_bonds); // Extract number of bonds from buffer @@ -105,14 +105,14 @@ void ComputeReaxFFBondsKokkos::compute_bonds() for (int i = 0; i < nlocal; i++) { int numbonds = static_cast(buf[j+2]); nbonds += numbonds; - j += 2*numbonds + 3; + j += (store_bonds ? 2*numbonds : 0) + 3; } } /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_local() +void ComputeReaxFFAtomKokkos::compute_local() { invoked_local = update->ntimestep; @@ -122,7 +122,7 @@ void ComputeReaxFFBondsKokkos::compute_local() if(nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); - memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); prev_nbonds = nbonds; } @@ -150,7 +150,7 @@ void ComputeReaxFFBondsKokkos::compute_local() /* ---------------------------------------------------------------------- */ template -void ComputeReaxFFBondsKokkos::compute_peratom() +void ComputeReaxFFAtomKokkos::compute_peratom() { invoked_peratom = update->ntimestep; @@ -166,7 +166,7 @@ void ComputeReaxFFBondsKokkos::compute_peratom() ptr[0] = buf[j]; // sbo ptr[1] = buf[j+1]; // nlp ptr[2] = numbonds; - j += 2*numbonds + 3; + j += (store_bonds ? 2*numbonds : 0) + 3; } } @@ -175,16 +175,18 @@ void ComputeReaxFFBondsKokkos::compute_peratom() ------------------------------------------------------------------------- */ template -double ComputeReaxFFBondsKokkos::memory_usage() +double ComputeReaxFFAtomKokkos::memory_usage() { - double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*3) * sizeof(double); + double bytes = (double)(nlocal*3) * sizeof(double); + if(store_bonds) + bytes += (double)(nbonds*3) * sizeof(double); + bytes += (double)(nbuf > 0 ? nbuf * sizeof(double) : 0); return bytes; } namespace LAMMPS_NS { -template class ComputeReaxFFBondsKokkos; +template class ComputeReaxFFAtomKokkos; #ifdef LMP_KOKKOS_GPU -template class ComputeReaxFFBondsKokkos; +template class ComputeReaxFFAtomKokkos; #endif } diff --git a/src/KOKKOS/compute_reaxff_bonds_kokkos.h b/src/KOKKOS/compute_reaxff_atom_kokkos.h similarity index 79% rename from src/KOKKOS/compute_reaxff_bonds_kokkos.h rename to src/KOKKOS/compute_reaxff_atom_kokkos.h index 48f3860283..7037c7e308 100644 --- a/src/KOKKOS/compute_reaxff_bonds_kokkos.h +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.h @@ -17,29 +17,29 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds/kk,ComputeReaxFFBondsKokkos); -ComputeStyle(reaxff/bonds/kk/device,ComputeReaxFFBondsKokkos); -ComputeStyle(reaxff/bonds/kk/host,ComputeReaxFFBondsKokkos); +ComputeStyle(reaxff/atom/kk,ComputeReaxFFAtomKokkos); +ComputeStyle(reaxff/atom/kk/device,ComputeReaxFFAtomKokkos); +ComputeStyle(reaxff/atom/kk/host,ComputeReaxFFAtomKokkos); // clang-format on #else #ifndef LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H #define LMP_COMPUTE_REAXFF_BONDS_KOKKOS_H -#include "compute_reaxff_bonds.h" +#include "compute_reaxff_atom.h" #include "pair_reaxff_kokkos.h" #include "kokkos_type.h" namespace LAMMPS_NS { template -class ComputeReaxFFBondsKokkos : public ComputeReaxFFBonds { +class ComputeReaxFFAtomKokkos : public ComputeReaxFFAtom { public: using device_type = DeviceType; using AT = ArrayTypes; - ComputeReaxFFBondsKokkos(class LAMMPS *, int, char **); - ~ComputeReaxFFBondsKokkos() override; + ComputeReaxFFAtomKokkos(class LAMMPS *, int, char **); + ~ComputeReaxFFAtomKokkos() override; void init() override; void compute_local() override; void compute_peratom() override; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index e298eca2da..914962f0e6 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4250,15 +4250,21 @@ void PairReaxFFKokkos::PackBondBuffer(DAT::tdual_ffloat_1d k_buf, in /* ---------------------------------------------------------------------- */ template -void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local) +void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_buf, int &nbuf_local, bool store_bonds) { d_buf = k_buf.view(); k_params_sing.template sync(); copymode = 1; nlocal = atomKK->nlocal; - PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); - Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + if(store_bonds) { + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + } else { + PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); + Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); + } + copymode = 0; k_buf.modify(); @@ -4313,6 +4319,7 @@ void PairReaxFFKokkos::pack_bond_buffer_item(int i, int &j, const bo } template +template KOKKOS_INLINE_FUNCTION void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, const bool &final) const { @@ -4325,21 +4332,23 @@ void PairReaxFFKokkos::pack_reduced_bond_buffer_item(int i, int &j, j += 3; - if (final) { - for (int k = 0; k < numbonds; ++k) { - d_buf[j+k] = d_neighid(i,k); + if constexpr(STORE_BONDS) { + if (final) { + for (int k = 0; k < numbonds; ++k) { + d_buf[j+k] = d_neighid(i,k); + } } - } - j += numbonds; + j += numbonds; - if (final) { - for (int k = 0; k < numbonds; k++) { - d_buf[j+k] = d_abo(i,k); + if (final) { + for (int k = 0; k < numbonds; k++) { + d_buf[j+k] = d_abo(i,k); + } } - } - j += numbonds; + j += numbonds; + } if (final && i == nlocal-1) k_nbuf_local.view()() = j - 1; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index 571dd63fd1..f246afcc86 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -135,7 +135,7 @@ class PairReaxFFKokkos : public PairReaxFF { double memory_usage(); void FindBond(int &); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); - void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &); + void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &, bool); void FindBondSpecies(); template @@ -293,6 +293,7 @@ class PairReaxFFKokkos : public PairReaxFF { KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; + template KOKKOS_INLINE_FUNCTION void pack_reduced_bond_buffer_item(int, int&, const bool&) const; @@ -553,7 +554,7 @@ struct PairReaxKokkosPackBondBufferFunctor { } }; -template +template struct PairReaxKokkosPackReducedBondBufferFunctor { typedef DeviceType device_type; typedef int value_type; @@ -562,7 +563,7 @@ struct PairReaxKokkosPackReducedBondBufferFunctor { KOKKOS_INLINE_FUNCTION void operator()(const int ii, int &j, const bool &final) const { - c.pack_reduced_bond_buffer_item(ii,j,final); + c.template pack_reduced_bond_buffer_item(ii,j,final); } }; diff --git a/src/REAXFF/compute_reaxff_bonds.cpp b/src/REAXFF/compute_reaxff_atom.cpp similarity index 74% rename from src/REAXFF/compute_reaxff_bonds.cpp rename to src/REAXFF/compute_reaxff_atom.cpp index 3b3520fda3..d975fe42f8 100644 --- a/src/REAXFF/compute_reaxff_bonds.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -16,7 +16,7 @@ Contributing author: Richard Berger (LANL) ------------------------------------------------------------------------- */ -#include "compute_reaxff_bonds.h" +#include "compute_reaxff_atom.h" #include "atom.h" #include "molecule.h" #include "update.h" @@ -33,14 +33,13 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ -ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : +ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/bonds"); + error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/atom"); - local_flag = 1; peratom_flag = 1; // initialize output @@ -54,12 +53,25 @@ ComputeReaxFFBonds::ComputeReaxFFBonds(LAMMPS *lmp, int narg, char **arg) : size_local_cols = 3; invoked_bonds = -1; + + store_bonds = false; + + int iarg = 3; + while (iarg narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom bonds", error); + store_bonds = utils::logical(FLERR, arg[iarg+1], false, lmp); + iarg += 2; + } else error->all(FLERR,"Illegal compute reaxff/atom command"); + } + + local_flag = store_bonds; } /* ---------------------------------------------------------------------- */ -ComputeReaxFFBonds::~ComputeReaxFFBonds() +ComputeReaxFFAtom::~ComputeReaxFFAtom() { memory->destroy(array_local); memory->destroy(array_atom); @@ -70,16 +82,16 @@ ComputeReaxFFBonds::~ComputeReaxFFBonds() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::init() +void ComputeReaxFFAtom::init() { reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/bonds without " + if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff, reaxff/kk, or reaxff/omp"); } /* ---------------------------------------------------------------------- */ -int ComputeReaxFFBonds::FindBond() +int ComputeReaxFFAtom::FindBond() { int *ilist, i, ii, inum; int j, pj, nj; @@ -105,8 +117,10 @@ int ComputeReaxFFBonds::FindBond() bo_tmp = bo_ij->bo_data.BO; if (bo_tmp > bo_cut) { - neighid[i][nj] = jtag; - abo[i][nj] = bo_tmp; + if(store_bonds) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + } nj++; } } @@ -119,7 +133,7 @@ int ComputeReaxFFBonds::FindBond() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_bonds() +void ComputeReaxFFAtom::compute_bonds() { invoked_bonds = update->ntimestep; @@ -129,15 +143,17 @@ void ComputeReaxFFBonds::compute_bonds() memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; - memory->create(abo, nlocal, MAXREAXBOND, "reaxff/bonds:abo"); - memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/bonds:neighid"); - memory->create(bondcount, nlocal, "reaxff/bonds:bondcount"); - memory->create(array_atom, nlocal, 3, "reaxff/bonds:array_atom"); + if(store_bonds) { + memory->create(abo, nlocal, MAXREAXBOND, "reaxff/atom:abo"); + memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/atom:neighid"); + } + memory->create(bondcount, nlocal, "reaxff/atom:bondcount"); + memory->create(array_atom, nlocal, 3, "reaxff/atom:array_atom"); } for (int i = 0; i < nlocal; i++) { bondcount[i] = 0; - for (int j = 0; j < MAXREAXBOND; j++) { + for (int j = 0; store_bonds && j < MAXREAXBOND; j++) { neighid[i][j] = 0; abo[i][j] = 0.0; } @@ -148,7 +164,7 @@ void ComputeReaxFFBonds::compute_bonds() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_local() +void ComputeReaxFFAtom::compute_local() { invoked_local = update->ntimestep; @@ -159,7 +175,7 @@ void ComputeReaxFFBonds::compute_local() if(nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); - memory->create(array_local, nbonds, 3, "reaxff/bonds:array_local"); + memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); prev_nbonds = nbonds; } @@ -181,7 +197,7 @@ void ComputeReaxFFBonds::compute_local() /* ---------------------------------------------------------------------- */ -void ComputeReaxFFBonds::compute_peratom() +void ComputeReaxFFAtom::compute_peratom() { invoked_peratom = update->ntimestep; @@ -201,11 +217,13 @@ void ComputeReaxFFBonds::compute_peratom() memory usage of local data ------------------------------------------------------------------------- */ -double ComputeReaxFFBonds::memory_usage() +double ComputeReaxFFAtom::memory_usage() { - double bytes = (double)(nbonds*3) * sizeof(double); - bytes += (double)(nlocal*3) * sizeof(double); - bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + double bytes = (double)(nlocal*3) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); + if(store_bonds) { + bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); + bytes += (double)(nbonds*3) * sizeof(double); + } return bytes; } diff --git a/src/REAXFF/compute_reaxff_bonds.h b/src/REAXFF/compute_reaxff_atom.h similarity index 84% rename from src/REAXFF/compute_reaxff_bonds.h rename to src/REAXFF/compute_reaxff_atom.h index b876c9e02d..31b18e7238 100644 --- a/src/REAXFF/compute_reaxff_bonds.h +++ b/src/REAXFF/compute_reaxff_atom.h @@ -17,21 +17,21 @@ #ifdef COMPUTE_CLASS // clang-format off -ComputeStyle(reaxff/bonds,ComputeReaxFFBonds); +ComputeStyle(reaxff/atom,ComputeReaxFFAtom); // clang-format on #else -#ifndef LMP_COMPUTE_REAXFF_BONDS_H -#define LMP_COMPUTE_REAXFF_BONDS_H +#ifndef LMP_COMPUTE_REAXFF_ATOM_H +#define LMP_COMPUTE_REAXFF_ATOM_H #include "compute.h" namespace LAMMPS_NS { -class ComputeReaxFFBonds : public Compute { +class ComputeReaxFFAtom : public Compute { public: - ComputeReaxFFBonds(class LAMMPS *, int, char **); - ~ComputeReaxFFBonds() override; + ComputeReaxFFAtom(class LAMMPS *, int, char **); + ~ComputeReaxFFAtom() override; void init() override; void compute_local() override; void compute_peratom() override; @@ -43,6 +43,7 @@ class ComputeReaxFFBonds : public Compute { int nlocal; int nbonds; int prev_nbonds; + bool store_bonds; tagint **neighid; double **abo; From fd83ed4004a8d26b95f1f69e75b288804361bfd5 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 13:42:51 -0700 Subject: [PATCH 056/305] compute reaxff/atom: add support for pair hybrid --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 9 ++++--- src/REAXFF/compute_reaxff_atom.cpp | 30 ++++++++++++++++++++--- src/REAXFF/compute_reaxff_atom.h | 1 + 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index 2485d9ae72..845e8bc6e7 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -56,9 +56,12 @@ ComputeReaxFFAtomKokkos::~ComputeReaxFFAtomKokkos() template void ComputeReaxFFAtomKokkos::init() { - reaxff = dynamic_cast(force->pair_match("^reax../kk",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " - "pair_style reaxff/kk"); + ComputeReaxFFAtom::init(); + + if(!reaxff || !reaxff->kokkosable) { + error->all(FLERR,"Cannot use compute reaxff/atom/kk without " + "pair_style reaxff/kk"); + } } /* ---------------------------------------------------------------------- */ diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index d975fe42f8..1d7bdf0e7d 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -55,10 +55,21 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : invoked_bonds = -1; store_bonds = false; + nsub = 0; int iarg = 3; while (iarg narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom pair", error); + ++iarg; + + if (isdigit(arg[iarg][0])) { + nsub = utils::inumeric(FLERR, arg[iarg], false, lmp); + ++iarg; + if (nsub > 0) continue; + } + error->all(FLERR, "Illegal compute reaxff/atom command"); + } else if (strcmp(arg[iarg], "bonds") == 0) { if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "compute reaxff/atom bonds", error); store_bonds = utils::logical(FLERR, arg[iarg+1], false, lmp); iarg += 2; @@ -84,9 +95,20 @@ ComputeReaxFFAtom::~ComputeReaxFFAtom() void ComputeReaxFFAtom::init() { - reaxff = dynamic_cast(force->pair_match("^reax..",0)); - if (reaxff == nullptr) error->all(FLERR,"Cannot use compute reaxff/atom without " - "pair_style reaxff, reaxff/kk, or reaxff/omp"); + if (lmp->suffix_enable) { + if (lmp->suffix) + reaxff = dynamic_cast(force->pair_match(fmt::format("^reax../{}", lmp->suffix), 0, nsub)); + if (!reaxff && lmp->suffix2) + reaxff = dynamic_cast(force->pair_match(fmt::format("^reax../{}", lmp->suffix2), 0, nsub)); + } + + if (!reaxff) reaxff = dynamic_cast(force->pair_match("^reax..", 0, nsub)); + + if (!reaxff) error->all(FLERR,"Cannot use compute reaxff/atom without " + "pair_style reaxff or reaxff/omp"); + + if(reaxff->kokkosable && !kokkosable) + error->all(FLERR,"Cannot use compute reaxff/atom with pair_style reaxff/kk. Use reaxff/atom/kk."); } /* ---------------------------------------------------------------------- */ diff --git a/src/REAXFF/compute_reaxff_atom.h b/src/REAXFF/compute_reaxff_atom.h index 31b18e7238..1f9aaec1ae 100644 --- a/src/REAXFF/compute_reaxff_atom.h +++ b/src/REAXFF/compute_reaxff_atom.h @@ -43,6 +43,7 @@ class ComputeReaxFFAtom : public Compute { int nlocal; int nbonds; int prev_nbonds; + int nsub; bool store_bonds; tagint **neighid; From a5cc181358cab34f2edfa1ab79e04d71723bfa70 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 14:03:10 -0700 Subject: [PATCH 057/305] Start with compute reaxff/atom documentation --- doc/src/Commands_compute.rst | 1 + doc/src/compute_reaxff_atom.rst | 67 +++++++++++++++++++++++++++++++++ doc/src/pair_reaxff.rst | 3 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 doc/src/compute_reaxff_atom.rst diff --git a/doc/src/Commands_compute.rst b/doc/src/Commands_compute.rst index dbd6b58ce7..819a1b10d8 100644 --- a/doc/src/Commands_compute.rst +++ b/doc/src/Commands_compute.rst @@ -115,6 +115,7 @@ KOKKOS, o = OPENMP, t = OPT. * :doc:`property/grid ` * :doc:`property/local ` * :doc:`ptm/atom ` + * :doc:`reaxff/atom (k) ` * :doc:`rdf ` * :doc:`reduce ` * :doc:`reduce/chunk ` diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst new file mode 100644 index 0000000000..4906f3fe5c --- /dev/null +++ b/doc/src/compute_reaxff_atom.rst @@ -0,0 +1,67 @@ +.. index:: fix reaxff/atom +.. index:: fix reaxff/atom/kk + +fix reaxff/atom command +======================= + +Accelerator Variants: *reaxff/atom/kk* + +Syntax +"""""" + +.. code-block:: LAMMPS + + compute ID group-ID reaxff/atom attribute args ... keyword value ... + +* ID, group-ID are documented in :doc:`compute ` command +* reaxff/atom = name of this compute command +* attribute = *pair* + + .. parsed-literal:: + + *pair* args = nsub + nsub = *n*-instance of a sub-style, if a pair style is used multiple times in a hybrid style + +* keyword = *bonds* + + .. parsed-literal:: + + *bonds* value = *no* or *yes* + *no* = ignore list of local bonds + *yes* = include list of local bonds + +Examples +"""""""" + +.. code-block:: LAMMPS + + compute 1 all reaxff/atom bonds yes + +Description +""""""""""" + +TODO + +---------- + +.. include:: accel_styles.rst + +---------- + +Restrictions +"""""""""""" + +The compute reaxff/atom command requires that the :doc:`pair_style reaxff +` is invoked. This fix is part of the REAXFF package. It is only +enabled if LAMMPS was built with that package. See the :doc:`Build package +` page for more info. + +Related commands +"""""""""""""""" + +:doc:`pair_style reaxff ` + +Default +""""""" + +The option defaults are *bonds* = *no*. diff --git a/doc/src/pair_reaxff.rst b/doc/src/pair_reaxff.rst index d28e15b0a2..03d53d1ff4 100644 --- a/doc/src/pair_reaxff.rst +++ b/doc/src/pair_reaxff.rst @@ -373,7 +373,8 @@ Related commands :doc:`pair_coeff `, :doc:`fix qeq/reaxff `, :doc:`fix acks2/reaxff `, :doc:`fix reaxff/bonds `, -:doc:`fix reaxff/species ` +:doc:`fix reaxff/species `, +:doc:`compute reaxff/atom ` Default """"""" From b72c34d4979b184adbb4aea83240e7bcb30e040e Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 21 Nov 2023 14:20:17 -0700 Subject: [PATCH 058/305] compute reaxff/atom: return tag[i] instead of i --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 3 ++- src/REAXFF/compute_reaxff_atom.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index 845e8bc6e7..c703bc3552 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -135,6 +135,7 @@ void ComputeReaxFFAtomKokkos::compute_local() int b = 0; int j = 0; + auto tag = atom->tag; for (int i = 0; i < nlocal; ++i) { const int numbonds = static_cast(buf[j+2]); @@ -142,7 +143,7 @@ void ComputeReaxFFAtomKokkos::compute_local() const int bo_offset = neigh_offset + numbonds; for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; - bond[0] = i; + bond[0] = tag[i]; bond[1] = static_cast (buf[neigh_offset+k]); bond[2] = buf[bo_offset+k]; } diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 1d7bdf0e7d..2e5d4058b8 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -202,6 +202,7 @@ void ComputeReaxFFAtom::compute_local() } size_local_rows = nbonds; + auto tag = atom->tag; int b = 0; @@ -210,7 +211,7 @@ void ComputeReaxFFAtom::compute_local() for (int k = 0; k < numbonds; k++) { auto bond = array_local[b++]; - bond[0] = i; + bond[0] = tag[i]; bond[1] = neighid[i][k]; bond[2] = abo[i][k]; } From 782ca9e0ff366c8a2e540bfc08d558155357fac5 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Fri, 1 Dec 2023 14:46:30 -0700 Subject: [PATCH 059/305] Thread over neighbors in addition to atoms when using a half neighbor list --- src/KOKKOS/kokkos.cpp | 4 +- src/KOKKOS/pair_kokkos.h | 318 ++++++++++++++++++++++++++++----------- 2 files changed, 236 insertions(+), 86 deletions(-) diff --git a/src/KOKKOS/kokkos.cpp b/src/KOKKOS/kokkos.cpp index c963cd52d0..5572f69901 100644 --- a/src/KOKKOS/kokkos.cpp +++ b/src/KOKKOS/kokkos.cpp @@ -608,8 +608,8 @@ void KokkosLMP::accelerator(int narg, char **arg) force->newton = force->newton_pair = force->newton_bond = newtonflag; - if (neigh_thread && neighflag != FULL) - error->all(FLERR,"Must use KOKKOS package option 'neigh full' with 'neigh/thread on'"); + if (neigh_thread && newtonflag) + error->all(FLERR,"Must use KOKKOS package option 'newton off' with 'neigh/thread on'"); neighbor->binsize_user = binsize; if (binsize <= 0.0) neighbor->binsizeflag = 0; diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index d3c766f5ae..c0b81cc594 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -116,6 +116,7 @@ struct PairComputeFunctor { // Loop over neighbors of one atom without coulomb interaction // This function is called in parallel + template KOKKOS_FUNCTION EV_FLOAT compute_item(const int& ii, @@ -161,7 +162,7 @@ struct PairComputeFunctor { fytmp += dely*fpair; fztmp += delz*fpair; - if ((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { a_f(j,0) -= delx*fpair; a_f(j,1) -= dely*fpair; a_f(j,2) -= delz*fpair; @@ -169,9 +170,9 @@ struct PairComputeFunctor { if (EVFLAG) { F_FLOAT evdwl = 0.0; - if (c.eflag) { + if (c.eflag_either) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - ev.evdwl += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j KOKKOS_FUNCTION EV_FLOAT compute_item(const int& ii, @@ -241,7 +243,7 @@ struct PairComputeFunctor { fytmp += dely*fpair; fztmp += delz*fpair; - if ((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && (NEWTON_PAIR || j < c.nlocal)) { a_f(j,0) -= delx*fpair; a_f(j,1) -= dely*fpair; a_f(j,2) -= delz*fpair; @@ -250,14 +252,14 @@ struct PairComputeFunctor { if (EVFLAG) { F_FLOAT evdwl = 0.0; F_FLOAT ecoul = 0.0; - if (c.eflag) { + if (c.eflag_either) { if (rsq < (STACKPARAMS?c.m_cut_ljsq[itype][jtype]:c.d_cut_ljsq(itype,jtype))) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - ev.evdwl += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j(rsq,i,j,itype,jtype,factor_coul,qtmp); - ev.ecoul += (((NEIGHFLAG==HALF || NEIGHFLAG==HALFTHREAD)&&(NEWTON_PAIR||(j::member_type team, const NeighListKokkos &list, const NoCoulTag&) const { + auto a_f = dup_f.template access::value>(); + const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; @@ -292,7 +297,7 @@ struct PairComputeFunctor { const X_FLOAT ztmp = c.x(i,2); const int itype = c.type(i); - if (ZEROFLAG) { + if (NEIGHFLAG == FULL && ZEROFLAG) { Kokkos::single(Kokkos::PerThread(team), [&] (){ f(i,0) = 0.0; f(i,1) = 0.0; @@ -321,35 +326,49 @@ struct PairComputeFunctor { const F_FLOAT fpair = factor_lj*c.template compute_fpair(rsq,i,j,itype,jtype); - ftmp.x += delx*fpair; - ftmp.y += dely*fpair; - ftmp.z += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + ftmp.x += fx; + ftmp.y += fy; + ftmp.z += fz; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } } },fsum); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fsum.x; - f(i,1) += fsum.y; - f(i,2) += fsum.z; + a_f(i,0) += fsum.x; + a_f(i,1) += fsum.y; + a_f(i,2) += fsum.z; }); }); } - // Use TeamPolicy, assume Newton off, Full Neighborlist, and no energy/virial + // TeamPolicy, newton off, and no energy/virial // Loop over neighbors of one atom with coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION void compute_item_team(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const CoulTag& ) const { + auto a_f = dup_f.template access::value>(); + const int inum = team.league_size(); const int atoms_per_team = team.team_size(); int firstatom = team.league_rank()*atoms_per_team; int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { + const int i = list.d_ilist[ii]; const X_FLOAT xtmp = c.x(i,0); const X_FLOAT ytmp = c.x(i,1); @@ -357,8 +376,9 @@ struct PairComputeFunctor { const int itype = c.type(i); const F_FLOAT qtmp = c.q(i); - if (ZEROFLAG) { - Kokkos::single(Kokkos::PerThread(team), [&] (){ + if (NEIGHFLAG == FULL && ZEROFLAG) { + Kokkos::single(Kokkos::PerThread(team), [&] () + { f(i,0) = 0.0; f(i,1) = 0.0; f(i,2) = 0.0; @@ -391,34 +411,50 @@ struct PairComputeFunctor { if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) fpair+=c.template compute_fcoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - ftmp.x += delx*fpair; - ftmp.y += dely*fpair; - ftmp.z += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + ftmp.x += fx; + ftmp.y += fy; + ftmp.z += fz; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } } + },fsum); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fsum.x; - f(i,1) += fsum.y; - f(i,2) += fsum.z; + a_f(i,0) += fsum.x; + a_f(i,1) += fsum.y; + a_f(i,2) += fsum.z; }); }); } - - // Use TeamPolicy, assume Newton off, Full Neighborlist, and energy/virial + // TeamPolicy, newton off, and energy/virial // Loop over neighbors of one atom without coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION EV_FLOAT compute_item_team_ev(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const NoCoulTag&) const { + auto a_f = dup_f.template access::value>(); + auto a_eatom = dup_eatom.template access::value>(); + auto a_vatom = dup_vatom.template access::value>(); + EV_FLOAT ev; const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; + Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { const int i = list.d_ilist[ii]; @@ -427,8 +463,9 @@ struct PairComputeFunctor { const X_FLOAT ztmp = c.x(i,2); const int itype = c.type(i); - if (ZEROFLAG) { - Kokkos::single(Kokkos::PerThread(team), [&] (){ + if (NEIGHFLAG == FULL && ZEROFLAG) { + Kokkos::single(Kokkos::PerThread(team), [&] () + { f(i,0) = 0.0; f(i,1) = 0.0; f(i,2) = 0.0; @@ -456,37 +493,85 @@ struct PairComputeFunctor { const F_FLOAT fpair = factor_lj*c.template compute_fpair(rsq,i,j,itype,jtype); - fev_tmp.f[0] += delx*fpair; - fev_tmp.f[1] += dely*fpair; - fev_tmp.f[2] += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + fev_tmp.f[0] += fx; + fev_tmp.f[1] += fy; + fev_tmp.f[2] += fz; + + const int I_CONTRIB = (NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD); + const int J_CONTRIB = ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal); + const E_FLOAT factor = J_CONTRIB?1.0:0.5; + + if (J_CONTRIB) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } F_FLOAT evdwl = 0.0; - if (c.eflag) { + if (c.eflag_either) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - fev_tmp.evdwl += 0.5*evdwl; + fev_tmp.evdwl += factor * evdwl; + + if (c.eflag_atom) { + const E_FLOAT epairhalf = 0.5 * evdwl; + + if (I_CONTRIB) + a_eatom[i] += epairhalf; + + if (J_CONTRIB) + a_eatom[j] += epairhalf; + } } + if (c.vflag_either) { - fev_tmp.v[0] += 0.5*delx*delx*fpair; - fev_tmp.v[1] += 0.5*dely*dely*fpair; - fev_tmp.v[2] += 0.5*delz*delz*fpair; - fev_tmp.v[3] += 0.5*delx*dely*fpair; - fev_tmp.v[4] += 0.5*delx*delz*fpair; - fev_tmp.v[5] += 0.5*dely*delz*fpair; + const E_FLOAT v0 = delx*delx*fpair; + const E_FLOAT v1 = dely*dely*fpair; + const E_FLOAT v2 = delz*delz*fpair; + const E_FLOAT v3 = delx*dely*fpair; + const E_FLOAT v4 = delx*delz*fpair; + const E_FLOAT v5 = dely*delz*fpair; + + fev_tmp.v[0] += factor*v0; + fev_tmp.v[1] += factor*v1; + fev_tmp.v[2] += factor*v2; + fev_tmp.v[3] += factor*v3; + fev_tmp.v[4] += factor*v4; + fev_tmp.v[5] += factor*v5; + + if (c.vflag_atom) { + if (I_CONTRIB) { + a_vatom(i,0) += 0.5*v0; + a_vatom(i,1) += 0.5*v1; + a_vatom(i,2) += 0.5*v2; + a_vatom(i,3) += 0.5*v3; + a_vatom(i,4) += 0.5*v4; + a_vatom(i,5) += 0.5*v5; + } + if (J_CONTRIB) { + a_vatom(j,0) += 0.5*v0; + a_vatom(j,1) += 0.5*v1; + a_vatom(j,2) += 0.5*v2; + a_vatom(j,3) += 0.5*v3; + a_vatom(j,4) += 0.5*v4; + a_vatom(j,5) += 0.5*v5; + } + } } } },fev); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fev.f[0]; - f(i,1) += fev.f[1]; - f(i,2) += fev.f[2]; + a_f(i,0) += fev.f[0]; + a_f(i,1) += fev.f[1]; + a_f(i,2) += fev.f[2]; if (c.eflag_global) ev.evdwl += fev.evdwl; - if (c.eflag_atom) - d_eatom(i) += fev.evdwl; - if (c.vflag_global) { ev.v[0] += fev.v[0]; ev.v[1] += fev.v[1]; @@ -496,26 +581,37 @@ struct PairComputeFunctor { ev.v[5] += fev.v[5]; } - if (c.vflag_atom) { - d_vatom(i,0) += fev.v[0]; - d_vatom(i,1) += fev.v[1]; - d_vatom(i,2) += fev.v[2]; - d_vatom(i,3) += fev.v[3]; - d_vatom(i,4) += fev.v[4]; - d_vatom(i,5) += fev.v[5]; + if (NEIGHFLAG == FULL) { + + if (c.eflag_atom) + a_eatom(i) += fev.evdwl; + + if (c.vflag_atom) { + a_vatom(i,0) += fev.v[0]; + a_vatom(i,1) += fev.v[1]; + a_vatom(i,2) += fev.v[2]; + a_vatom(i,3) += fev.v[3]; + a_vatom(i,4) += fev.v[4]; + a_vatom(i,5) += fev.v[5]; + } } }); }); return ev; } - // Use TeamPolicy, assume Newton off, Full Neighborlist, and energy/virial + // TeamPolicy, newton off, and energy/virial // Loop over neighbors of one atom with coulomb interaction // This function is called in parallel + KOKKOS_FUNCTION EV_FLOAT compute_item_team_ev(typename Kokkos::TeamPolicy::member_type team, const NeighListKokkos &list, const CoulTag& ) const { + auto a_f = dup_f.template access::value>(); + auto a_eatom = dup_eatom.template access::value>(); + auto a_vatom = dup_vatom.template access::value>(); + EV_FLOAT ev; const int inum = team.league_size(); @@ -566,45 +662,92 @@ struct PairComputeFunctor { if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) fpair+=c.template compute_fcoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - fev_tmp.f[0] += delx*fpair; - fev_tmp.f[1] += dely*fpair; - fev_tmp.f[2] += delz*fpair; + const F_FLOAT fx = delx*fpair; + const F_FLOAT fy = dely*fpair; + const F_FLOAT fz = delz*fpair; + + fev_tmp.f[0] += fx; + fev_tmp.f[1] += fy; + fev_tmp.f[2] += fz; + + const int I_CONTRIB = (NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD); + const int J_CONTRIB = ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal); + const E_FLOAT factor = J_CONTRIB?1.0:0.5; + + if ((NEIGHFLAG == HALF || NEIGHFLAG == HALFTHREAD) && j < c.nlocal) { + a_f(j,0) -= fx; + a_f(j,1) -= fy; + a_f(j,2) -= fz; + } F_FLOAT evdwl = 0.0; F_FLOAT ecoul = 0.0; - if (c.eflag) { + if (c.eflag_either) { if (rsq < (STACKPARAMS?c.m_cut_ljsq[itype][jtype]:c.d_cut_ljsq(itype,jtype))) { evdwl = factor_lj * c.template compute_evdwl(rsq,i,j,itype,jtype); - fev_tmp.evdwl += 0.5*evdwl; + fev_tmp.evdwl += factor * evdwl; } if (rsq < (STACKPARAMS?c.m_cut_coulsq[itype][jtype]:c.d_cut_coulsq(itype,jtype))) { ecoul = c.template compute_ecoul(rsq,i,j,itype,jtype,factor_coul,qtmp); - fev_tmp.ecoul += 0.5*ecoul; + fev_tmp.ecoul += factor * ecoul; + } + + + if (c.eflag_atom) { + const E_FLOAT epairhalf = 0.5 * (evdwl + ecoul); + + if (I_CONTRIB) + a_eatom[i] += epairhalf; + + if (J_CONTRIB) + a_eatom[j] += epairhalf; } } + if (c.vflag_either) { - fev_tmp.v[0] += 0.5*delx*delx*fpair; - fev_tmp.v[1] += 0.5*dely*dely*fpair; - fev_tmp.v[2] += 0.5*delz*delz*fpair; - fev_tmp.v[3] += 0.5*delx*dely*fpair; - fev_tmp.v[4] += 0.5*delx*delz*fpair; - fev_tmp.v[5] += 0.5*dely*delz*fpair; + const E_FLOAT v0 = delx*delx*fpair; + const E_FLOAT v1 = dely*dely*fpair; + const E_FLOAT v2 = delz*delz*fpair; + const E_FLOAT v3 = delx*dely*fpair; + const E_FLOAT v4 = delx*delz*fpair; + const E_FLOAT v5 = dely*delz*fpair; + + fev_tmp.v[0] += factor*v0; + fev_tmp.v[1] += factor*v1; + fev_tmp.v[2] += factor*v2; + fev_tmp.v[3] += factor*v3; + fev_tmp.v[4] += factor*v4; + fev_tmp.v[5] += factor*v5; + + if (c.vflag_atom) { + if (I_CONTRIB) { + a_vatom(i,0) += 0.5*v0; + a_vatom(i,1) += 0.5*v1; + a_vatom(i,2) += 0.5*v2; + a_vatom(i,3) += 0.5*v3; + a_vatom(i,4) += 0.5*v4; + a_vatom(i,5) += 0.5*v5; + } + if (J_CONTRIB) { + a_vatom(j,0) += 0.5*v0; + a_vatom(j,1) += 0.5*v1; + a_vatom(j,2) += 0.5*v2; + a_vatom(j,3) += 0.5*v3; + a_vatom(j,4) += 0.5*v4; + a_vatom(j,5) += 0.5*v5; + } + } } } },fev); Kokkos::single(Kokkos::PerThread(team), [&] () { - f(i,0) += fev.f[0]; - f(i,1) += fev.f[1]; - f(i,2) += fev.f[2]; + a_f(i,0) += fev.f[0]; + a_f(i,1) += fev.f[1]; + a_f(i,2) += fev.f[2]; - if (c.eflag_global) { + if (c.eflag_global) ev.evdwl += fev.evdwl; - ev.ecoul += fev.ecoul; - } - - if (c.eflag_atom) - d_eatom(i) += fev.evdwl + fev.ecoul; if (c.vflag_global) { ev.v[0] += fev.v[0]; @@ -615,13 +758,19 @@ struct PairComputeFunctor { ev.v[5] += fev.v[5]; } - if (c.vflag_atom) { - d_vatom(i,0) += fev.v[0]; - d_vatom(i,1) += fev.v[1]; - d_vatom(i,2) += fev.v[2]; - d_vatom(i,3) += fev.v[3]; - d_vatom(i,4) += fev.v[4]; - d_vatom(i,5) += fev.v[5]; + if (NEIGHFLAG == FULL) { + + if (c.eflag_atom) + a_eatom(i) += fev.evdwl; + + if (c.vflag_atom) { + a_vatom(i,0) += fev.v[0]; + a_vatom(i,1) += fev.v[1]; + a_vatom(i,2) += fev.v[2]; + a_vatom(i,3) += fev.v[3]; + a_vatom(i,4) += fev.v[4]; + a_vatom(i,5) += fev.v[5]; + } } }); }); @@ -636,7 +785,7 @@ struct PairComputeFunctor { auto a_eatom = dup_eatom.template access::value>(); auto a_vatom = dup_vatom.template access::value>(); - const int EFLAG = c.eflag; + const int EFLAG = c.eflag_either; const int NEWTON_PAIR = c.newton_pair; const int VFLAG = c.vflag_either; @@ -657,7 +806,7 @@ struct PairComputeFunctor { const E_FLOAT v5 = dely*delz*fpair; if (c.vflag_global) { - if (NEIGHFLAG!=FULL) { + if (NEIGHFLAG != FULL) { if (NEWTON_PAIR) { ev.v[0] += v0; ev.v[1] += v1; @@ -747,7 +896,8 @@ struct PairComputeFunctor { // This uses the fact that failure to match template parameters is not an error. // By having the enable_if with a ! and without it, exactly one of the functions // pair_compute_neighlist will match - either the dummy version -// or the real one further below. +// or the real one further below + template EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t*> list) { EV_FLOAT ev; From e241f08cfe3c24a49d46cf9e7515b4db335406e4 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 11 Dec 2023 14:05:42 -0700 Subject: [PATCH 060/305] Finish first version of compute reaxff/atom docs --- doc/src/compute.rst | 1 + doc/src/compute_reaxff_atom.rst | 38 ++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/doc/src/compute.rst b/doc/src/compute.rst index 6737203618..6ef093c16d 100644 --- a/doc/src/compute.rst +++ b/doc/src/compute.rst @@ -280,6 +280,7 @@ The individual style names on the :doc:`Commands compute ` pag * :doc:`property/local ` - convert local attributes to local vectors/arrays * :doc:`ptm/atom ` - determines the local lattice structure based on the Polyhedral Template Matching method * :doc:`rdf ` - radial distribution function :math:`g(r)` histogram of group of atoms +* :doc:`reaxff/atom ` - extract ReaxFF bond information * :doc:`reduce ` - combine per-atom quantities into a single global value * :doc:`reduce/chunk ` - reduce per-atom quantities within each chunk * :doc:`reduce/region ` - same as compute reduce, within a region diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst index 4906f3fe5c..9f690d19c3 100644 --- a/doc/src/compute_reaxff_atom.rst +++ b/doc/src/compute_reaxff_atom.rst @@ -1,8 +1,8 @@ -.. index:: fix reaxff/atom -.. index:: fix reaxff/atom/kk +.. index:: compute reaxff/atom +.. index:: compute reaxff/atom/kk -fix reaxff/atom command -======================= +compute reaxff/atom command +=========================== Accelerator Variants: *reaxff/atom/kk* @@ -40,7 +40,35 @@ Examples Description """"""""""" -TODO +Define a computation that extracts bond information computed by the ReaxFF +potential specified by :doc:`pair_style reaxff `. + +By default, it produces per-atom data that includes the following columns: + +* abo = atom bond order (sum of all bonds) +* nlp = number of lone pairs +* nb = number of bonds + +Bonds will only be included if its atoms are in the group. + +In addition, if ``bonds`` is set to ``yes``, the compute will also produce a +local array of all bonds on the current processor whose atoms are in the group. +The columns of each entry of this local array are: + +* id_i = atom i id of bond +* id_j = atom j id of bond +* bo = bond order of bond + +Output info +""""""""""" + +This compute calculates a per-atom array and local array depending on the +number of keywords. The number of rows in the local array is the number of +bonds as described above. Both per-atom and local array have 3 columns. + +The arrays can be accessed by any command that uses local and per-atom values +from a compute as input. See the :doc:`Howto output ` page for +an overview of LAMMPS output options. ---------- From 930fbe8c5df277cb0aa1195a5a7ad031a2585a5c Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Mon, 11 Dec 2023 14:07:48 -0700 Subject: [PATCH 061/305] reaxff/atom: First attempt of filtering by group --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 4 +-- src/KOKKOS/pair_reaxff_kokkos.cpp | 37 +++++++++++++---------- src/KOKKOS/pair_reaxff_kokkos.h | 10 +++--- src/REAXFF/compute_reaxff_atom.cpp | 31 +++++++++++-------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index c703bc3552..c3e9367ff4 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -82,9 +82,9 @@ void ComputeReaxFFAtomKokkos::compute_bonds() int maxnumbonds = 0; if (reaxff->execution_space == Device) - device_pair()->FindBond(maxnumbonds); + device_pair()->FindBond(maxnumbonds, groupbit); else - host_pair()->FindBond(maxnumbonds); + host_pair()->FindBond(maxnumbonds, groupbit); nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 914962f0e6..29ac398128 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4162,22 +4162,23 @@ double PairReaxFFKokkos::memory_usage() /* ---------------------------------------------------------------------- */ template -void PairReaxFFKokkos::FindBond(int &numbonds) +void PairReaxFFKokkos::FindBond(int &numbonds, int groupbit) { copymode = 1; Kokkos::parallel_for(Kokkos::RangePolicy(0,nmax),*this); bo_cut_bond = api->control->bg_cut; - atomKK->sync(execution_space,TAG_MASK); + atomKK->sync(execution_space,TAG_MASK|MASK_MASK); tag = atomKK->k_tag.view(); + mask = atomKK->k_mask.view(); const int inum = list->inum; NeighListKokkos* k_list = static_cast*>(list); d_ilist = k_list->d_ilist; numbonds = 0; - PairReaxKokkosFindBondFunctor find_bond_functor(this); + PairReaxKokkosFindBondFunctor find_bond_functor(this, groupbit); Kokkos::parallel_reduce(inum,find_bond_functor,numbonds); copymode = 0; } @@ -4194,24 +4195,28 @@ void PairReaxFFKokkos::operator()(TagPairReaxFindBondZero, const int template KOKKOS_INLINE_FUNCTION -void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbonds) const +void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbonds, int groupbit) const { const int i = d_ilist[ii]; int nj = 0; - const int j_start = d_bo_first[i]; - const int j_end = j_start + d_bo_num[i]; - for (int jj = j_start; jj < j_end; jj++) { - int j = d_bo_list[jj]; - j &= NEIGHMASK; - const tagint jtag = tag[j]; - const int j_index = jj - j_start; - double bo_tmp = d_BO(i,j_index); + if(mask[i] & groupbit) { + const int j_start = d_bo_first[i]; + const int j_end = j_start + d_bo_num[i]; + for (int jj = j_start; jj < j_end; jj++) { + int j = d_bo_list[jj]; + j &= NEIGHMASK; + if(mask[j] & groupbit) { + const tagint jtag = tag[j]; + const int j_index = jj - j_start; + double bo_tmp = d_BO(i,j_index); - if (bo_tmp > bo_cut_bond) { - d_neighid(i,nj) = jtag; - d_abo(i,nj) = bo_tmp; - nj++; + if (bo_tmp > bo_cut_bond) { + d_neighid(i,nj) = jtag; + d_abo(i,nj) = bo_tmp; + nj++; + } + } } } d_numneigh_bonds[i] = nj; diff --git a/src/KOKKOS/pair_reaxff_kokkos.h b/src/KOKKOS/pair_reaxff_kokkos.h index f246afcc86..f54350b04b 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.h +++ b/src/KOKKOS/pair_reaxff_kokkos.h @@ -133,7 +133,7 @@ class PairReaxFFKokkos : public PairReaxFF { void compute(int, int); void init_style(); double memory_usage(); - void FindBond(int &); + void FindBond(int &, int groupbit = 1); void PackBondBuffer(DAT::tdual_ffloat_1d, int &); void PackReducedBondBuffer(DAT::tdual_ffloat_1d, int &, bool); void FindBondSpecies(); @@ -288,7 +288,7 @@ class PairReaxFFKokkos : public PairReaxFF { void operator()(TagPairReaxFindBondZero, const int&) const; KOKKOS_INLINE_FUNCTION - void calculate_find_bond_item(int, int&) const; + void calculate_find_bond_item(int, int&, int) const; KOKKOS_INLINE_FUNCTION void pack_bond_buffer_item(int, int&, const bool&) const; @@ -417,6 +417,7 @@ class PairReaxFFKokkos : public PairReaxFF { typename AT::t_f_array f; typename AT::t_int_1d_randomread type; typename AT::t_tagint_1d_randomread tag; + typename AT::t_int_1d_randomread mask; typename AT::t_float_1d_randomread q; typename AT::t_tagint_1d_randomread molecule; @@ -526,8 +527,9 @@ template struct PairReaxKokkosFindBondFunctor { typedef DeviceType device_type; typedef int value_type; + int groupbit; PairReaxFFKokkos c; - PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr):c(*c_ptr) {}; + PairReaxKokkosFindBondFunctor(PairReaxFFKokkos* c_ptr, int groupbit):c(*c_ptr),groupbit(groupbit) {}; KOKKOS_INLINE_FUNCTION void join(int &dst, @@ -537,7 +539,7 @@ struct PairReaxKokkosFindBondFunctor { KOKKOS_INLINE_FUNCTION void operator()(const int ii, int &numbonds) const { - c.calculate_find_bond_item(ii,numbonds); + c.calculate_find_bond_item(ii,numbonds,groupbit); } }; diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 2e5d4058b8..fba67ecacf 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -126,28 +126,33 @@ int ComputeReaxFFAtom::FindBond() bo_cut = reaxff->api->control->bg_cut; tagint *tag = atom->tag; + int * mask = atom->mask; int numbonds = 0; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; - nj = 0; + if (mask[i] & groupbit) { + nj = 0; - for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { - bo_ij = &(reaxff->api->lists->select.bond_list[pj]); - j = bo_ij->nbr; - jtag = tag[j]; - bo_tmp = bo_ij->bo_data.BO; + for (pj = Start_Index(i, reaxff->api->lists); pj < End_Index(i, reaxff->api->lists); ++pj) { + bo_ij = &(reaxff->api->lists->select.bond_list[pj]); + j = bo_ij->nbr; + if (mask[j] & groupbit) { + jtag = tag[j]; + bo_tmp = bo_ij->bo_data.BO; - if (bo_tmp > bo_cut) { - if(store_bonds) { - neighid[i][nj] = jtag; - abo[i][nj] = bo_tmp; + if (bo_tmp > bo_cut) { + if(store_bonds) { + neighid[i][nj] = jtag; + abo[i][nj] = bo_tmp; + } + nj++; + } } - nj++; } + bondcount[i] = nj; + numbonds += nj; } - bondcount[i] = nj; - numbonds += nj; } return numbonds; } From 0cf4c9e7a32585ca7c78d4fd4df4f50c89d83d9b Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 13 Dec 2023 11:07:42 -0700 Subject: [PATCH 062/305] Whitespace --- src/KOKKOS/compute_reaxff_atom_kokkos.cpp | 13 ++++++------- src/KOKKOS/pair_reaxff_kokkos.cpp | 6 +++--- src/REAXFF/compute_reaxff_atom.cpp | 17 +++++++---------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp index c3e9367ff4..8dbcb9441e 100644 --- a/src/KOKKOS/compute_reaxff_atom_kokkos.cpp +++ b/src/KOKKOS/compute_reaxff_atom_kokkos.cpp @@ -42,7 +42,6 @@ ComputeReaxFFAtomKokkos::ComputeReaxFFAtomKokkos(LAMMPS *lmp, int na kokkosable = 1; } - /* ---------------------------------------------------------------------- */ template @@ -58,7 +57,7 @@ void ComputeReaxFFAtomKokkos::init() { ComputeReaxFFAtom::init(); - if(!reaxff || !reaxff->kokkosable) { + if (!reaxff || !reaxff->kokkosable) { error->all(FLERR,"Cannot use compute reaxff/atom/kk without " "pair_style reaxff/kk"); } @@ -88,7 +87,7 @@ void ComputeReaxFFAtomKokkos::compute_bonds() nbuf = ((store_bonds ? maxnumbonds*2 : 0) + 3)*nlocal; - if(!buf || k_buf.extent(0) < nbuf) { + if (!buf || k_buf.extent(0) < nbuf) { memoryKK->destroy_kokkos(k_buf, buf); memoryKK->create_kokkos(k_buf, buf, nbuf, "reaxff/atom:buf"); } @@ -119,10 +118,10 @@ void ComputeReaxFFAtomKokkos::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) + if (invoked_bonds < update->ntimestep) compute_bonds(); - if(nbonds > prev_nbonds) { + if (nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); @@ -158,7 +157,7 @@ void ComputeReaxFFAtomKokkos::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) + if (invoked_bonds < update->ntimestep) compute_bonds(); // extract peratom bond information from buffer @@ -182,7 +181,7 @@ template double ComputeReaxFFAtomKokkos::memory_usage() { double bytes = (double)(nlocal*3) * sizeof(double); - if(store_bonds) + if (store_bonds) bytes += (double)(nbonds*3) * sizeof(double); bytes += (double)(nbuf > 0 ? nbuf * sizeof(double) : 0); return bytes; diff --git a/src/KOKKOS/pair_reaxff_kokkos.cpp b/src/KOKKOS/pair_reaxff_kokkos.cpp index 29ac398128..11a40970c2 100644 --- a/src/KOKKOS/pair_reaxff_kokkos.cpp +++ b/src/KOKKOS/pair_reaxff_kokkos.cpp @@ -4200,13 +4200,13 @@ void PairReaxFFKokkos::calculate_find_bond_item(int ii, int &numbond const int i = d_ilist[ii]; int nj = 0; - if(mask[i] & groupbit) { + if (mask[i] & groupbit) { const int j_start = d_bo_first[i]; const int j_end = j_start + d_bo_num[i]; for (int jj = j_start; jj < j_end; jj++) { int j = d_bo_list[jj]; j &= NEIGHMASK; - if(mask[j] & groupbit) { + if (mask[j] & groupbit) { const tagint jtag = tag[j]; const int j_index = jj - j_start; double bo_tmp = d_BO(i,j_index); @@ -4262,7 +4262,7 @@ void PairReaxFFKokkos::PackReducedBondBuffer(DAT::tdual_ffloat_1d k_ copymode = 1; nlocal = atomKK->nlocal; - if(store_bonds) { + if (store_bonds) { PairReaxKokkosPackReducedBondBufferFunctor pack_bond_buffer_functor(this); Kokkos::parallel_scan(nlocal,pack_bond_buffer_functor); } else { diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index fba67ecacf..1b54f3b82a 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -79,7 +79,6 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : local_flag = store_bonds; } - /* ---------------------------------------------------------------------- */ ComputeReaxFFAtom::~ComputeReaxFFAtom() @@ -107,7 +106,7 @@ void ComputeReaxFFAtom::init() if (!reaxff) error->all(FLERR,"Cannot use compute reaxff/atom without " "pair_style reaxff or reaxff/omp"); - if(reaxff->kokkosable && !kokkosable) + if (reaxff->kokkosable && !kokkosable) error->all(FLERR,"Cannot use compute reaxff/atom with pair_style reaxff/kk. Use reaxff/atom/kk."); } @@ -142,7 +141,7 @@ int ComputeReaxFFAtom::FindBond() bo_tmp = bo_ij->bo_data.BO; if (bo_tmp > bo_cut) { - if(store_bonds) { + if (store_bonds) { neighid[i][nj] = jtag; abo[i][nj] = bo_tmp; } @@ -157,7 +156,6 @@ int ComputeReaxFFAtom::FindBond() return numbonds; } - /* ---------------------------------------------------------------------- */ void ComputeReaxFFAtom::compute_bonds() @@ -170,7 +168,7 @@ void ComputeReaxFFAtom::compute_bonds() memory->destroy(bondcount); memory->destroy(array_atom); nlocal = atom->nlocal; - if(store_bonds) { + if (store_bonds) { memory->create(abo, nlocal, MAXREAXBOND, "reaxff/atom:abo"); memory->create(neighid, nlocal, MAXREAXBOND, "reaxff/atom:neighid"); } @@ -195,11 +193,10 @@ void ComputeReaxFFAtom::compute_local() { invoked_local = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if (invoked_bonds < update->ntimestep) compute_bonds(); - } - if(nbonds > prev_nbonds) { + if (nbonds > prev_nbonds) { // grow array_local memory->destroy(array_local); memory->create(array_local, nbonds, 3, "reaxff/atom:array_local"); @@ -229,7 +226,7 @@ void ComputeReaxFFAtom::compute_peratom() { invoked_peratom = update->ntimestep; - if(invoked_bonds < update->ntimestep) { + if (invoked_bonds < update->ntimestep) { compute_bonds(); } @@ -249,7 +246,7 @@ double ComputeReaxFFAtom::memory_usage() { double bytes = (double)(nlocal*3) * sizeof(double); bytes += (double)(nlocal) * sizeof(int); - if(store_bonds) { + if (store_bonds) { bytes += (double)(2*nlocal*MAXREAXBOND) * sizeof(double); bytes += (double)(nbonds*3) * sizeof(double); } From b199368c19bdc5f1b5f0fc991473ae7de3f73d31 Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Thu, 14 Dec 2023 10:46:32 +1000 Subject: [PATCH 063/305] add extract function to fix_property_atom --- src/fix_property_atom.cpp | 24 ++++++++++++++++++++++++ src/fix_property_atom.h | 1 + 2 files changed, 25 insertions(+) diff --git a/src/fix_property_atom.cpp b/src/fix_property_atom.cpp index 9613523059..1e004ae4cb 100644 --- a/src/fix_property_atom.cpp +++ b/src/fix_property_atom.cpp @@ -948,3 +948,27 @@ int FixPropertyAtom::size_restart(int /*nlocal*/) { return values_peratom + 1; } + +/* ---------------------------------------------------------------------- + extract fix property/atom properties +------------------------------------------------------------------------- */ + +void *FixPropertyAtom::extract(const char *str, int &dim) +{ + dim=0; + if (strcmp(str, "nvalue") == 0) { + return &nvalue; + } else if (strcmp(str, "border") == 0) { + return &border; + } + dim=1; + if (strcmp(str, "styles") == 0) { + return &styles; + } else if (strcmp(str, "index") == 0) { + return &index; + } else if (strcmp(str, "cols") == 0) { + return &cols; + } + return nullptr; +} + diff --git a/src/fix_property_atom.h b/src/fix_property_atom.h index c50b6049dc..820acf3a20 100644 --- a/src/fix_property_atom.h +++ b/src/fix_property_atom.h @@ -51,6 +51,7 @@ class FixPropertyAtom : public Fix { void unpack_restart(int, int) override; int size_restart(int) override; int maxsize_restart() override; + void *extract(const char *, int &) override; double memory_usage() override; protected: From 61fd4d643b492cb55217a2a7d285b41d38b46f44 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:20:08 -0500 Subject: [PATCH 064/305] correct logic for "cannot happen" case --- src/EXTRA-COMPUTE/compute_slcsa_atom.cpp | 126 +++++++++++------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp b/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp index f12551085c..509362a73b 100644 --- a/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp +++ b/src/EXTRA-COMPUTE/compute_slcsa_atom.cpp @@ -305,84 +305,84 @@ void ComputeSLCSAAtom::compute_peratom() int *mask = atom->mask; int nlocal = atom->nlocal; - double **compute_array; if (descriptorval.which == ArgInfo::COMPUTE) { if (!(descriptorval.val.c->invoked_flag & Compute::INVOKED_PERATOM)) { descriptorval.val.c->compute_peratom(); descriptorval.val.c->invoked_flag |= Compute::INVOKED_PERATOM; } - compute_array = descriptorval.val.c->array_atom; - } + double **compute_array = descriptorval.val.c->array_atom; - memory->create(full_descriptor, ncomps, "slcsa/atom:local descriptor"); - memory->create(projected_descriptor, nclasses - 1, "slcsa/atom:reduced descriptor"); - memory->create(scores, nclasses, "slcsa/atom:scores"); - memory->create(probas, nclasses, "slcsa/atom:probas"); - memory->create(prodright, nclasses - 1, "slcsa/atom:prodright"); - memory->create(dmaha, nclasses, "slcsa/atom:prodright"); + memory->create(full_descriptor, ncomps, "slcsa/atom:local descriptor"); + memory->create(projected_descriptor, nclasses - 1, "slcsa/atom:reduced descriptor"); + memory->create(scores, nclasses, "slcsa/atom:scores"); + memory->create(probas, nclasses, "slcsa/atom:probas"); + memory->create(prodright, nclasses - 1, "slcsa/atom:prodright"); + memory->create(dmaha, nclasses, "slcsa/atom:prodright"); - for (int i = 0; i < nlocal; i++) { - if (mask[i] & groupbit) { - for (int j = 0; j < ncomps; j++) { full_descriptor[j] = compute_array[i][j]; } - // Here comes the LDA + LR process - // 1st step : Retrieve mean database descriptor - for (int j = 0; j < ncomps; j++) { full_descriptor[j] -= database_mean_descriptor[j]; } - // 2nd step : Matrix multiplication to go from ncompsx1 -> (nclasses-1)*1 - for (int j = 0; j < nclasses - 1; j++) { - projected_descriptor[j] = 0.; - for (int k = 0; k < ncomps; k++) { - projected_descriptor[j] += full_descriptor[k] * lda_scalings[k][j]; - } - } - // 3rd step : Matrix multiplication - for (int j = 0; j < nclasses; j++) { - scores[j] = lr_bias[j]; - for (int k = 0; k < nclasses - 1; k++) { - scores[j] += lr_decision[j][k] * projected_descriptor[k]; - } - } - // 4th step : Matrix multiplication - double sumexpscores = 0.; - for (int j = 0; j < nclasses; j++) sumexpscores += exp(scores[j]); - for (int j = 0; j < nclasses; j++) { probas[j] = exp(scores[j]) / sumexpscores; } - - classification[i][nclasses] = argmax(probas, nclasses); - - // 5th step : Mahalanobis distance - for (int j = 0; j < nclasses; j++) { - prodright[0] = 0.; - prodright[1] = 0.; - prodright[2] = 0.; - for (int k = 0; k < nclasses - 1; k++) { - for (int l = 0; l < nclasses - 1; l++) { - prodright[k] += - (icov_list[j][k][l] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + for (int j = 0; j < ncomps; j++) full_descriptor[j] = compute_array[i][j]; + // Here comes the LDA + LR process + // 1st step : Retrieve mean database descriptor + for (int j = 0; j < ncomps; j++) full_descriptor[j] -= database_mean_descriptor[j]; + // 2nd step : Matrix multiplication to go from ncompsx1 -> (nclasses-1)*1 + for (int j = 0; j < nclasses - 1; j++) { + projected_descriptor[j] = 0.0; + for (int k = 0; k < ncomps; k++) { + projected_descriptor[j] += full_descriptor[k] * lda_scalings[k][j]; } } - double prodleft = 0.; - for (int k = 0; k < nclasses - 1; k++) { - prodleft += (prodright[k] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + // 3rd step : Matrix multiplication + for (int j = 0; j < nclasses; j++) { + scores[j] = lr_bias[j]; + for (int k = 0; k < nclasses - 1; k++) { + scores[j] += lr_decision[j][k] * projected_descriptor[k]; + } } - classification[i][j] = sqrt(prodleft); - } - // 6th step : Sanity check - int locclass = classification[i][nclasses]; + // 4th step : Matrix multiplication + double sumexpscores = 0.0; + for (int j = 0; j < nclasses; j++) sumexpscores += exp(scores[j]); + for (int j = 0; j < nclasses; j++) probas[j] = exp(scores[j]) / sumexpscores; - if (classification[i][locclass] > maha_thresholds[locclass]) { - classification[i][nclasses] = -1.; - } + classification[i][nclasses] = argmax(probas, nclasses); - } else { - for (int j = 0; j < ncols; j++) { classification[i][j] = -1.; } + // 5th step : Mahalanobis distance + for (int j = 0; j < nclasses; j++) { + prodright[0] = 0.0; + prodright[1] = 0.0; + prodright[2] = 0.0; + for (int k = 0; k < nclasses - 1; k++) { + for (int l = 0; l < nclasses - 1; l++) { + prodright[k] += (icov_list[j][k][l] * + (projected_descriptor[k] - mean_projected_descriptors[j][k])); + } + } + double prodleft = 0.0; + for (int k = 0; k < nclasses - 1; k++) { + prodleft += + (prodright[k] * (projected_descriptor[k] - mean_projected_descriptors[j][k])); + } + classification[i][j] = sqrt(prodleft); + } + // 6th step : Sanity check + int locclass = classification[i][nclasses]; + + if (classification[i][locclass] > maha_thresholds[locclass]) { + classification[i][nclasses] = -1.0; + } + + } else { + for (int j = 0; j < ncols; j++) classification[i][j] = -1.0; + } } + memory->destroy(full_descriptor); + memory->destroy(projected_descriptor); + memory->destroy(scores); + memory->destroy(probas); + memory->destroy(prodright); + memory->destroy(dmaha); } - memory->destroy(full_descriptor); - memory->destroy(projected_descriptor); - memory->destroy(scores); - memory->destroy(probas); - memory->destroy(prodright); - memory->destroy(dmaha); } int ComputeSLCSAAtom::compute_ncomps(int twojmax) From 48b5e16f5dfbe46bbed13c01ab9aba705975a7ee Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:20:47 -0500 Subject: [PATCH 065/305] remove unused class members, initialize pointers, adjust programming style --- src/ML-PACE/compute_pace.cpp | 67 ++++++++++++++++-------------------- src/ML-PACE/compute_pace.h | 4 +-- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index cf3be8f47f..12693a58d9 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -45,10 +45,10 @@ struct ACECimpl { using namespace LAMMPS_NS; -enum{SCALAR,VECTOR,ARRAY}; +enum { SCALAR, VECTOR, ARRAY }; ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), pace(nullptr), - paceall(nullptr), pace_peratom(nullptr), map(nullptr), cg(nullptr) + Compute(lmp, narg, arg), cutsq(nullptr), list(nullptr), pace(nullptr), paceall(nullptr), + pace_peratom(nullptr), map(nullptr), cg(nullptr), c_pe(nullptr), c_virial(nullptr) { array_flag = 1; extarray = 0; @@ -71,33 +71,27 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : //read in file with CG coefficients or c_tilde coefficients auto potential_file_name = utils::get_potential_file_path(arg[3]); - delete acecimpl -> basis_set; - acecimpl -> basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl -> basis_set->cutoffmax; - cutmax = acecimpl -> basis_set->cutoffmax; + delete acecimpl->basis_set; + acecimpl->basis_set = new ACECTildeBasisSet(potential_file_name); + double cut = acecimpl->basis_set->cutoffmax; + cutmax = acecimpl->basis_set->cutoffmax; double cuti; double radelemall = 0.5; //# of rank 1, rank > 1 functions int n_r1, n_rp = 0; - n_r1 = acecimpl -> basis_set->total_basis_size_rank1[0]; - n_rp = acecimpl -> basis_set->total_basis_size[0]; + n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; + n_rp = acecimpl->basis_set->total_basis_size[0]; int ncoeff = n_r1 + n_rp; - - //int nvalues = ncoeff; - nvalues = ncoeff; - //----------------------------------------------------------- - //nperdim = ncoeff; - ndims_force = 3; ndims_virial = 6; bik_rows = 1; - yoffset = nvalues; //nperdim; - zoffset = 2*nvalues; //nperdim; + yoffset = nvalues; + zoffset = 2*nvalues; natoms = atom->natoms; if (bikflag) bik_rows = natoms; dgrad_rows = ndims_force*natoms; @@ -255,28 +249,27 @@ void ComputePACE::compute_array() const int typeoffset_local = ndims_peratom*nvalues*(itype-1); const int typeoffset_global = nvalues*(itype-1); - delete acecimpl -> ace; - acecimpl -> ace = new ACECTildeEvaluator(*acecimpl -> basis_set); - acecimpl -> ace->compute_projections = 1; - acecimpl -> ace->compute_b_grad = 1; + delete acecimpl->ace; + acecimpl->ace = new ACECTildeEvaluator(*acecimpl->basis_set); + acecimpl->ace->compute_projections = 1; + acecimpl->ace->compute_b_grad = 1; int n_r1, n_rp = 0; - n_r1 = acecimpl -> basis_set->total_basis_size_rank1[0]; - n_rp = acecimpl -> basis_set->total_basis_size[0]; + n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; + n_rp = acecimpl->basis_set->total_basis_size[0]; int ncoeff = n_r1 + n_rp; - acecimpl -> ace->element_type_mapping.init(ntypes+1); + acecimpl->ace->element_type_mapping.init(ntypes+1); for (int ik = 1; ik <= ntypes; ik++) { - for(int mu = 0; mu < acecimpl -> basis_set ->nelements; mu++){ + for(int mu = 0; mu < acecimpl->basis_set->nelements; mu++){ if (mu != -1) { if (mu == ik - 1) { map[ik] = mu; - acecimpl -> ace->element_type_mapping(ik) = mu; + acecimpl->ace->element_type_mapping(ik) = mu; } } } } - if (dgradflag) { // dBi/dRi tags @@ -307,9 +300,9 @@ void ComputePACE::compute_array() } // resize the neighbor cache after setting the basis - acecimpl -> ace->resize_neighbours_cache(max_jnum); - acecimpl -> ace->compute_atom(i, atom->x, atom->type, list->numneigh[i], list->firstneigh[i]); - Array1D Bs = acecimpl -> ace -> projections; + acecimpl->ace->resize_neighbours_cache(max_jnum); + acecimpl->ace->compute_atom(i, atom->x, atom->type, list->numneigh[i], list->firstneigh[i]); + Array1D Bs = acecimpl->ace->projections; for (int jj = 0; jj < jnum; jj++) { const int j = jlist[jj]; @@ -322,9 +315,9 @@ void ComputePACE::compute_array() // dimension: (n_descriptors,max_jnum,3) //example to access entries for neighbour jj after running compute_atom for atom i: for (int func_ind =0; func_ind < n_r1 + n_rp; func_ind++){ - DOUBLE_TYPE fx_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,0); - DOUBLE_TYPE fy_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,1); - DOUBLE_TYPE fz_dB = acecimpl -> ace -> neighbours_dB(func_ind,jj,2); + DOUBLE_TYPE fx_dB = acecimpl->ace->neighbours_dB(func_ind,jj,0); + DOUBLE_TYPE fy_dB = acecimpl->ace->neighbours_dB(func_ind,jj,1); + DOUBLE_TYPE fz_dB = acecimpl->ace->neighbours_dB(func_ind,jj,2); pacedi[func_ind] += fx_dB; pacedi[func_ind+yoffset] += fy_dB; pacedi[func_ind+zoffset] += fz_dB; @@ -333,15 +326,13 @@ void ComputePACE::compute_array() pacedj[func_ind+zoffset] -= fz_dB; } } else { - //printf("inside dBi/dRj logical : ncoeff = %d \n", ncoeff); for (int iicoeff = 0; iicoeff < ncoeff; iicoeff++) { // add to pace array for this proc - //printf("inside dBi/dRj loop\n"); // dBi/dRj - DOUBLE_TYPE fx_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,0); - DOUBLE_TYPE fy_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,1); - DOUBLE_TYPE fz_dB = acecimpl -> ace -> neighbours_dB(iicoeff,jj,2); + DOUBLE_TYPE fx_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,0); + DOUBLE_TYPE fy_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,1); + DOUBLE_TYPE fz_dB = acecimpl->ace->neighbours_dB(iicoeff,jj,2); pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 0][iicoeff+3] -= fx_dB; pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 1][iicoeff+3] -= fy_dB; pace[bik_rows + ((atom->tag[j]-1)*3*natoms) + 3*(atom->tag[i]-1) + 2][iicoeff+3] -= fz_dB; diff --git a/src/ML-PACE/compute_pace.h b/src/ML-PACE/compute_pace.h index 2f2f8d7f21..496c8a16d3 100644 --- a/src/ML-PACE/compute_pace.h +++ b/src/ML-PACE/compute_pace.h @@ -35,15 +35,13 @@ class ComputePACE : public Compute { private: int natoms, nmax, size_peratom, lastcol; - int ncoeff, nvalues, nperdim, yoffset, zoffset; + int nvalues, yoffset, zoffset; int ndims_peratom, ndims_force, ndims_virial; double **cutsq; class NeighList *list; double **pace, **paceall; double **pace_peratom; - double rcutfac; int *map; // map types to [0,nelements) - int nelements, chemflag; int bikflag, bik_rows, dgradflag, dgrad_rows; double *cg; double cutmax; From 9c01d64804cfd5265924ac62b4e4ea3dca54049f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Wed, 13 Dec 2023 19:21:06 -0500 Subject: [PATCH 066/305] avoid accessing uninitialized data --- src/ML-SNAP/compute_sna_atom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ML-SNAP/compute_sna_atom.cpp b/src/ML-SNAP/compute_sna_atom.cpp index 753751690d..3fe3cca92d 100644 --- a/src/ML-SNAP/compute_sna_atom.cpp +++ b/src/ML-SNAP/compute_sna_atom.cpp @@ -627,7 +627,7 @@ double ComputeSNAAtom::sum_weights(double * rsq, double * w, int ncounts) double ComputeSNAAtom::get_target_rcut(double S_target, double * rsq, double rcut, int ncounts, int weightmode, double delta) { - double S_sol; + double S_sol = 0.0; if (weightmode == 0) { double * www = weights(rsq, rcut, ncounts); S_sol = sum_weights(rsq, www, ncounts); From 757f28f8bf649729d4563e66b6a55c17470ff0cc Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 10:59:30 -0500 Subject: [PATCH 067/305] correct info about fix halt default setting --- doc/src/fix_halt.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/fix_halt.rst b/doc/src/fix_halt.rst index 4231c77cc5..25331804aa 100644 --- a/doc/src/fix_halt.rst +++ b/doc/src/fix_halt.rst @@ -183,4 +183,4 @@ Related commands Default """"""" -The option defaults are error = hard, message = yes, and path = ".". +The option defaults are error = soft, message = yes, and path = ".". From 727c3c95723391bd1bdc2dce1dee7332ac7748f2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:15:11 -0500 Subject: [PATCH 068/305] move call to determine free disk space to platform namespace --- src/fix_halt.cpp | 6 +++--- src/platform.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/platform.h | 9 +++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/fix_halt.cpp b/src/fix_halt.cpp index fcfefe102d..36d660c90d 100644 --- a/src/fix_halt.cpp +++ b/src/fix_halt.cpp @@ -170,8 +170,8 @@ void FixHalt::init() // check if disk limit is supported if (attribute == DISKFREE) { - if (diskfree() < 0.0) - error->all(FLERR,"Disk limit not supported by OS or illegal path"); + if (!dlimit_path || platform::disk_free(dlimit_path) < 0.0) + error->all(FLERR, "Disk limit not supported by OS or illegal path"); } } @@ -196,7 +196,7 @@ void FixHalt::end_of_step() if (update->ntimestep != nextstep) return; attvalue = tlimit(); } else if (attribute == DISKFREE) { - attvalue = diskfree(); + attvalue = platform::disk_free(dlimit_path) / 1048576.0; // MBytes } else if (attribute == BONDMAX) { attvalue = bondmax(); } else { diff --git a/src/platform.cpp b/src/platform.cpp index 064f142425..c8a964076f 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -42,6 +42,7 @@ #define PSAPI_VERSION 2 #include +#include #include // for _get_osfhandle() #include #include @@ -61,6 +62,13 @@ #include #include #endif + +// for disk_free() +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif + //////////////////////////////////////////////////////////////////////// #include @@ -1047,6 +1055,35 @@ bool platform::file_is_readable(const std::string &path) } return false; } +/* ---------------------------------------------------------------------- + determine available disk space, if supported. Return -1 if not. +------------------------------------------------------------------------- */ + +double platform::disk_free(const std::string &path) +{ + double disk_free = -1.0; + +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) + struct statvfs fs; + + if (path.size()) { + int rv = statvfs(path.c_str(), &fs); + if (rv == 0) { +#if defined(__linux__) + disk_free = fs.f_bavail * fs.f_bsize; +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) + disk_free = fs.f_bavail * fs.f_frsize; +#endif + } + } +#else define(_WIN32) + bigint free_bytes; + if (GetDiskFreeSpaceEx(path.c_str(), &free_bytes, nullptr, nullptr)) disk_free = free_bytes; +#endif + return disk_free; +} /* ---------------------------------------------------------------------- check if filename has a known compression extension diff --git a/src/platform.h b/src/platform.h index 036074c900..4328f873dd 100644 --- a/src/platform.h +++ b/src/platform.h @@ -377,6 +377,15 @@ namespace platform { bool file_is_readable(const std::string &path); + /*! Return free disk space in bytes of file system pointed to by path + * + * Returns -1.0 if the path is invalid or free space reporting not supported. + * + * \param path file or folder path in file system + * \return */ + + double disk_free(const std::string &path); + /*! Check if a file name ends in a known extension for a compressed file format * * Currently supported file extensions are: .gz, .bz2, .zst, .xz, .lzma, lz4 From 298ce1863c493023468c11caa443761c75ee3ded Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:15:28 -0500 Subject: [PATCH 069/305] coding style updates --- src/fix_halt.cpp | 154 +++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 92 deletions(-) diff --git a/src/fix_halt.cpp b/src/fix_halt.cpp index 36d660c90d..b34c79867f 100644 --- a/src/fix_halt.cpp +++ b/src/fix_halt.cpp @@ -1,4 +1,3 @@ -// clang-format off /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories @@ -31,49 +30,49 @@ using namespace LAMMPS_NS; using namespace FixConst; -enum{BONDMAX,TLIMIT,DISKFREE,VARIABLE}; -enum{LT,LE,GT,GE,EQ,NEQ,XOR}; -enum{HARD,SOFT,CONTINUE}; -enum{NOMSG=0,YESMSG=1}; +enum { BONDMAX, TLIMIT, DISKFREE, VARIABLE }; +enum { LT, LE, GT, GE, EQ, NEQ, XOR }; +enum { HARD, SOFT, CONTINUE }; +enum { NOMSG = 0, YESMSG = 1 }; /* ---------------------------------------------------------------------- */ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : - Fix(lmp, narg, arg), idvar(nullptr), dlimit_path(nullptr) + Fix(lmp, narg, arg), idvar(nullptr), dlimit_path(nullptr) { - if (narg < 7) error->all(FLERR,"Illegal fix halt command"); - nevery = utils::inumeric(FLERR,arg[3],false,lmp); - if (nevery <= 0) error->all(FLERR,"Illegal fix halt command"); + if (narg < 7) utils::missing_cmd_args(FLERR, "fix halt", error); + nevery = utils::inumeric(FLERR, arg[3], false, lmp); + if (nevery <= 0) error->all(FLERR, "Illegal fix halt command: nevery must be > 0"); // comparison args idvar = nullptr; int iarg = 4; - if (strcmp(arg[iarg],"tlimit") == 0) { + if (strcmp(arg[iarg], "tlimit") == 0) { attribute = TLIMIT; - } else if (strcmp(arg[iarg],"diskfree") == 0) { + } else if (strcmp(arg[iarg], "diskfree") == 0) { attribute = DISKFREE; dlimit_path = utils::strdup("."); - } else if (strcmp(arg[iarg],"bondmax") == 0) { + } else if (strcmp(arg[iarg], "bondmax") == 0) { attribute = BONDMAX; } else { - ArgInfo argi(arg[iarg],ArgInfo::VARIABLE); + ArgInfo argi(arg[iarg], ArgInfo::VARIABLE); - if ((argi.get_type() == ArgInfo::UNKNOWN) - || (argi.get_type() == ArgInfo::NONE) - || (argi.get_dim() != 0)) - error->all(FLERR,"Invalid fix halt attribute"); + if ((argi.get_type() == ArgInfo::UNKNOWN) || (argi.get_type() == ArgInfo::NONE) || + (argi.get_dim() != 0)) + error->all(FLERR, "Invalid fix halt attribute {}", arg[iarg]); attribute = VARIABLE; idvar = argi.copy_name(); ivar = input->variable->find(idvar); - if (ivar < 0) error->all(FLERR,"Could not find fix halt variable name"); + if (ivar < 0) error->all(FLERR, "Could not find fix halt variable name"); if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR,"Fix halt variable is not equal-style variable"); + error->all(FLERR, "Fix halt variable is not equal-style variable"); } + // clang-format off ++iarg; if (strcmp(arg[iarg],"<") == 0) operation = LT; else if (strcmp(arg[iarg],"<=") == 0) operation = LE; @@ -85,7 +84,7 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : else error->all(FLERR,"Invalid fix halt operator"); ++iarg; - value = utils::numeric(FLERR,arg[iarg],false,lmp); + value = utils::numeric(FLERR, arg[iarg], false, lmp); // parse optional args @@ -93,38 +92,40 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : msgflag = YESMSG; ++iarg; while (iarg < narg) { - if (strcmp(arg[iarg],"error") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); - if (strcmp(arg[iarg+1],"hard") == 0) eflag = HARD; - else if (strcmp(arg[iarg+1],"soft") == 0) eflag = SOFT; - else if (strcmp(arg[iarg+1],"continue") == 0) eflag = CONTINUE; - else error->all(FLERR,"Illegal fix halt command"); + if (strcmp(arg[iarg], "error") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt error", error); + if (strcmp(arg[iarg + 1], "hard") == 0) eflag = HARD; + else if (strcmp(arg[iarg + 1], "soft") == 0) eflag = SOFT; + else if (strcmp(arg[iarg + 1], "continue") == 0) eflag = CONTINUE; + else error->all(FLERR, "Unknown fix halt error condition {}", arg[iarg]); iarg += 2; - } else if (strcmp(arg[iarg],"message") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); - msgflag = utils::logical(FLERR,arg[iarg+1],false,lmp); + } else if (strcmp(arg[iarg], "message") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt message", error); + msgflag = utils::logical(FLERR, arg[iarg + 1], false, lmp); iarg += 2; - } else if (strcmp(arg[iarg],"path") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal fix halt command"); + } else if (strcmp(arg[iarg], "path") == 0) { + if (iarg + 2 > narg) utils::missing_cmd_args(FLERR, "fix halt error", error); ++iarg; delete[] dlimit_path; // strip off outer quotes, if present - int len = strlen(arg[iarg])+1; - if ( ((arg[iarg][0] == '"') || (arg[iarg][0] == '\'')) - && (arg[iarg][0] == arg[iarg][len-2])) { - arg[iarg][len-2] = '\0'; - dlimit_path = utils::strdup(arg[iarg]+1); - } else dlimit_path = utils::strdup(arg[iarg]); + int len = strlen(arg[iarg]) + 1; + if (((arg[iarg][0] == '"') || (arg[iarg][0] == '\'')) && + (arg[iarg][0] == arg[iarg][len - 2])) { + arg[iarg][len - 2] = '\0'; + dlimit_path = utils::strdup(arg[iarg] + 1); + } else + dlimit_path = utils::strdup(arg[iarg]); ++iarg; - } else error->all(FLERR,"Illegal fix halt command"); + } else error->all(FLERR, "Unknown fix halt keyword {}", arg[iarg]); } + // clang-format on // add nfirst to all computes that store invocation times // since don't know a priori which are invoked via variables by this fix // once in end_of_step() can set timestep for ones actually invoked if (attribute == VARIABLE) { - const bigint nfirst = (update->ntimestep/nevery)*nevery + nevery; + const bigint nfirst = (update->ntimestep / nevery) * nevery + nevery; modify->addstep_compute_all(nfirst); } } @@ -133,8 +134,8 @@ FixHalt::FixHalt(LAMMPS *lmp, int narg, char **arg) : FixHalt::~FixHalt() { - delete [] idvar; - delete [] dlimit_path; + delete[] idvar; + delete[] dlimit_path; } /* ---------------------------------------------------------------------- */ @@ -156,14 +157,14 @@ void FixHalt::init() if (attribute == VARIABLE) { ivar = input->variable->find(idvar); - if (ivar < 0) error->all(FLERR,"Could not find fix halt variable name"); + if (ivar < 0) error->all(FLERR, "Could not find fix halt variable {}", idvar); if (input->variable->equalstyle(ivar) == 0) - error->all(FLERR,"Fix halt variable is not equal-style variable"); + error->all(FLERR, "Fix halt variable {} is not equal-style variable", idvar); } // settings used by TLIMIT - nextstep = (update->ntimestep/nevery)*nevery + nevery; + nextstep = (update->ntimestep / nevery) * nevery + nevery; thisstep = -1; tratio = 0.5; @@ -205,6 +206,10 @@ void FixHalt::end_of_step() modify->addstep_compute(update->ntimestep + nevery); } + // ensure that the attribute is *exactly* the same on all ranks + + MPI_Bcast(&attvalue, 1, MPI_DOUBLE, 0, world); + // check if halt is triggered, else just return if (operation == LT) { @@ -220,21 +225,19 @@ void FixHalt::end_of_step() } else if (operation == NEQ) { if (attvalue == value) return; } else if (operation == XOR) { - if ((attvalue == 0.0 && value == 0.0) || - (attvalue != 0.0 && value != 0.0)) return; + if ((attvalue == 0.0 && value == 0.0) || (attvalue != 0.0 && value != 0.0)) return; } // hard halt -> exit LAMMPS // soft/continue halt -> trigger timer to break from run loop // print message with ID of fix halt in case multiple instances - std::string message = fmt::format("Fix halt condition for fix-id {} met on " - "step {} with value {}", + std::string message = fmt::format("Fix halt condition for fix-id {} met on step {} with value {}", id, update->ntimestep, attvalue); if (eflag == HARD) { - error->all(FLERR,message); - } else if (eflag == SOFT || eflag == CONTINUE) { - if (comm->me == 0 && msgflag == YESMSG) error->message(FLERR,message); + error->all(FLERR, message); + } else if ((eflag == SOFT) || (eflag == CONTINUE)) { + if ((comm->me == 0) && (msgflag == YESMSG)) error->message(FLERR, message); timer->force_timeout(); } } @@ -260,8 +263,8 @@ double FixHalt::bondmax() int **bondlist = neighbor->bondlist; int nbondlist = neighbor->nbondlist; - int i1,i2; - double delx,dely,delz,rsq; + int i1, i2; + double delx, dely, delz, rsq; double maxone = 0.0; for (int n = 0; n < nbondlist; n++) { @@ -272,12 +275,12 @@ double FixHalt::bondmax() dely = x[i1][1] - x[i2][1]; delz = x[i1][2] - x[i2][2]; - rsq = delx*delx + dely*dely + delz*delz; - maxone = MAX(rsq,maxone); + rsq = delx * delx + dely * dely + delz * delz; + maxone = MAX(rsq, maxone); } double maxall; - MPI_Allreduce(&maxone,&maxall,1,MPI_DOUBLE,MPI_MAX,world); + MPI_Allreduce(&maxone, &maxall, 1, MPI_DOUBLE, MPI_MAX, world); return sqrt(maxall); } @@ -291,48 +294,15 @@ double FixHalt::bondmax() double FixHalt::tlimit() { double cpu = timer->elapsed(Timer::TOTAL); - MPI_Bcast(&cpu,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cpu, 1, MPI_DOUBLE, 0, world); if (cpu < value) { bigint elapsed = update->ntimestep - update->firststep; - bigint final = update->firststep + - static_cast (tratio*value/cpu * elapsed); - nextstep = (final/nevery)*nevery + nevery; + bigint final = update->firststep + static_cast(tratio * value / cpu * elapsed); + nextstep = (final / nevery) * nevery + nevery; if (nextstep == update->ntimestep) nextstep += nevery; tratio = 1.0; } return cpu; } - -/* ---------------------------------------------------------------------- - determine available disk space, if supported. Return -1 if not. -------------------------------------------------------------------------- */ -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#endif -double FixHalt::diskfree() -{ -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) - struct statvfs fs; - double disk_free = -1.0; - - if (dlimit_path) { - disk_free = 1.0e100; - int rv = statvfs(dlimit_path,&fs); - if (rv == 0) { -#if defined(__linux__) - disk_free = fs.f_bavail*fs.f_bsize/1048576.0; -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) - disk_free = fs.f_bavail*fs.f_frsize/1048576.0; -#endif - } else - disk_free = -1.0; - - MPI_Bcast(&disk_free,1,MPI_DOUBLE,0,world); - } - return disk_free; -#else - return -1.0; -#endif -} From 54ff3cf78f345d90dc31b9bc8eecf9087d2853f8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:21:09 -0500 Subject: [PATCH 070/305] include new platform call in docs --- doc/src/Developer_platform.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/src/Developer_platform.rst b/doc/src/Developer_platform.rst index cdc4bb6770..9b05299146 100644 --- a/doc/src/Developer_platform.rst +++ b/doc/src/Developer_platform.rst @@ -70,6 +70,9 @@ File and path functions and global constants .. doxygenfunction:: is_console :project: progguide +.. doxygenfunction:: disk_free + :project: progguide + .. doxygenfunction:: path_is_directory :project: progguide From 45ca21da3b3a968e7a6d028f947702e38e8baafa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 11:21:16 -0500 Subject: [PATCH 071/305] fix typo --- tools/msi2lmp/src/SearchAndFill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/msi2lmp/src/SearchAndFill.c b/tools/msi2lmp/src/SearchAndFill.c index ce344c5ab6..35de0c81fe 100644 --- a/tools/msi2lmp/src/SearchAndFill.c +++ b/tools/msi2lmp/src/SearchAndFill.c @@ -116,7 +116,7 @@ void SearchAndFill(struct FrcFieldItem *item) /* Read lines until keyword is found */ if (fseek(FrcF,file_pos,SEEK_SET) < 0) { - fprintf(stderr, "Resetting file stream failed: ", strerror(errno)); + fprintf(stderr, "Resetting file stream failed: %s\n", strerror(errno)); exit(2); } strcpy(line,"empty"); From eb4c85566e5ca4bcf40a4a811677c06ceb2369ec Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 13:06:20 -0500 Subject: [PATCH 072/305] fix up port of platform::disk_free() to Windows --- src/platform.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform.cpp b/src/platform.cpp index c8a964076f..b2061fe681 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -42,7 +42,6 @@ #define PSAPI_VERSION 2 #include -#include #include // for _get_osfhandle() #include #include @@ -1079,8 +1078,9 @@ double platform::disk_free(const std::string &path) } } #else define(_WIN32) - bigint free_bytes; - if (GetDiskFreeSpaceEx(path.c_str(), &free_bytes, nullptr, nullptr)) disk_free = free_bytes; + bigint free_bytes = 0; + if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &free_bytes, nullptr, nullptr)) + disk_free = free_bytes; #endif return disk_free; } From 4d6e70600e755cf4a2164655a50f3ba3505e572a Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 15:03:13 -0500 Subject: [PATCH 073/305] some more small changes for portability and clarity --- src/platform.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform.cpp b/src/platform.cpp index b2061fe681..b324bd0b5c 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -1060,7 +1060,7 @@ bool platform::file_is_readable(const std::string &path) double platform::disk_free(const std::string &path) { - double disk_free = -1.0; + double bytes_free = -1.0; #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__OpenBSD__) || defined(__NetBSD__) @@ -1070,19 +1070,19 @@ double platform::disk_free(const std::string &path) int rv = statvfs(path.c_str(), &fs); if (rv == 0) { #if defined(__linux__) - disk_free = fs.f_bavail * fs.f_bsize; + bytes_free = fs.f_bavail * fs.f_bsize; #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__OpenBSD__) || defined(__NetBSD__) - disk_free = fs.f_bavail * fs.f_frsize; + bytes_free = fs.f_bavail * fs.f_frsize; #endif } } -#else define(_WIN32) - bigint free_bytes = 0; - if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &free_bytes, nullptr, nullptr)) - disk_free = free_bytes; +#elif defined(_WIN32) + uint64_t is_free = 0; + if (GetDiskFreeSpaceEx(path.c_str(), (PULARGE_INTEGER) &is_free, nullptr, nullptr)) + bytes_free = is_free; #endif - return disk_free; + return bytes_free; } /* ---------------------------------------------------------------------- From ae58fe273257fdc12ba57b010e4dd3ee65e2d4c5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 15:30:13 -0500 Subject: [PATCH 074/305] silence compiler warning --- src/neighbor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/neighbor.cpp b/src/neighbor.cpp index ea208f61ce..c6c959a894 100644 --- a/src/neighbor.cpp +++ b/src/neighbor.cpp @@ -1791,16 +1791,17 @@ void Neighbor::print_pairwise_info() out += fmt::format(", trim from ({})",rq->copylist+1); else out += fmt::format(", copy from ({})",rq->copylist+1); - } else if (rq->halffull) + } else if (rq->halffull) { if (rq->trim) out += fmt::format(", half/full trim from ({})",rq->halffulllist+1); else out += fmt::format(", half/full from ({})",rq->halffulllist+1); - else if (rq->skip) + } else if (rq->skip) { if (rq->trim) out += fmt::format(", skip trim from ({})",rq->skiplist+1); else out += fmt::format(", skip from ({})",rq->skiplist+1); + } out += "\n"; // list of neigh list attributes From 5a9c9981e747654221f6cfdd4a572d9c523f7d46 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:14:21 -0500 Subject: [PATCH 075/305] replace include file with forward declaration --- src/KOKKOS/atom_kokkos.cpp | 1 + src/KOKKOS/atom_kokkos.h | 3 +-- src/KOKKOS/min_kokkos.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/atom_kokkos.cpp b/src/KOKKOS/atom_kokkos.cpp index bc393b29d8..c55c1d315b 100644 --- a/src/KOKKOS/atom_kokkos.cpp +++ b/src/KOKKOS/atom_kokkos.cpp @@ -25,6 +25,7 @@ #include "kokkos_base.h" #include "modify.h" #include "fix.h" +#include "fix_property_atom_kokkos.h" using namespace LAMMPS_NS; diff --git a/src/KOKKOS/atom_kokkos.h b/src/KOKKOS/atom_kokkos.h index 21a9aeebbd..6a3036375d 100644 --- a/src/KOKKOS/atom_kokkos.h +++ b/src/KOKKOS/atom_kokkos.h @@ -14,7 +14,6 @@ #include "atom.h" // IWYU pragma: export #include "kokkos_type.h" -#include "fix_property_atom_kokkos.h" #include @@ -27,7 +26,7 @@ class AtomKokkos : public Atom { public: bool sort_classic; int nprop_atom; - FixPropertyAtomKokkos** fix_prop_atom; + class FixPropertyAtomKokkos **fix_prop_atom; DAT::tdual_tagint_1d k_tag; DAT::tdual_int_1d k_type, k_mask; diff --git a/src/KOKKOS/min_kokkos.cpp b/src/KOKKOS/min_kokkos.cpp index c01a53c7b3..3460fe9009 100644 --- a/src/KOKKOS/min_kokkos.cpp +++ b/src/KOKKOS/min_kokkos.cpp @@ -21,6 +21,7 @@ #include "angle.h" #include "atom_kokkos.h" #include "atom_masks.h" +#include "atom_vec.h" #include "bond.h" #include "comm.h" #include "compute.h" From 603837c96ca07d84c638b9db1cb1f39b9cddbc0d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:21:04 -0500 Subject: [PATCH 076/305] add versionadded tag --- doc/src/compute_reaxff_atom.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/src/compute_reaxff_atom.rst b/doc/src/compute_reaxff_atom.rst index 9f690d19c3..997ad02e9f 100644 --- a/doc/src/compute_reaxff_atom.rst +++ b/doc/src/compute_reaxff_atom.rst @@ -40,6 +40,8 @@ Examples Description """"""""""" +.. versionadded:: TBD + Define a computation that extracts bond information computed by the ReaxFF potential specified by :doc:`pair_style reaxff `. From 7dab2b7eee941e28e01416ede9688e661c1cf520 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:21:17 -0500 Subject: [PATCH 077/305] add new package files to .gitignore --- src/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/.gitignore b/src/.gitignore index 3b7902303d..112a1486f7 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -633,6 +633,8 @@ /compute_ptm_atom.h /compute_rattlers_atom.cpp /compute_rattlers_atom.h +/compute_reaxff_atom.cpp +/compute_reaxff_atom.h /compute_rigid_local.cpp /compute_rigid_local.h /compute_slcsa_atom.cpp From ff0553e8592a888c7757c14a1f9fac6a688b03dd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 16:50:30 -0500 Subject: [PATCH 078/305] fix typos --- src/balance.cpp | 2 +- src/fix_balance.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/balance.cpp b/src/balance.cpp index 42463752f9..c8bbead327 100644 --- a/src/balance.cpp +++ b/src/balance.cpp @@ -233,7 +233,7 @@ void Balance::command(int narg, char **arg) } if (style == SHIFT) { - const int blen = bstr.size(); + const int blen = bstr.size() + 1; for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Balance shift string is invalid"); diff --git a/src/fix_balance.cpp b/src/fix_balance.cpp index 7174765f52..f405de28b7 100644 --- a/src/fix_balance.cpp +++ b/src/fix_balance.cpp @@ -83,7 +83,7 @@ FixBalance::FixBalance(LAMMPS *lmp, int narg, char **arg) : // error checks if (lbstyle == SHIFT) { - int blen = bstr.size(); + const int blen = bstr.size() + 1; for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Fix balance shift string is invalid"); From 5f0bdca3f2cad56b84b9252820c9e82298cb4902 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:09:37 -0500 Subject: [PATCH 079/305] add missing override --- src/EXTRA-FIX/fix_nonaffine_displacement.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EXTRA-FIX/fix_nonaffine_displacement.h b/src/EXTRA-FIX/fix_nonaffine_displacement.h index 0a195dc08e..3341ab1834 100644 --- a/src/EXTRA-FIX/fix_nonaffine_displacement.h +++ b/src/EXTRA-FIX/fix_nonaffine_displacement.h @@ -32,7 +32,7 @@ class FixNonaffineDisplacement : public Fix { void post_constructor() override; void init() override; void init_list(int, class NeighList *) override; - void setup(int); + void setup(int) override; void post_force(int) override; void write_restart(FILE *fp) override; void restart(char *buf) override; @@ -62,7 +62,7 @@ class FixNonaffineDisplacement : public Fix { void calculate_D2Min(); void save_reference_state(); void minimum_image0(double *); - void grow_arrays(int); + void grow_arrays(int) override; }; } // namespace LAMMPS_NS From c4626e982f8a94d0a3d7594e5cd1712ca624d469 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:19:45 -0500 Subject: [PATCH 080/305] revert bogus change --- src/balance.cpp | 2 +- src/fix_balance.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/balance.cpp b/src/balance.cpp index c8bbead327..42463752f9 100644 --- a/src/balance.cpp +++ b/src/balance.cpp @@ -233,7 +233,7 @@ void Balance::command(int narg, char **arg) } if (style == SHIFT) { - const int blen = bstr.size() + 1; + const int blen = bstr.size(); for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Balance shift string is invalid"); diff --git a/src/fix_balance.cpp b/src/fix_balance.cpp index f405de28b7..23a56c0a9d 100644 --- a/src/fix_balance.cpp +++ b/src/fix_balance.cpp @@ -83,7 +83,7 @@ FixBalance::FixBalance(LAMMPS *lmp, int narg, char **arg) : // error checks if (lbstyle == SHIFT) { - const int blen = bstr.size() + 1; + const int blen = bstr.size(); for (int i = 0; i < blen; i++) { if (bstr[i] != 'x' && bstr[i] != 'y' && bstr[i] != 'z') error->all(FLERR,"Fix balance shift string is invalid"); From 172238f4cafc59c1ab703739c98fc65a026bdcf4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:59:18 -0500 Subject: [PATCH 081/305] workaround hack for macOS --- src/PYTHON/python_impl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index c1c26c7bb3..ea4ac63ce7 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if PY_VERSION_HEX >= 0x030800f0 +#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,6 +87,8 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif +#else +#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From 1df91f21a18d88006e07ec50e56c16ca7036b927 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 17:59:18 -0500 Subject: [PATCH 082/305] workaround hack for macOS --- src/PYTHON/python_impl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index c1c26c7bb3..ea4ac63ce7 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if PY_VERSION_HEX >= 0x030800f0 +#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,6 +87,8 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif +#else +#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From 54089fb5abbd18d91ad2b0f1665338df403396f1 Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 10:58:06 +1000 Subject: [PATCH 083/305] Revert "add extract function to fix_property_atom" This reverts commit b199368c19bdc5f1b5f0fc991473ae7de3f73d31. --- src/fix_property_atom.cpp | 24 ------------------------ src/fix_property_atom.h | 1 - 2 files changed, 25 deletions(-) diff --git a/src/fix_property_atom.cpp b/src/fix_property_atom.cpp index 1e004ae4cb..9613523059 100644 --- a/src/fix_property_atom.cpp +++ b/src/fix_property_atom.cpp @@ -948,27 +948,3 @@ int FixPropertyAtom::size_restart(int /*nlocal*/) { return values_peratom + 1; } - -/* ---------------------------------------------------------------------- - extract fix property/atom properties -------------------------------------------------------------------------- */ - -void *FixPropertyAtom::extract(const char *str, int &dim) -{ - dim=0; - if (strcmp(str, "nvalue") == 0) { - return &nvalue; - } else if (strcmp(str, "border") == 0) { - return &border; - } - dim=1; - if (strcmp(str, "styles") == 0) { - return &styles; - } else if (strcmp(str, "index") == 0) { - return &index; - } else if (strcmp(str, "cols") == 0) { - return &cols; - } - return nullptr; -} - diff --git a/src/fix_property_atom.h b/src/fix_property_atom.h index 820acf3a20..c50b6049dc 100644 --- a/src/fix_property_atom.h +++ b/src/fix_property_atom.h @@ -51,7 +51,6 @@ class FixPropertyAtom : public Fix { void unpack_restart(int, int) override; int size_restart(int) override; int maxsize_restart() override; - void *extract(const char *, int &) override; double memory_usage() override; protected: From 44fbcf7bfe2b8fd27c2cacf171fdc8dc6219ec69 Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 11:01:50 +1000 Subject: [PATCH 084/305] reorder "ghost" processing in fix property/atom --- src/fix_property_atom.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/fix_property_atom.cpp b/src/fix_property_atom.cpp index 9613523059..93e33ca056 100644 --- a/src/fix_property_atom.cpp +++ b/src/fix_property_atom.cpp @@ -51,6 +51,19 @@ FixPropertyAtom::FixPropertyAtom(LAMMPS *lmp, int narg, char **arg) : nvalue = 0; values_peratom = 0; + // get "ghost" first for settings + + border = 0; + while (iarg < narg) { + if (strcmp(arg[iarg], "ghost") == 0) { + if (iarg + 2 > narg) error->all(FLERR, "Illegal fix property/atom command"); + border = utils::logical(FLERR, arg[iarg + 1], false, lmp); + iarg += 2; + } else iarg++; + } + + iarg = 3; + while (iarg < narg) { if (strcmp(arg[iarg], "mol") == 0) { if (atom->molecule_flag) @@ -168,11 +181,8 @@ FixPropertyAtom::FixPropertyAtom(LAMMPS *lmp, int narg, char **arg) : // optional args - border = 0; while (iarg < narg) { if (strcmp(arg[iarg], "ghost") == 0) { - if (iarg + 2 > narg) error->all(FLERR, "Illegal fix property/atom command"); - border = utils::logical(FLERR, arg[iarg + 1], false, lmp); iarg += 2; } else if (strcmp(arg[iarg], "writedata") == 0) { if (iarg + 2 > narg) error->all(FLERR, "Illegal fix property/atom command"); From 61ca9b79db605dae37450c2b20d12d1f88ce817e Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 11:48:46 +1000 Subject: [PATCH 085/305] add custom_border to Atom and AtomKokkos --- src/KOKKOS/atom_kokkos.cpp | 7 ++++++- src/KOKKOS/atom_kokkos.h | 2 +- src/atom.cpp | 13 ++++++++++++- src/atom.h | 4 +++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/atom_kokkos.cpp b/src/KOKKOS/atom_kokkos.cpp index bc393b29d8..ecd618e7ac 100644 --- a/src/KOKKOS/atom_kokkos.cpp +++ b/src/KOKKOS/atom_kokkos.cpp @@ -297,7 +297,7 @@ void AtomKokkos::grow(unsigned int mask) return index in ivector or dvector of its location ------------------------------------------------------------------------- */ -int AtomKokkos::add_custom(const char *name, int flag, int cols) +int AtomKokkos::add_custom(const char *name, int flag, int cols, int border) { int index; @@ -342,6 +342,11 @@ int AtomKokkos::add_custom(const char *name, int flag, int cols) dcols[index] = cols; } + if (index < 0) + error->all(FLERR,"Invalid call to AtomKokkos::add_custom()"); + else + custom_border[flag + (cols) ? 2 : 0].push_back(border); + return index; } diff --git a/src/KOKKOS/atom_kokkos.h b/src/KOKKOS/atom_kokkos.h index 21a9aeebbd..000ad5e112 100644 --- a/src/KOKKOS/atom_kokkos.h +++ b/src/KOKKOS/atom_kokkos.h @@ -154,7 +154,7 @@ class AtomKokkos : public Atom { void sync_overlapping_device(const ExecutionSpace space, unsigned int mask); void sort() override; virtual void grow(unsigned int mask); - int add_custom(const char *, int, int) override; + int add_custom(const char *, int, int, int border = 0) override; void remove_custom(int, int, int) override; virtual void deallocate_topology(); private: diff --git a/src/atom.cpp b/src/atom.cpp index b604c54e6b..8ac72e8950 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -2605,6 +2605,7 @@ void Atom::update_callback(int ifix) lists of names can have NULL entries if previously removed return flag = 0/1 for int/double return cols = 0/N for vector/array where N = # of columns + return border = 0/1 if fix property/atom has "ghost" no/yes ------------------------------------------------------------------------- */ int Atom::find_custom(const char *name, int &flag, int &cols) @@ -2642,6 +2643,13 @@ int Atom::find_custom(const char *name, int &flag, int &cols) return -1; } +int Atom::find_custom(const char *name, int &flag, int &cols, int &border) +{ + int i = find_custom(name, flag, cols); + if (i != -1) border = custom_border[flag + (cols) ? 2 : 0][i]; + return i; +} + /** \brief Add a custom per-atom property with the given name and type and size \verbatim embed:rst @@ -2654,7 +2662,7 @@ This function is called, e.g. from :doc:`fix property/atom `. * \param cols Number of values: 0 for a single value, 1 or more for a vector of values * \return index of property in the respective list of properties */ -int Atom::add_custom(const char *name, int flag, int cols) +int Atom::add_custom(const char *name, int flag, int cols, int border) { int index = -1; @@ -2697,6 +2705,9 @@ int Atom::add_custom(const char *name, int flag, int cols) if (index < 0) error->all(FLERR,"Invalid call to Atom::add_custom()"); + else + custom_border[flag + (cols) ? 2 : 0].push_back(border); + return index; } diff --git a/src/atom.h b/src/atom.h index 548168ac59..9724e5662f 100644 --- a/src/atom.h +++ b/src/atom.h @@ -242,6 +242,7 @@ class Atom : protected Pointers { int *icols, *dcols; char **ivname, **dvname, **ianame, **daname; int nivector, ndvector, niarray, ndarray; + std::array, 4> custom_border; // molecule templates // each template can be a set of consecutive molecules @@ -363,7 +364,8 @@ class Atom : protected Pointers { void update_callback(int); int find_custom(const char *, int &, int &); - virtual int add_custom(const char *, int, int); + int find_custom(const char *, int &, int &, int &); + virtual int add_custom(const char *, int, int, int border = 0); virtual void remove_custom(int, int, int); void *extract(const char *); From 09c87040b54b7bea54a0b7528cea0801ef94e81b Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 11:54:15 +1000 Subject: [PATCH 086/305] add border arguments to FixPropertyAtom add_custom functions --- src/fix_property_atom.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fix_property_atom.cpp b/src/fix_property_atom.cpp index 93e33ca056..c3af7c2f1a 100644 --- a/src/fix_property_atom.cpp +++ b/src/fix_property_atom.cpp @@ -125,7 +125,7 @@ FixPropertyAtom::FixPropertyAtom(LAMMPS *lmp, int narg, char **arg) : if (index[nvalue] >= 0) error->all(FLERR, "Fix property/atom vector name already exists"); if (ReadData::is_data_section(id)) error->all(FLERR, "Fix property/atom fix ID must not be a data file section name"); - index[nvalue] = atom->add_custom(&arg[iarg][2], 0, 0); + index[nvalue] = atom->add_custom(&arg[iarg][2], 0, 0, border); cols[nvalue] = 0; values_peratom++; nvalue++; @@ -138,7 +138,7 @@ FixPropertyAtom::FixPropertyAtom(LAMMPS *lmp, int narg, char **arg) : if (index[nvalue] >= 0) error->all(FLERR, "Fix property/atom vector name already exists"); if (ReadData::is_data_section(id)) error->all(FLERR, "Fix property/atom fix ID must not be a data file section name"); - index[nvalue] = atom->add_custom(&arg[iarg][2], 1, 0); + index[nvalue] = atom->add_custom(&arg[iarg][2], 1, 0, border); cols[nvalue] = 0; values_peratom++; nvalue++; @@ -167,7 +167,7 @@ FixPropertyAtom::FixPropertyAtom(LAMMPS *lmp, int narg, char **arg) : which = 1; styles[nvalue] = DARRAY; } - index[nvalue] = atom->add_custom(&arg[iarg][3], which, ncols); + index[nvalue] = atom->add_custom(&arg[iarg][3], which, ncols, border); cols[nvalue] = ncols; values_peratom += ncols; nvalue++; From a873106790223b3a72ed4d9293f55a2a8ba59bcc Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 12:26:05 +1000 Subject: [PATCH 087/305] improve AMOEBA fix property/atom checks --- src/AMOEBA/pair_amoeba.cpp | 43 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/AMOEBA/pair_amoeba.cpp b/src/AMOEBA/pair_amoeba.cpp index cad9e2b628..c94faf91a7 100644 --- a/src/AMOEBA/pair_amoeba.cpp +++ b/src/AMOEBA/pair_amoeba.cpp @@ -827,28 +827,31 @@ void PairAmoeba::init_style() // check if all custom atom arrays were set via fix property/atom - int flag,cols; + char const * names[6] = {"amtype", "amgroup", "redID", + "xyzaxis", "polaxe", "pval"}; + int const flag_check[6] = {0, 0, 1, 1, 0, 1}; // correct type (0 int, 1 dbl) + int const cols_check[6] = {0, 0, 0, 3, 0, 0}; // xyzaxis 3 cols, all others 0 + int const border_check[6] = {1, 0, 0, 0, 0, 0}; // which types need ghost + int flag, cols, border; + int index[6]; - index_amtype = atom->find_custom("amtype",flag,cols); - if (index_amtype < 0 || flag || cols) - error->all(FLERR,"Pair {} amtype is not defined", mystyle); - index_amgroup = atom->find_custom("amgroup",flag,cols); - if (index_amgroup < 0 || flag || cols) - error->all(FLERR,"Pair {} amgroup is not defined", mystyle); + for (int i = 0; i < 6; i++) { + index[i] = atom->find_custom(names[i], flag, cols, border); + std::string err = ""; + if (index[i] < 0) err = "was not defined"; + else if (flag_check[i] != flag) err = "has the wrong type"; + else if (cols_check[i] != cols) err = "has the wrong number of columns"; + else if (border_check[i] && !border) err = "must be set by fix property/atom with ghost yes"; + if (err != "") + error->all(FLERR,"Pair {} per-atom variable {} {}", mystyle, names[i], err); + } - index_redID = atom->find_custom("redID",flag,cols); - if (index_redID < 0 || !flag || cols) - error->all(FLERR,"Pair {} redID is not defined", mystyle); - index_xyzaxis = atom->find_custom("xyzaxis",flag,cols); - if (index_xyzaxis < 0 || !flag || cols == 0) - error->all(FLERR,"Pair {} xyzaxis is not defined", mystyle); - - index_polaxe = atom->find_custom("polaxe",flag,cols); - if (index_polaxe < 0 || flag || cols) - error->all(FLERR,"Pair {} polaxe is not defined", mystyle); - index_pval = atom->find_custom("pval",flag,cols); - if (index_pval < 0 || !flag || cols) - error->all(FLERR,"Pair {} pval is not defined", mystyle); + index_amtype = index[0]; + index_amgroup = index[1]; + index_redID = index[2]; + index_xyzaxis = index[3]; + index_polaxe = index[4]; + index_pval = index[5]; // ------------------------------------------------------------------- // one-time initializations From abe635671625214731b59bc8dc95a9cbf1bc23fe Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 21:34:11 -0500 Subject: [PATCH 088/305] update help index table for recently added or changed styles --- tools/lammps-gui/help_index.table | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/lammps-gui/help_index.table b/tools/lammps-gui/help_index.table index 5ce4ae6203..5aa9e13dda 100644 --- a/tools/lammps-gui/help_index.table +++ b/tools/lammps-gui/help_index.table @@ -254,6 +254,7 @@ compute_nbond_atom.html compute nbond/atom compute_omega_chunk.html compute omega/chunk compute_orientorder_atom.html compute orientorder/atom compute_orientorder_atom.html compute orientorder/atom/kk +compute_pace.html compute pace compute_pair_local.html compute pair/local compute_pair.html compute pair compute_pe_atom.html compute pe/atom @@ -267,13 +268,17 @@ compute_property_chunk.html compute property/chunk compute_property_grid.html compute property/grid compute_property_local.html compute property/local compute_ptm_atom.html compute ptm/atom +compute_rattlers_atom.html compute rattlers/atom compute_rdf.html compute rdf +compute_reaxff_atom.html compute reaxff/atom +compute_reaxff_atom.html compute reaxff/atom/kk compute_reduce_chunk.html compute reduce/chunk compute_reduce.html compute reduce compute_reduce.html compute reduce/region compute_rigid_local.html compute rigid/local compute.html compute compute_saed.html compute saed +compute_slcsa_atom.html compute slcsa/atom compute_slice.html compute slice compute_smd_contact_radius.html compute smd/contact/radius compute_smd_damage.html compute smd/damage @@ -484,6 +489,7 @@ fix_drude_transform.html fix drude/transform/inverse fix_dt_reset.html fix dt/reset fix_dt_reset.html fix dt/reset/kk fix_efield.html fix efield +fix_efield.html fix efield/kk fix_efield.html fix efield/tip4p fix_ehex.html fix ehex fix_electrode.html fix electrode/conp @@ -565,6 +571,7 @@ fix_nh.html fix nvt/kk fix_nh.html fix nvt/omp fix_nh_uef.html fix npt/uef fix_nh_uef.html fix nvt/uef +fix_nonaffine_displacement.html fix nonaffine/displacement fix_nph_asphere.html fix nph/asphere fix_nph_asphere.html fix nph/asphere/omp fix_nph_body.html fix nph/body @@ -634,6 +641,7 @@ fix_polarize.html fix polarize/functional fix_pour.html fix pour fix_precession_spin.html fix precession/spin fix_press_berendsen.html fix press/berendsen +fix_press_langevin.html fix press/langevin fix_print.html fix print fix_propel_self.html fix propel/self fix_property_atom.html fix property/atom @@ -703,14 +711,17 @@ fix_spring_chunk.html fix spring/chunk fix_spring_rg.html fix spring/rg fix_spring.html fix spring fix_spring_self.html fix spring/self +fix_spring_self.html fix spring/self/kk fix_srd.html fix srd fix_store_force.html fix store/force fix_store_state.html fix store/state fix_temp_berendsen.html fix temp/berendsen +fix_temp_berendsen.html fix temp/berendsen/kk fix_temp_csvr.html fix temp/csld fix_temp_csvr.html fix temp/csvr fix_temp_rescale_eff.html fix temp/rescale/eff fix_temp_rescale.html fix temp/rescale +fix_temp_rescale.html fix temp/rescale/kk fix_tfmc.html fix tfmc fix_tgnh_drude.html fix tgnpt/drude fix_tgnh_drude.html fix tgnvt/drude @@ -980,6 +991,7 @@ pair_coul_shield.html pair_style coul/shield pair_coul_slater.html pair_style coul/slater pair_coul_slater.html pair_style coul/slater/cut pair_coul_slater.html pair_style coul/slater/long +pair_coul_slater.html pair_style coul/slater/long/gpu pair_coul_tt.html pair_style coul/tt pair_cs.html pair_style born/coul/dsf/cs pair_cs.html pair_style born/coul/long/cs @@ -1073,8 +1085,10 @@ pair_fep_soft.html pair_style lj/class2/coul/cut/soft pair_fep_soft.html pair_style lj/class2/coul/long/soft pair_fep_soft.html pair_style lj/class2/soft pair_fep_soft.html pair_style lj/cut/coul/cut/soft +pair_fep_soft.html pair_style lj/cut/coul/cut/soft/gpu pair_fep_soft.html pair_style lj/cut/coul/cut/soft/omp pair_fep_soft.html pair_style lj/cut/coul/long/soft +pair_fep_soft.html pair_style lj/cut/coul/long/soft/gpu pair_fep_soft.html pair_style lj/cut/coul/long/soft/omp pair_fep_soft.html pair_style lj/cut/soft pair_fep_soft.html pair_style lj/cut/soft/omp @@ -1225,7 +1239,9 @@ pair_meam_sw_spline.html pair_style meam/sw/spline pair_mesocnt.html pair_style mesocnt pair_mesocnt.html pair_style mesocnt/viscous pair_mesodpd.html pair_style edpd +pair_mesodpd.html pair_style edpd/gpu pair_mesodpd.html pair_style mdpd +pair_mesodpd.html pair_style mdpd/gpu pair_mesodpd.html pair_style mdpd/rhosum pair_mesodpd.html pair_style tdpd pair_mgpt.html pair_style mgpt @@ -1245,7 +1261,8 @@ pair_morse.html pair_style morse/smooth/linear/omp pair_multi_lucy.html pair_style multi/lucy pair_multi_lucy_rx.html pair_style multi/lucy/rx pair_multi_lucy_rx.html pair_style multi/lucy/rx/kk -pair_nb3b_harmonic.html pair_style nb3b/harmonic +pair_nb3b.html pair_style nb3b/harmonic +pair_nb3b.html pair_style nb3b/screened pair_nm.html pair_style nm/cut pair_nm.html pair_style nm/cut/coul/cut pair_nm.html pair_style nm/cut/coul/cut/omp @@ -1303,16 +1320,20 @@ pair_smd_triangulated_surface.html pair_style smd/tri_surface pair_smd_ulsph.html pair_style smd/ulsph pair_smtbq.html pair_style smtbq pair_snap.html pair_style snap +pair_snap.html pair_style snap/intel pair_snap.html pair_style snap/kk pair_soft.html pair_style soft pair_soft.html pair_style soft/gpu pair_soft.html pair_style soft/omp pair_sph_heatconduction.html pair_style sph/heatconduction +pair_sph_heatconduction.html pair_style sph/heatconduction/gpu pair_sph_idealgas.html pair_style sph/idealgas pair_sph_lj.html pair_style sph/lj +pair_sph_lj.html pair_style sph/lj/gpu pair_sph_rhosum.html pair_style sph/rhosum pair_sph_taitwater_morris.html pair_style sph/taitwater/morris pair_sph_taitwater.html pair_style sph/taitwater +pair_sph_taitwater.html pair_style sph/taitwater/gpu pair_spica.html pair_style lj/spica pair_spica.html pair_style lj/spica/coul/long pair_spica.html pair_style lj/spica/coul/long/gpu @@ -1384,6 +1405,7 @@ pair_write.html pair_write pair_ylz.html pair_style ylz pair_yukawa_colloid.html pair_style yukawa/colloid pair_yukawa_colloid.html pair_style yukawa/colloid/gpu +pair_yukawa_colloid.html pair_style yukawa/colloid/kk pair_yukawa_colloid.html pair_style yukawa/colloid/omp pair_yukawa.html pair_style yukawa pair_yukawa.html pair_style yukawa/gpu From e36a764db2a62ad45024e5c7fbdb8482ece4ec66 Mon Sep 17 00:00:00 2001 From: Shern Tee Date: Fri, 15 Dec 2023 12:44:55 +1000 Subject: [PATCH 089/305] add array and vector STL headers --- src/atom.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atom.h b/src/atom.h index 9724e5662f..6f22ebd160 100644 --- a/src/atom.h +++ b/src/atom.h @@ -18,6 +18,8 @@ #include #include +#include +#include namespace LAMMPS_NS { From 84af9e347675506305e4a46847d37b14cc1b3a71 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 14 Dec 2023 22:02:39 -0500 Subject: [PATCH 090/305] remove hack --- src/PYTHON/python_impl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index ea4ac63ce7..02070ee151 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -79,7 +79,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Force the stdout and stderr streams to be unbuffered. bool unbuffered = PYTHONUNBUFFERED != nullptr && strcmp(PYTHONUNBUFFERED, "1") == 0; -#if (PY_VERSION_HEX >= 0x030800f0) && !defined(__APPLE__) +#if (PY_VERSION_HEX >= 0x030800f0) PyConfig config; PyConfig_InitPythonConfig(&config); config.buffered_stdio = !unbuffered; @@ -87,8 +87,6 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // Python Global configuration variable Py_UnbufferedStdioFlag = unbuffered; #endif -#else -#warning Cannot force stdout and stderr to be unbuffered #endif #ifdef MLIAP_PYTHON From 95d1a41ee44a2fe05e4acb266bb550e4fa5dd3bc Mon Sep 17 00:00:00 2001 From: jtclemm Date: Fri, 15 Dec 2023 13:33:45 -0700 Subject: [PATCH 091/305] Fixing bpm/sphere error in fix move, displace atoms --- src/displace_atoms.cpp | 16 +++++++++++---- src/fix_move.cpp | 45 +++++++++++++++++++++++++++++++++--------- src/fix_move.h | 2 +- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/displace_atoms.cpp b/src/displace_atoms.cpp index fa333f1bc2..5ecf5a2c9e 100644 --- a/src/displace_atoms.cpp +++ b/src/displace_atoms.cpp @@ -160,7 +160,7 @@ void DisplaceAtoms::command(int narg, char **arg) int *mask = atom->mask; int nlocal = atom->nlocal; - double fraction,dramp; + double fraction, dramp; for (i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { @@ -255,11 +255,12 @@ void DisplaceAtoms::command(int narg, char **arg) int line_flag = atom->line_flag; int tri_flag = atom->tri_flag; int body_flag = atom->body_flag; + int quat_atom_flag = atom->quat_flag; int theta_flag = 0; int quat_flag = 0; if (line_flag) theta_flag = 1; - if (ellipsoid_flag || tri_flag || body_flag) quat_flag = 1; + if (ellipsoid_flag || tri_flag || body_flag || quat_atom_flag) quat_flag = 1; // AtomVec pointers to retrieve per-atom storage of extra quantities @@ -269,6 +270,7 @@ void DisplaceAtoms::command(int narg, char **arg) auto avec_body = dynamic_cast(atom->style_match("body")); double **x = atom->x; + double **quat_atom = atom->quat; int *ellipsoid = atom->ellipsoid; int *line = atom->line; int *tri = atom->tri; @@ -313,7 +315,7 @@ void DisplaceAtoms::command(int narg, char **arg) // quats for ellipsoids, tris, and bodies - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -322,12 +324,18 @@ void DisplaceAtoms::command(int narg, char **arg) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) { - MathExtra::quatquat(qrotate,quat,qnew); + MathExtra::quatquat(qrotate, quat, qnew); quat[0] = qnew[0]; quat[1] = qnew[1]; quat[2] = qnew[2]; quat[3] = qnew[3]; } + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, quat_atom[i], qnew); + quat_atom[i][0] = qnew[0]; + quat_atom[i][1] = qnew[1]; + quat_atom[i][2] = qnew[2]; + quat_atom[i][3] = qnew[3]; } } } diff --git a/src/fix_move.cpp b/src/fix_move.cpp index 36bba410fc..99b5b30bec 100644 --- a/src/fix_move.cpp +++ b/src/fix_move.cpp @@ -276,10 +276,11 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : line_flag = atom->line_flag; tri_flag = atom->tri_flag; body_flag = atom->body_flag; + quat_atom_flag = atom->quat_flag; theta_flag = quat_flag = 0; if (line_flag) theta_flag = 1; - if (ellipsoid_flag || tri_flag || body_flag) quat_flag = 1; + if (ellipsoid_flag || tri_flag || body_flag || quat_atom_flag) quat_flag = 1; extra_flag = 0; if (omega_flag || angmom_flag || theta_flag || quat_flag) extra_flag = 1; @@ -329,7 +330,7 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } } - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { double *quat; for (int i = 0; i < nlocal; i++) { quat = nullptr; @@ -349,6 +350,16 @@ FixMove::FixMove(LAMMPS *lmp, int narg, char **arg) : } else qoriginal[i][0] = qoriginal[i][1] = qoriginal[i][2] = qoriginal[i][3] = 0.0; } + } else if (quat_atom_flag) { + double **quat_atom = atom->quat; + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + qoriginal[i][0] = quat_atom[i][0]; + qoriginal[i][1] = quat_atom[i][1]; + qoriginal[i][2] = quat_atom[i][2]; + qoriginal[i][3] = quat_atom[i][3]; + } + } } // nrestart = size of per-atom restart data @@ -521,6 +532,7 @@ void FixMove::initial_integrate(int /*vflag*/) double *radius = atom->radius; double *rmass = atom->rmass; double *mass = atom->mass; + double **quat_atom = atom->quat; int *type = atom->type; int *ellipsoid = atom->ellipsoid; int *line = atom->line; @@ -749,9 +761,9 @@ void FixMove::initial_integrate(int /*vflag*/) avec_line->bonus[atom->line[i]].theta = theta_new; } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -760,6 +772,8 @@ void FixMove::initial_integrate(int /*vflag*/) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) MathExtra::quatquat(qrotate, qoriginal[i], quat); + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, qoriginal[i], quat_atom[i]); } } @@ -880,9 +894,9 @@ void FixMove::initial_integrate(int /*vflag*/) avec_line->bonus[atom->line[i]].theta = theta_new; } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -891,6 +905,8 @@ void FixMove::initial_integrate(int /*vflag*/) else if (body_flag && body[i] >= 0) quat = avec_body->bonus[body[i]].quat; if (quat) MathExtra::quatquat(qrotate, qoriginal[i], quat); + } else if (quat_atom_flag) { + MathExtra::quatquat(qrotate, qoriginal[i], quat_atom[i]); } } @@ -1263,6 +1279,7 @@ void FixMove::set_arrays(int i) double *quat; double **x = atom->x; + double **quat_atom = atom->quat; imageint *image = atom->image; int *ellipsoid = atom->ellipsoid; int *line = atom->line; @@ -1341,9 +1358,9 @@ void FixMove::set_arrays(int i) toriginal[i] = theta - 0.0; // NOTE: edit this line } - // quats for ellipsoids, tris, and bodies + // quats for ellipsoids, tris, bodies, and bpm/sphere - if (quat_flag) { + if (quat_flag & !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -1354,6 +1371,11 @@ void FixMove::set_arrays(int i) if (quat) { // qoriginal = f(quat,-delta); // NOTE: edit this line } + } else if (quat_atom_flag) { + // qoriginal[0] = quat_atom[i][0]; // NOTE: edit this line + // qoriginal[1] = quat_atom[i][1]; // NOTE: edit this line + // qoriginal[2] = quat_atom[i][2]; // NOTE: edit this line + // qoriginal[3] = quat_atom[i][3]; // NOTE: edit this line } } xoriginal[i][0] -= vx * delta; @@ -1400,7 +1422,7 @@ void FixMove::set_arrays(int i) // quats for ellipsoids, tris, and bodies - if (quat_flag) { + if (quat_flag && !quat_atom_flag) { quat = nullptr; if (ellipsoid_flag && ellipsoid[i] >= 0) quat = avec_ellipsoid->bonus[ellipsoid[i]].quat; @@ -1411,6 +1433,11 @@ void FixMove::set_arrays(int i) if (quat) { // qoriginal = f(quat,-delta); // NOTE: edit this line } + } else if (quat_atom_flag) { + // qoriginal[0] = quat_atom[i][0]; // NOTE: edit this line + // qoriginal[1] = quat_atom[i][1]; // NOTE: edit this line + // qoriginal[2] = quat_atom[i][2]; // NOTE: edit this line + // qoriginal[3] = quat_atom[i][3]; // NOTE: edit this line } } } diff --git a/src/fix_move.h b/src/fix_move.h index e3c018f54d..244a9d704a 100644 --- a/src/fix_move.h +++ b/src/fix_move.h @@ -61,7 +61,7 @@ class FixMove : public Fix { int xvar, yvar, zvar, vxvar, vyvar, vzvar; int xvarstyle, yvarstyle, zvarstyle, vxvarstyle, vyvarstyle, vzvarstyle; int extra_flag, omega_flag, angmom_flag; - int radius_flag, ellipsoid_flag, line_flag, tri_flag, body_flag; + int radius_flag, ellipsoid_flag, line_flag, tri_flag, body_flag, quat_atom_flag; int theta_flag, quat_flag; int nlevels_respa, nrestart; int time_origin; From 41495579a688bcb112fe6e85430951657607d9dd Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 00:18:05 -0500 Subject: [PATCH 092/305] update list --- doc/utils/sphinx-config/false_positives.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index ad3dee6a00..03e67b95cb 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -792,6 +792,7 @@ dispersionflag dissipative Dissipative distharm +distutils dl dlabel dlambda From e100a42087f0bf75a815fbe95cd837ba8ac2d22b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:07:37 -0500 Subject: [PATCH 093/305] (re)throw EOF exception when next_dvector() has not yet read any items --- src/potential_file_reader.cpp | 2 ++ src/text_file_reader.cpp | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/potential_file_reader.cpp b/src/potential_file_reader.cpp index 2c0b9a6a55..613225a797 100644 --- a/src/potential_file_reader.cpp +++ b/src/potential_file_reader.cpp @@ -144,6 +144,8 @@ void PotentialFileReader::next_dvector(double *list, int n) { try { return reader->next_dvector(list, n); + } catch (EOFException &) { + throw EOFException("EOF reached"); } catch (FileReaderException &e) { error->one(FLERR, e.what()); } diff --git a/src/text_file_reader.cpp b/src/text_file_reader.cpp index 46a5fd33a9..0b8d717687 100644 --- a/src/text_file_reader.cpp +++ b/src/text_file_reader.cpp @@ -189,8 +189,9 @@ void TextFileReader::next_dvector(double *list, int n) char *ptr = next_line(); if (ptr == nullptr) { - // EOF - if (i < n) { + if (i == 0) { // EOF without any records + throw EOFException("EOF reached"); + } else if (i < n) { // EOF with incomplete data throw FileReaderException( fmt::format("Incorrect format in {} file! {}/{} values", filetype, i, n)); } From 96ef731f069c2d65121c8a0e7041f94e4e235df9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:07:53 -0500 Subject: [PATCH 094/305] remove unused items --- src/MOLECULE/fix_cmap.cpp | 11 ----------- src/MOLECULE/fix_cmap.h | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index f74c05ef06..edd52ee47b 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -87,9 +87,6 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : respa_level_support = 1; ilevel_respa = 0; - MPI_Comm_rank(world,&me); - MPI_Comm_size(world,&nprocs); - // allocate memory for CMAP data memory->create(g_axis,CMAPDIM,"cmap:g_axis"); @@ -184,10 +181,6 @@ void FixCMAP::init() for (i = 0; i < 6; i++) set_map_derivatives(cmapgrid[i],d1cmapgrid[i],d2cmapgrid[i],d12cmapgrid[i]); - // define newton_bond here in case restart file was read (not data file) - - newton_bond = force->newton_bond; - if (utils::strmatch(update->integrate_style,"^respa")) { ilevel_respa = (dynamic_cast(update->integrate))->nlevels-1; if (respa_level >= 0) ilevel_respa = MIN(respa_level,ilevel_respa); @@ -934,10 +927,6 @@ void FixCMAP::read_data_header(char *line) } catch (std::exception &e) { error->all(FLERR,"Invalid read data header line for fix cmap: {}", e.what()); } - - // not set in constructor because this fix could be defined before newton command - - newton_bond = force->newton_bond; } /* ---------------------------------------------------------------------- diff --git a/src/MOLECULE/fix_cmap.h b/src/MOLECULE/fix_cmap.h index fce76aa540..1c6aba95e0 100644 --- a/src/MOLECULE/fix_cmap.h +++ b/src/MOLECULE/fix_cmap.h @@ -65,8 +65,7 @@ class FixCMAP : public Fix { double memory_usage() override; private: - int nprocs, me; - int newton_bond, eflag_caller; + int eflag_caller; int ctype, ilevel_respa; int ncrosstermtypes, crossterm_per_atom, maxcrossterm; int ncrosstermlist; From fb6a5843b9d5457bded5aa323233f40ff728faaa Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:14:47 -0500 Subject: [PATCH 095/305] handle comments in CMAP coeff assignments --- src/MOLECULE/fix_cmap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index edd52ee47b..cfc71f96fc 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -946,10 +946,10 @@ void FixCMAP::read_data_section(char * /*keyword*/, int /*n*/, char *buf, // loop over lines of CMAP crossterms // tokenize the line into values - // add crossterm to one of my atoms, depending on newton_bond + // add crossterm to one of my atoms for (const auto &line : lines) { - ValueTokenizer values(line); + ValueTokenizer values(utils::trim_comment(line)); try { values.skip(); itype = values.next_int(); From 4bf1b1d9c0d058ceaba03f1976709b3868684fa1 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:15:31 -0500 Subject: [PATCH 096/305] some refactoring and modernization --- src/MOLECULE/fix_cmap.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index cfc71f96fc..1f137cb833 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -49,15 +49,14 @@ using namespace LAMMPS_NS; using namespace FixConst; using namespace MathConst; -#define MAXLINE 256 -#define LISTDELTA 10000 -#define LB_FACTOR 1.5 +static constexpr int LISTDELTA = 10000; +static constexpr double LB_FACTOR = 1.5; -#define CMAPMAX 6 // max # of CMAP terms stored by one atom -#define CMAPDIM 24 // grid map dimension is 24 x 24 -#define CMAPXMIN -360.0 -#define CMAPXMIN2 -180.0 -#define CMAPDX 15.0 // 360/CMAPDIM +static constexpr int CMAPMAX = 6; // max # of CMAP terms stored by one atom +static constexpr int CMAPDIM = 24; // grid map dimension is 24 x 24 +static constexpr double CMAPXMIN = -360.0; +static constexpr double CMAPXMIN2 = -180.0; +static constexpr double CMAPDX = 15.0; // 360/CMAPDIM /* ---------------------------------------------------------------------- */ @@ -90,10 +89,10 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : // allocate memory for CMAP data memory->create(g_axis,CMAPDIM,"cmap:g_axis"); - memory->create(cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:grid"); - memory->create(d1cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d1grid"); - memory->create(d2cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d2grid"); - memory->create(d12cmapgrid,6,CMAPDIM,CMAPDIM,"cmap:d12grid"); + memory->create(cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:grid"); + memory->create(d1cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d1grid"); + memory->create(d2cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d2grid"); + memory->create(d12cmapgrid,CMAPMAX,CMAPDIM,CMAPDIM,"cmap:d12grid"); // read and setup CMAP data @@ -231,6 +230,8 @@ void FixCMAP::min_setup(int vflag) void FixCMAP::pre_neighbor() { int i,m,atom1,atom2,atom3,atom4,atom5; + const int me = comm->me; + const int nprocs = comm->nprocs; // guesstimate initial length of local crossterm list // if ncmap was not set (due to read_restart, no read_data), From 1ab406ee1acbdbb47208b219ba3b5f33f8ecb627 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:16:06 -0500 Subject: [PATCH 097/305] read CMAP data blocks one at a time and catch EOF exception to stop reading --- src/MOLECULE/fix_cmap.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index 1f137cb833..01b62d2a28 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -39,6 +39,7 @@ #include "memory.h" #include "potential_file_reader.h" #include "respa.h" +#include "text_file_reader.h" #include "update.h" #include @@ -631,15 +632,22 @@ void FixCMAP::read_grid_map(char *cmapfile) { if (comm->me == 0) { try { - memset(&cmapgrid[0][0][0], 0, 6*CMAPDIM*CMAPDIM*sizeof(double)); + ncrosstermtypes = 0; + memset(&cmapgrid[0][0][0], 0, CMAPMAX*CMAPDIM*CMAPDIM*sizeof(double)); + utils::logmesg(lmp, "Reading CMAP parameters from: {}\n", cmapfile); PotentialFileReader reader(lmp, cmapfile, "cmap grid"); - // there are six maps in this order. + // there may be up to six maps. + // the charmm36.cmap file has in this order. // alanine, alanine-proline, proline, proline-proline, glycine, glycine-proline. - // read as one big blob of numbers while ignoring comments - - reader.next_dvector(&cmapgrid[0][0][0],6*CMAPDIM*CMAPDIM); + // custom CMAP files created by charmm-gui may have fewer entries + // read one map at a time as a blob of numbers while ignoring comments + // and stop reading when whe have reached EOF. + for (ncrosstermtypes = 0; ncrosstermtypes < CMAPMAX; ++ncrosstermtypes) + reader.next_dvector(&cmapgrid[ncrosstermtypes][0][0],CMAPDIM*CMAPDIM); + } catch (EOFException &) { + utils::logmesg(lmp, " Read in CMAP data for {} crossterm types\n", ncrosstermtypes); } catch (std::exception &e) { error->one(FLERR,"Error reading CMAP potential file: {}", e.what()); } From 15a7b93361bc74f915b397c74a443022585dd5a7 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 17:40:31 -0500 Subject: [PATCH 098/305] relax epsilon to be compatible with most recent GCC compilers on Fedora 39 --- unittest/commands/test_groups.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/commands/test_groups.cpp b/unittest/commands/test_groups.cpp index b91a6108d9..7f0a054c40 100644 --- a/unittest/commands/test_groups.cpp +++ b/unittest/commands/test_groups.cpp @@ -314,7 +314,7 @@ TEST_F(GroupTest, Dynamic) command("group ramp variable grow");); } -constexpr double EPSILON = 1.0e-14; +constexpr double EPSILON = 1.0e-13; TEST_F(GroupTest, VariableFunctions) { From 695a81ef70b3a97b094c4e42ef82c8b9e603a649 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 21:50:38 -0500 Subject: [PATCH 099/305] avoid uninitialized data access --- src/MOLECULE/fix_cmap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MOLECULE/fix_cmap.cpp b/src/MOLECULE/fix_cmap.cpp index 01b62d2a28..cb4cb8cadc 100644 --- a/src/MOLECULE/fix_cmap.cpp +++ b/src/MOLECULE/fix_cmap.cpp @@ -86,6 +86,7 @@ FixCMAP::FixCMAP(LAMMPS *lmp, int narg, char **arg) : wd_section = 1; respa_level_support = 1; ilevel_respa = 0; + eflag_caller = 1; // allocate memory for CMAP data From 4ae4c8103d281e3c0979ccaa03dcb704741a3c47 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 22:46:02 -0500 Subject: [PATCH 100/305] step LAMMPS-GUI patch level --- tools/lammps-gui/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index caae722865..b1469794bb 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.5.10 LANGUAGES CXX) +project(lammps-gui VERSION 1.5.11 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) From d2ca1fe354cb7b595f5fad45aaec07fefea97ccf Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 16 Dec 2023 23:00:06 -0500 Subject: [PATCH 101/305] avoid that mliappy is initialized multiple times --- src/PYTHON/python_impl.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/PYTHON/python_impl.cpp b/src/PYTHON/python_impl.cpp index 02070ee151..0db468d701 100644 --- a/src/PYTHON/python_impl.cpp +++ b/src/PYTHON/python_impl.cpp @@ -17,6 +17,7 @@ #include "python_impl.h" +#include "comm.h" #include "error.h" #include "input.h" #include "memory.h" @@ -56,7 +57,6 @@ #endif #include "mliap_unified_couple_kokkos.h" - #endif #endif @@ -90,23 +90,27 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) #endif #ifdef MLIAP_PYTHON - // Inform python intialization scheme of the mliappy module. - // This -must- happen before python is initialized. - int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); + // cannot register mliappy module a second time + if (!Py_IsInitialized()) { + // Inform python intialization scheme of the mliappy module. + // This -must- happen before python is initialized. + int err = PyImport_AppendInittab("mliap_model_python_couple", PyInit_mliap_model_python_couple); + if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); + + err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); + if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); - err = PyImport_AppendInittab("mliap_unified_couple", PyInit_mliap_unified_couple); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); #ifdef LMP_KOKKOS - // Inform python intialization scheme of the mliappy module. - // This -must- happen before python is initialized. - err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", PyInit_mliap_model_python_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY embedded python module."); - - err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); - if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python module."); + // Inform python intialization scheme of the mliappy module. + // This -must- happen before python is initialized. + err = PyImport_AppendInittab("mliap_model_python_couple_kokkos", + PyInit_mliap_model_python_couple_kokkos); + if (err) error->all(FLERR, "Could not register MLIAPPY embedded python KOKKOS module."); + err = PyImport_AppendInittab("mliap_unified_couple_kokkos", PyInit_mliap_unified_couple_kokkos); + if (err) error->all(FLERR, "Could not register MLIAPPY unified embedded python KOKKOS module."); #endif + } #endif #if PY_VERSION_HEX >= 0x030800f0 && !defined(Py_LIMITED_API) @@ -120,7 +124,7 @@ PythonImpl::PythonImpl(LAMMPS *lmp) : Pointers(lmp) // With Python 3.7 this function is now called by Py_Initialize() // Deprecated since version 3.9, will be removed in version 3.11 #if PY_VERSION_HEX < 0x030700f0 - if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); } + if (!PyEval_ThreadsInitialized()) PyEval_InitThreads(); #endif PyUtils::GIL lock; From 58ed034d7a92718f075e237076b88ff669cf022b Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Sun, 17 Dec 2023 15:42:10 -0600 Subject: [PATCH 102/305] Updated Developer unittest for debugging failed unit tests individually --- doc/src/Developer_unittest.rst | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index e311fcdfc5..b517ca1802 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -526,3 +526,69 @@ The ``unittest/tools`` folder contains tests for programs in the shell, which are implemented as a python scripts using the ``unittest`` Python module and launching the tool commands through the ``subprocess`` Python module. + + +Troubleshooting failed unit tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The unit tests of a newly added features (e.g. pair, fix or compute styles) +are not automatically run when your pull request (PR) is submitted via GitHub. +To trigger the unit test(s), you need to add appropriate labels the PR, e.g., +``gpu_unit_tests`` to enable the unit tests for the GPU package. After the test +has started, you had better remove the label since every PR activity will +retrigger the test. + +For a failed build on GitHub, click on Details to go to the LAMMPS Jenkins CI web page. +Click on the "Exit" symbol near the "Logout" button on the top right of that page +to go to the classic view. In the classic view, there is a list of the individual runs +that make up this test run. You can click on any of those. Then on "Test Result" +to display the list of tests. Click on the Status column to sort the tests +based on their Failed or Passed status. Then click on the failed test to expand +its output. + +For example, the following output snippet shows the failed unit test + +.. code-block:: bash + + [ RUN ] PairStyle.gpu + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:63: Failure + Expected: (err) <= (epsilon) + Actual: 0.00018957912910606503 vs 0.0001 + Google Test trace: + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:56: EXPECT_FORCES: init_forces (newton off) + /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:64: Failure + Expected: (err) <= (epsilon) + Actual: 0.00022892713393549854 vs 0.0001 + + +Note that the force style engine runs the same system on a rather +off-equilibrium system with few atoms for just a few steps, writes data and restart, +uses ``clean`` to reset, and then run with different settings and integrators. +Beyond potential issues/bugs in the source code, the mismatch between the expected +and actual values could be that force arrays are not properly cleared between +multiple run commands and/or when starting a run from restarts. + +As a rule of thumb, the test epsilon can often be in the range 5e-14 to 1e-13. +For "noisy" force kernels, e.g. those involving `exp()`, `log()` or `sin()` +operations and subject to compiler optimization, epsilon can be further relaxed, +sometimes epsilon can be relaxed to 1.e-12. If lookup tables are added, +we may need to go to 1.e-10 or even higher. + +To rerun the failed unit test individually, change to the ``build`` directory +and run the test with verbose output. For example, + +.. code-block:: bash + + env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V + +It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` to +shorten the build time during recompilation. Installing `ccache` in +your development environment helps speed up recompilation by +caching previous compilations and detecting when the same compilation +is being done again. + + + + + + From 188e1090e9e37272144890a33479670cc592dde9 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 20:01:47 -0500 Subject: [PATCH 103/305] add some corrections and clarifications --- doc/src/Build_development.rst | 41 +++++++++------- doc/src/Developer_unittest.rst | 90 ++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 51 deletions(-) diff --git a/doc/src/Build_development.rst b/doc/src/Build_development.rst index d30a433d62..c674b2c258 100644 --- a/doc/src/Build_development.rst +++ b/doc/src/Build_development.rst @@ -255,16 +255,18 @@ A test run is then a a collection multiple individual test runs each with many comparisons to reference results based on template input files, individual command settings, relative error margins, and reference data stored in a YAML format file with ``.yaml`` -suffix. Currently the programs ``test_pair_style``, ``test_bond_style``, and -``test_angle_style`` are implemented. They will compare forces, energies and -(global) stress for all atoms after a ``run 0`` calculation and after a -few steps of MD with :doc:`fix nve `, each in multiple variants -with different settings and also for multiple accelerated styles. If a -prerequisite style or package is missing, the individual tests are -skipped. All tests will be executed on a single MPI process, so using -the CMake option ``-D BUILD_MPI=off`` can significantly speed up testing, -since this will skip the MPI initialization for each test run. -Below is an example command and output: +suffix. Currently the programs ``test_pair_style``, ``test_bond_style``, +``test_angle_style``, ``test_dihedral_style``, and +``test_improper_style`` are implemented. They will compare forces, +energies and (global) stress for all atoms after a ``run 0`` calculation +and after a few steps of MD with :doc:`fix nve `, each in +multiple variants with different settings and also for multiple +accelerated styles. If a prerequisite style or package is missing, the +individual tests are skipped. All force style tests will be executed on +a single MPI process, so using the CMake option ``-D BUILD_MPI=off`` can +significantly speed up testing, since this will skip the MPI +initialization for each test run. Below is an example command and +output: .. code-block:: console @@ -416,15 +418,16 @@ When compiling LAMMPS with enabled tests, most test executables will need to be linked against the LAMMPS library. Since this can be a very large library with many C++ objects when many packages are enabled, link times can become very long on machines that use the GNU BFD linker (e.g. -Linux systems). Alternatives like the ``lld`` linker of the LLVM project -or the ``gold`` linker available with GNU binutils can speed up this step -substantially. CMake will by default test if any of the two can be -enabled and use it when ``ENABLE_TESTING`` is active. It can also be -selected manually through the ``CMAKE_CUSTOM_LINKER`` CMake variable. -Allowed values are ``lld``, ``gold``, ``bfd``, or ``default``. The -``default`` option will use the system default linker otherwise, the -linker is chosen explicitly. This option is only available for the -GNU or Clang C++ compiler. +Linux systems). Alternatives like the ``mold`` linker, the ``lld`` +linker of the LLVM project, or the ``gold`` linker available with GNU +binutils can speed up this step substantially (in this order). CMake +will by default test if any of the three can be enabled and use it when +``ENABLE_TESTING`` is active. It can also be selected manually through +the ``CMAKE_CUSTOM_LINKER`` CMake variable. Allowed values are +``mold``, ``lld``, ``gold``, ``bfd``, or ``default``. The ``default`` +option will use the system default linker otherwise, the linker is +chosen explicitly. This option is only available for the GNU or Clang +C++ compilers. Tests for other components and utility functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index b517ca1802..c0cdee6c09 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -121,7 +121,7 @@ will be suppressed and only a summary printed, but adding the '-V' option will then produce output from the tests above like the following: -.. code-block:: +.. code-block:: console [...] 1: [ RUN ] Tokenizer.empty_string @@ -531,24 +531,33 @@ Python module. Troubleshooting failed unit tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The unit tests of a newly added features (e.g. pair, fix or compute styles) -are not automatically run when your pull request (PR) is submitted via GitHub. -To trigger the unit test(s), you need to add appropriate labels the PR, e.g., -``gpu_unit_tests`` to enable the unit tests for the GPU package. After the test -has started, you had better remove the label since every PR activity will -retrigger the test. +The are by default no unit tests for newly added features (e.g. pair, fix, +or compute styles) unless your pull request also includes tests for the +added features. If you are modifying some features, you may see failures +for existing tests, if your modifications have some unexpected side effects +or your changes render the existing text invalid. If you are adding an +accelerated version of an existing style, then only tests for INTEL, +KOKKOS (with OpenMP only), OPENMP, and OPT will be run automatically. +Tests for the GPU package are time consuming and thus are only run +*after* a merge, or when a special label, ``gpu_unit_tests`` is added +to the pull request. After the test has started, it is often best to +remove the label since every PR activity will re-trigger the test (that +is a limitation of triggering a test with a label). Support for unit +tests with using KOKKOS with GPU acceleration is currently not supported. -For a failed build on GitHub, click on Details to go to the LAMMPS Jenkins CI web page. -Click on the "Exit" symbol near the "Logout" button on the top right of that page -to go to the classic view. In the classic view, there is a list of the individual runs -that make up this test run. You can click on any of those. Then on "Test Result" -to display the list of tests. Click on the Status column to sort the tests -based on their Failed or Passed status. Then click on the failed test to expand -its output. +When you see a failed build on GitHub, click on ``Details`` to be taken +to the corresponding LAMMPS Jenkins CI web page. Click on the "Exit" +symbol near the ``Logout`` button on the top right of that page to go to +the "classic view". In the classic view, there is a list of the +individual runs that make up this test run (they are shown but cannot be +inspected in the default view). You can click on any of those. +Clicking on ``Test Result`` will display the list of failed tests. Click +on the "Status" column to sort the tests based on their Failed or Passed +status. Then click on the failed test to expand its output. For example, the following output snippet shows the failed unit test -.. code-block:: bash +.. code-block:: console [ RUN ] PairStyle.gpu /home/builder/workspace/dev/pull_requests/ubuntu_gpu/unit_tests/cmake_gpu_opencl_mixed_smallbig_clang_static/unittest/force-styles/test_main.cpp:63: Failure @@ -560,19 +569,35 @@ For example, the following output snippet shows the failed unit test Expected: (err) <= (epsilon) Actual: 0.00022892713393549854 vs 0.0001 +Note that the force style engine runs one of a small number of systems +in a rather off-equilibrium configuration with a few atoms for a few +steps, writes data and restart files, uses :doc:`the clear command +` to reset LAMMPS, and then runs from those files with different +settings (e.g. newton on/off) and integrators (e.g. verlet vs. respa). +Beyond potential issues/bugs in the source code, the mismatch between +the expected and actual values could be that force arrays are not +properly cleared between multiple run commands or that class members are +not correctly initialized or written to or read from a data or restart +file. -Note that the force style engine runs the same system on a rather -off-equilibrium system with few atoms for just a few steps, writes data and restart, -uses ``clean`` to reset, and then run with different settings and integrators. -Beyond potential issues/bugs in the source code, the mismatch between the expected -and actual values could be that force arrays are not properly cleared between -multiple run commands and/or when starting a run from restarts. - -As a rule of thumb, the test epsilon can often be in the range 5e-14 to 1e-13. -For "noisy" force kernels, e.g. those involving `exp()`, `log()` or `sin()` -operations and subject to compiler optimization, epsilon can be further relaxed, -sometimes epsilon can be relaxed to 1.e-12. If lookup tables are added, -we may need to go to 1.e-10 or even higher. +While the epsilon (relative precision) for a single, `IEEE 754 compliant +`_, double precision floating +point operation is at about 2.2e-16, the achievable precision for the +tests is lower due to most numbers being sums over intermediate results +and the non-associativity of floating point math leading to larger +errors. In some cases specific properties of the tested style. As a +rule of thumb, the test epsilon can often be in the range 5.0e-14 to +1.0e-13. But for "noisy" force kernels, e.g. those a larger amount of +arithmetic operations involving `exp()`, `log()` or `sin()` functions, +and also due to the effect of compiler optimization or differences +between compilers or platforms, epsilon may need to be further relaxed, +sometimes epsilon can be relaxed to 1.0e-12. If interpolation or lookup +tables are used, epsilon may need to be set to 1.0e-10 or even higher. +For tests of accelerated styles, the per-test epsilon is multiplied +by empirical factors that take into account the differences in the order +of floating point operations or that some or most intermediate operations +may be done using approximations or with single precision floating point +math. To rerun the failed unit test individually, change to the ``build`` directory and run the test with verbose output. For example, @@ -581,11 +606,12 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V -It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` to -shorten the build time during recompilation. Installing `ccache` in -your development environment helps speed up recompilation by -caching previous compilations and detecting when the same compilation -is being done again. +It is recommended to configure the build with ``-D +BUILD_SHARED_LIBS=on`` and use a custom linker to shorten the build time +during recompilation. Installing `ccache` in your development +environment helps speed up recompilation by caching previous +compilations and detecting when the same compilation is being done +again. Please see :doc:`Build_development` for further details. From 4c34bc2b9b5aa9d13f4e42660dd065ea4facdd88 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 20:25:39 -0500 Subject: [PATCH 104/305] upgrade CodeQL GitHub action scripts to latest version --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 00c0e8642d..c7dd945f5f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.x' - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/${{ matrix.language }}.yml @@ -55,4 +55,4 @@ jobs: cmake --build . --parallel 2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 94b62fa98baccb1b7a68f65ab09eff0093927411 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sun, 17 Dec 2023 21:22:33 -0500 Subject: [PATCH 105/305] tweak epsilon --- unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml b/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml index e242a56029..485730531f 100644 --- a/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml +++ b/unittest/force-styles/tests/mol-pair-lj_cut_coul_cut_soft.yaml @@ -1,7 +1,7 @@ --- lammps_version: 17 Feb 2022 date_generated: Fri Mar 18 22:17:31 2022 -epsilon: 2e-13 +epsilon: 5e-13 skip_tests: prerequisites: ! | atom full From ba4ac991b6b62ef1feacb016554915145c4696c5 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:06:36 +0800 Subject: [PATCH 106/305] small fix for ilp/tmd and KC/full --- src/INTERLAYER/pair_ilp_tmd.cpp | 2 +- src/INTERLAYER/pair_kolmogorov_crespi_full.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/INTERLAYER/pair_ilp_tmd.cpp b/src/INTERLAYER/pair_ilp_tmd.cpp index 8b08de39c0..73f89803c2 100644 --- a/src/INTERLAYER/pair_ilp_tmd.cpp +++ b/src/INTERLAYER/pair_ilp_tmd.cpp @@ -210,7 +210,7 @@ void PairILPTMD::calc_FRep(int eflag, int /* vflag */) delki[1] = x[k][1] - x[i][1]; delki[2] = x[k][2] - x[i][2]; if (evflag) - ev_tally_xyz(k, j, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], + ev_tally_xyz(k, i, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], delki[1], delki[2]); } diff --git a/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp b/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp index b497ae3568..ad42ba1922 100644 --- a/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp +++ b/src/INTERLAYER/pair_kolmogorov_crespi_full.cpp @@ -590,7 +590,7 @@ void PairKolmogorovCrespiFull::calc_FRep(int eflag, int /* vflag */) delki[1] = x[k][1] - x[i][1]; delki[2] = x[k][2] - x[i][2]; if (evflag) - ev_tally_xyz(k, j, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], + ev_tally_xyz(k, i, nlocal, newton_pair, 0.0, 0.0, fk[0], fk[1], fk[2], delki[0], delki[1], delki[2]); } From 8dcf980d0abc2418d8f21f1ff6f6ac7451237e6d Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:55:10 +0800 Subject: [PATCH 107/305] update doc of ilp/tmd --- doc/src/pair_ilp_tmd.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index 70a4768389..dcacc18ed6 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -22,12 +22,12 @@ Examples .. code-block:: LAMMPS pair_style hybrid/overlay ilp/tmd 16.0 1 - pair_coeff * * ilp/tmd MoS2.ILP Mo S S + pair_coeff * * ilp/tmd TMD.ILP Mo S S pair_style hybrid/overlay sw/mod sw/mod ilp/tmd 16.0 pair_coeff * * sw/mod 1 tmd.sw.mod Mo S S NULL NULL NULL - pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL Mo S S - pair_coeff * * ilp/tmd MoS2.ILP Mo S S Mo S S + pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL W Se Se + pair_coeff * * ilp/tmd TMD.ILP Mo S S W Se Se Description """"""""""" @@ -36,7 +36,7 @@ Description The *ilp/tmd* style computes the registry-dependent interlayer potential (ILP) potential for transition metal dichalcogenides (TMD) -as described in :ref:`(Ouyang7) `. +as described in :ref:`(Ouyang7) ` and ref:`(Jiang) `. .. math:: @@ -69,7 +69,7 @@ calculating the normals. each atom `i`, its six nearest neighboring atoms belonging to the same sub-layer are chosen to define the normal vector `{\bf n}_i`. -The parameter file (e.g. MoS2.ILP), is intended for use with *metal* +The parameter file (e.g. TMD.ILP), is intended for use with *metal* :doc:`units `, with energies in meV. Two additional parameters, *S*, and *rcut* are included in the parameter file. *S* is designed to facilitate scaling of energies. *rcut* is designed to build the neighbor @@ -77,7 +77,7 @@ list for calculating the normals for each atom pair. .. note:: - The parameters presented in the parameter file (e.g. MoS2.ILP), + The parameters presented in the parameter file (e.g. TMD.ILP), are fitted with taper function by setting the cutoff equal to 16.0 Angstrom. Using different cutoff or taper function should be careful. These parameters provide a good description in both short- and long-range @@ -133,10 +133,10 @@ if LAMMPS was built with that package. See the :doc:`Build package This pair style requires the newton setting to be *on* for pair interactions. -The MoS2.ILP potential file provided with LAMMPS (see the potentials +The TMD.ILP potential file provided with LAMMPS (see the potentials directory) are parameterized for *metal* units. You can use this potential with any LAMMPS units, but you would need to create your own -custom MoS2.ILP potential file with coefficients listed in the appropriate +custom TMD.ILP potential file with coefficients listed in the appropriate units, if your simulation does not use *metal* units. Related commands @@ -164,3 +164,7 @@ tap_flag = 1 .. _Ouyang7: **(Ouyang7)** W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). + +.. _Jiang: + +**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). From 20183ac9cc9680c608cb230a61c9474b3db8174b Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 16:56:24 +0800 Subject: [PATCH 108/305] update potential file for ilp/tmd --- potentials/TMD.ILP | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 potentials/TMD.ILP diff --git a/potentials/TMD.ILP b/potentials/TMD.ILP new file mode 100644 index 0000000000..e7a9cbe558 --- /dev/null +++ b/potentials/TMD.ILP @@ -0,0 +1,25 @@ +# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# CITATION: W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). +# CITATION: W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). +# Interlayer Potential (ILP) for bilayer and bulk Group-VI Transition Metal Dichalcogenides. +# The parameters below are fitted against the HSE + MBD-NL DFT reference data. +# +# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* +# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut +Mo Mo 5.579450 9.377662 2.027222 144.151775 97.978570 89.437597 2.059031 5.122055 491850.316195 1.0 4.0 +W W 5.530854 6.624992 1.983208 0.271792 140.174059 107.392585 1.356333 4.437591 691850.243962 1.0 4.0 +S S 3.161402 8.093263 1.953140 4.586764 118.065466 58.809416 0.215367 4.299600 148811.243409 1.0 4.0 +Se Se 3.938627 10.515924 2.415783 3.012583 22.400612 116.864517 0.151121 5.884241 112506.195626 1.0 4.0 +Mo W 5.412298 8.647128 2.108665 51.177950 184.342860 201.281256 2.547743 2.492287 99996.913401 1.0 4.0 +Mo S 3.627152 19.971375 7.585031 76.101931 3.317496 45.720328 0.947470 4.410425 150597.857716 1.0 4.0 +Mo Se 6.196447 4.844134 14.362005 7.407221 0.058823 27.156223 0.976771 3.979186 786029.840651 1.0 4.0 +W S 3.680136 11.163004 32.254117 110.019679 79.381335 138.340438 0.900750 8.875776 250600.809034 1.0 4.0 +W Se 3.559392 20.638856 1.202717 20.478669 197.422484 10.005271 1.052738 3.815817 288321.561114 1.0 4.0 +S Se 2.820092 7.491151 1.933323 141.532559 293.127817 90.470904 0.390492 4.170885 117688.987069 1.0 4.0 +# Symmetric Atom Pair +W Mo 5.412298 8.647128 2.108665 51.177950 184.342860 201.281256 2.547743 2.492287 99996.913401 1.0 4.0 +S Mo 3.627152 19.971375 7.585031 76.101931 3.317496 45.720328 0.947470 4.410425 150597.857716 1.0 4.0 +Se Mo 6.196447 4.844134 14.362005 7.407221 0.058823 27.156223 0.976771 3.979186 786029.840651 1.0 4.0 +S W 3.680136 11.163004 32.254117 110.019679 79.381335 138.340438 0.900750 8.875776 250600.809034 1.0 4.0 +Se W 3.559392 20.638856 1.202717 20.478669 197.422484 10.005271 1.052738 3.815817 288321.561114 1.0 4.0 +Se S 2.820092 7.491151 1.933323 141.532559 293.127817 90.470904 0.390492 4.170885 117688.987069 1.0 4.0 From 40f27eb7cf6cecc39be6090b07e66d96e7f68263 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Mon, 18 Dec 2023 17:36:04 +0800 Subject: [PATCH 109/305] update doc file for ilp/tmd --- doc/src/pair_ilp_tmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index dcacc18ed6..b5d9ad60b5 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -36,7 +36,7 @@ Description The *ilp/tmd* style computes the registry-dependent interlayer potential (ILP) potential for transition metal dichalcogenides (TMD) -as described in :ref:`(Ouyang7) ` and ref:`(Jiang) `. +as described in :ref:`(Ouyang7) ` and :ref:`(Jiang) `. .. math:: From 4d2aed89374760f611e23c0d121d92a15b01e5b1 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Mon, 18 Dec 2023 11:15:56 -0500 Subject: [PATCH 110/305] bug fix for when reaction site has angles, but post-reaction template has none (same for dihedrals, impropers) --- src/REACTION/fix_bond_react.cpp | 180 +++++++++++++++++--------------- 1 file changed, 93 insertions(+), 87 deletions(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index 1da26e32a1..10a7023e17 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -3348,7 +3348,7 @@ void FixBondReact::update_everything() dynamic_cast(ihistory)->clear_cache(); // Angles! First let's delete all angle info: - if (force->angle && twomol->angleflag) { + if (force->angle) { int *num_angle = atom->num_angle; int **angle_type = atom->angle_type; tagint **angle_atom1 = atom->angle_atom1; @@ -3389,33 +3389,35 @@ void FixBondReact::update_everything() } } // now let's add the new angle info. - for (int j = 0; j < twomol->natoms; j++) { - int jj = equivalences[j][1][rxnID]-1; - if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { - if (landlocked_atoms[j][rxnID] == 1) { - num_angle[atom->map(update_mega_glove[jj+1][i])] = twomol->num_angle[j]; - delta_angle += twomol->num_angle[j]; - for (int p = 0; p < twomol->num_angle[j]; p++) { - angle_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->angle_type[j][p]; - angle_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom1[j][p]-1][1][rxnID]][i]; - angle_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom2[j][p]-1][1][rxnID]][i]; - angle_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom3[j][p]-1][1][rxnID]][i]; + if (twomol->angleflag) { + for (int j = 0; j < twomol->natoms; j++) { + int jj = equivalences[j][1][rxnID]-1; + if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { + if (landlocked_atoms[j][rxnID] == 1) { + num_angle[atom->map(update_mega_glove[jj+1][i])] = twomol->num_angle[j]; + delta_angle += twomol->num_angle[j]; + for (int p = 0; p < twomol->num_angle[j]; p++) { + angle_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->angle_type[j][p]; + angle_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom1[j][p]-1][1][rxnID]][i]; + angle_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom2[j][p]-1][1][rxnID]][i]; + angle_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->angle_atom3[j][p]-1][1][rxnID]][i]; + } } - } - if (landlocked_atoms[j][rxnID] == 0) { - for (int p = 0; p < twomol->num_angle[j]; p++) { - if (landlocked_atoms[twomol->angle_atom1[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->angle_atom2[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->angle_atom3[j][p]-1][rxnID] == 1) { - insert_num = num_angle[atom->map(update_mega_glove[jj+1][i])]; - angle_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->angle_type[j][p]; - angle_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom1[j][p]-1][1][rxnID]][i]; - angle_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom2[j][p]-1][1][rxnID]][i]; - angle_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom3[j][p]-1][1][rxnID]][i]; - num_angle[atom->map(update_mega_glove[jj+1][i])]++; - if (num_angle[atom->map(update_mega_glove[jj+1][i])] > atom->angle_per_atom) - error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); - delta_angle++; + if (landlocked_atoms[j][rxnID] == 0) { + for (int p = 0; p < twomol->num_angle[j]; p++) { + if (landlocked_atoms[twomol->angle_atom1[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->angle_atom2[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->angle_atom3[j][p]-1][rxnID] == 1) { + insert_num = num_angle[atom->map(update_mega_glove[jj+1][i])]; + angle_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->angle_type[j][p]; + angle_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom1[j][p]-1][1][rxnID]][i]; + angle_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom2[j][p]-1][1][rxnID]][i]; + angle_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->angle_atom3[j][p]-1][1][rxnID]][i]; + num_angle[atom->map(update_mega_glove[jj+1][i])]++; + if (num_angle[atom->map(update_mega_glove[jj+1][i])] > atom->angle_per_atom) + error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); + delta_angle++; + } } } } @@ -3425,7 +3427,7 @@ void FixBondReact::update_everything() } // Dihedrals! first let's delete all dihedral info for landlocked atoms - if (force->dihedral && twomol->dihedralflag) { + if (force->dihedral) { int *num_dihedral = atom->num_dihedral; int **dihedral_type = atom->dihedral_type; tagint **dihedral_atom1 = atom->dihedral_atom1; @@ -3469,36 +3471,38 @@ void FixBondReact::update_everything() } } // now let's add new dihedral info - for (int j = 0; j < twomol->natoms; j++) { - int jj = equivalences[j][1][rxnID]-1; - if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { - if (landlocked_atoms[j][rxnID] == 1) { - num_dihedral[atom->map(update_mega_glove[jj+1][i])] = twomol->num_dihedral[j]; - delta_dihed += twomol->num_dihedral[j]; - for (int p = 0; p < twomol->num_dihedral[j]; p++) { - dihedral_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->dihedral_type[j][p]; - dihedral_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom1[j][p]-1][1][rxnID]][i]; - dihedral_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom2[j][p]-1][1][rxnID]][i]; - dihedral_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom3[j][p]-1][1][rxnID]][i]; - dihedral_atom4[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom4[j][p]-1][1][rxnID]][i]; + if (twomol->dihedralflag) { + for (int j = 0; j < twomol->natoms; j++) { + int jj = equivalences[j][1][rxnID]-1; + if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { + if (landlocked_atoms[j][rxnID] == 1) { + num_dihedral[atom->map(update_mega_glove[jj+1][i])] = twomol->num_dihedral[j]; + delta_dihed += twomol->num_dihedral[j]; + for (int p = 0; p < twomol->num_dihedral[j]; p++) { + dihedral_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->dihedral_type[j][p]; + dihedral_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom1[j][p]-1][1][rxnID]][i]; + dihedral_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom2[j][p]-1][1][rxnID]][i]; + dihedral_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom3[j][p]-1][1][rxnID]][i]; + dihedral_atom4[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->dihedral_atom4[j][p]-1][1][rxnID]][i]; + } } - } - if (landlocked_atoms[j][rxnID] == 0) { - for (int p = 0; p < twomol->num_dihedral[j]; p++) { - if (landlocked_atoms[twomol->dihedral_atom1[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->dihedral_atom2[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->dihedral_atom3[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->dihedral_atom4[j][p]-1][rxnID] == 1) { - insert_num = num_dihedral[atom->map(update_mega_glove[jj+1][i])]; - dihedral_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->dihedral_type[j][p]; - dihedral_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom1[j][p]-1][1][rxnID]][i]; - dihedral_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom2[j][p]-1][1][rxnID]][i]; - dihedral_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom3[j][p]-1][1][rxnID]][i]; - dihedral_atom4[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom4[j][p]-1][1][rxnID]][i]; - num_dihedral[atom->map(update_mega_glove[jj+1][i])]++; - if (num_dihedral[atom->map(update_mega_glove[jj+1][i])] > atom->dihedral_per_atom) - error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); - delta_dihed++; + if (landlocked_atoms[j][rxnID] == 0) { + for (int p = 0; p < twomol->num_dihedral[j]; p++) { + if (landlocked_atoms[twomol->dihedral_atom1[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->dihedral_atom2[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->dihedral_atom3[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->dihedral_atom4[j][p]-1][rxnID] == 1) { + insert_num = num_dihedral[atom->map(update_mega_glove[jj+1][i])]; + dihedral_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->dihedral_type[j][p]; + dihedral_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom1[j][p]-1][1][rxnID]][i]; + dihedral_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom2[j][p]-1][1][rxnID]][i]; + dihedral_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom3[j][p]-1][1][rxnID]][i]; + dihedral_atom4[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->dihedral_atom4[j][p]-1][1][rxnID]][i]; + num_dihedral[atom->map(update_mega_glove[jj+1][i])]++; + if (num_dihedral[atom->map(update_mega_glove[jj+1][i])] > atom->dihedral_per_atom) + error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); + delta_dihed++; + } } } } @@ -3508,7 +3512,7 @@ void FixBondReact::update_everything() } // finally IMPROPERS!!!! first let's delete all improper info for landlocked atoms - if (force->improper && twomol->improperflag) { + if (force->improper) { int *num_improper = atom->num_improper; int **improper_type = atom->improper_type; tagint **improper_atom1 = atom->improper_atom1; @@ -3552,36 +3556,38 @@ void FixBondReact::update_everything() } } // now let's add new improper info - for (int j = 0; j < twomol->natoms; j++) { - int jj = equivalences[j][1][rxnID]-1; - if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { - if (landlocked_atoms[j][rxnID] == 1) { - num_improper[atom->map(update_mega_glove[jj+1][i])] = twomol->num_improper[j]; - delta_imprp += twomol->num_improper[j]; - for (int p = 0; p < twomol->num_improper[j]; p++) { - improper_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->improper_type[j][p]; - improper_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom1[j][p]-1][1][rxnID]][i]; - improper_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom2[j][p]-1][1][rxnID]][i]; - improper_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom3[j][p]-1][1][rxnID]][i]; - improper_atom4[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom4[j][p]-1][1][rxnID]][i]; + if (twomol->improperflag) { + for (int j = 0; j < twomol->natoms; j++) { + int jj = equivalences[j][1][rxnID]-1; + if (atom->map(update_mega_glove[jj+1][i]) < nlocal && atom->map(update_mega_glove[jj+1][i]) >= 0) { + if (landlocked_atoms[j][rxnID] == 1) { + num_improper[atom->map(update_mega_glove[jj+1][i])] = twomol->num_improper[j]; + delta_imprp += twomol->num_improper[j]; + for (int p = 0; p < twomol->num_improper[j]; p++) { + improper_type[atom->map(update_mega_glove[jj+1][i])][p] = twomol->improper_type[j][p]; + improper_atom1[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom1[j][p]-1][1][rxnID]][i]; + improper_atom2[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom2[j][p]-1][1][rxnID]][i]; + improper_atom3[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom3[j][p]-1][1][rxnID]][i]; + improper_atom4[atom->map(update_mega_glove[jj+1][i])][p] = update_mega_glove[equivalences[twomol->improper_atom4[j][p]-1][1][rxnID]][i]; + } } - } - if (landlocked_atoms[j][rxnID] == 0) { - for (int p = 0; p < twomol->num_improper[j]; p++) { - if (landlocked_atoms[twomol->improper_atom1[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->improper_atom2[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->improper_atom3[j][p]-1][rxnID] == 1 || - landlocked_atoms[twomol->improper_atom4[j][p]-1][rxnID] == 1) { - insert_num = num_improper[atom->map(update_mega_glove[jj+1][i])]; - improper_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->improper_type[j][p]; - improper_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom1[j][p]-1][1][rxnID]][i]; - improper_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom2[j][p]-1][1][rxnID]][i]; - improper_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom3[j][p]-1][1][rxnID]][i]; - improper_atom4[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom4[j][p]-1][1][rxnID]][i]; - num_improper[atom->map(update_mega_glove[jj+1][i])]++; - if (num_improper[atom->map(update_mega_glove[jj+1][i])] > atom->improper_per_atom) - error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); - delta_imprp++; + if (landlocked_atoms[j][rxnID] == 0) { + for (int p = 0; p < twomol->num_improper[j]; p++) { + if (landlocked_atoms[twomol->improper_atom1[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->improper_atom2[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->improper_atom3[j][p]-1][rxnID] == 1 || + landlocked_atoms[twomol->improper_atom4[j][p]-1][rxnID] == 1) { + insert_num = num_improper[atom->map(update_mega_glove[jj+1][i])]; + improper_type[atom->map(update_mega_glove[jj+1][i])][insert_num] = twomol->improper_type[j][p]; + improper_atom1[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom1[j][p]-1][1][rxnID]][i]; + improper_atom2[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom2[j][p]-1][1][rxnID]][i]; + improper_atom3[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom3[j][p]-1][1][rxnID]][i]; + improper_atom4[atom->map(update_mega_glove[jj+1][i])][insert_num] = update_mega_glove[equivalences[twomol->improper_atom4[j][p]-1][1][rxnID]][i]; + num_improper[atom->map(update_mega_glove[jj+1][i])]++; + if (num_improper[atom->map(update_mega_glove[jj+1][i])] > atom->improper_per_atom) + error->one(FLERR,"Fix bond/react topology/atom exceed system topology/atom"); + delta_imprp++; + } } } } From c0c64e812fabd092400fc08039cc56b7147aa936 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 18 Dec 2023 11:29:42 -0700 Subject: [PATCH 111/305] Enable default threading over neighbors for half list --- src/KOKKOS/pair_kokkos.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index c0b81cc594..a160f84d95 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -933,8 +933,9 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P EV_FLOAT ev; if (!fpair->lmp->kokkos->neigh_thread_set) - if (list->inum <= 16384 && NEIGHFLAG == FULL) - fpair->lmp->kokkos->neigh_thread = 1; + if (list->inum <= 16384) + if (NEIGHFLAG == FULL || !fpair->newton_pair) + fpair->lmp->kokkos->neigh_thread = 1; if (fpair->lmp->kokkos->neigh_thread) { From 3237c30117938f4a49bf7abac1ea0b0c1f54ae28 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 18 Dec 2023 15:40:14 -0500 Subject: [PATCH 112/305] reorganize the LAMMPS version and development description --- doc/github-development-workflow.md | 8 +-- doc/src/Howto_github.rst | 10 ++-- doc/src/Install_git.rst | 20 +++---- doc/src/Manual_version.rst | 91 +++++++++++++++++++----------- 4 files changed, 77 insertions(+), 52 deletions(-) diff --git a/doc/github-development-workflow.md b/doc/github-development-workflow.md index fccd75d29a..e16ae82764 100644 --- a/doc/github-development-workflow.md +++ b/doc/github-development-workflow.md @@ -36,10 +36,10 @@ requests. MUST be submitted as a pull request to GitHub. All changes to the "develop" branch must be made exclusively through merging pull requests. The "release" and "stable" branches, respectively, are only to be -updated upon feature or stable releases based on the associated -tags. Updates to the stable release in between stable releases +updated upon "feature releases" or "stable releases" based on the +associated tags. Updates to the stable release in between stable releases (for example, back-ported bug fixes) are first merged into the "maintenance" -branch and then into the "stable" branch as update releases. +branch and then into the "stable" branch as "stable update releases". Pull requests may also be submitted to (long-running) feature branches created by LAMMPS developers inside the LAMMPS project, if needed. Those @@ -131,7 +131,7 @@ testing -- that the code in the branch "develop" does not get easily broken. These tests are run after every update to a pull request. More extensive and time-consuming tests (including regression testing) are performed after code is merged to the "develop" branch. There are feature -releases of LAMMPS made about every 4-6 weeks at a point, when the LAMMPS +releases of LAMMPS made about every 4-8 weeks at a point, when the LAMMPS developers feel, that a sufficient number of changes have been included and all post-merge testing has been successful. These feature releases are marked with a `patch_` tag and the "release" branch diff --git a/doc/src/Howto_github.rst b/doc/src/Howto_github.rst index cbe1264d52..b81716c09d 100644 --- a/doc/src/Howto_github.rst +++ b/doc/src/Howto_github.rst @@ -480,11 +480,11 @@ Some recent changes to the workflow are not captured in this tutorial. For example, in addition to the *develop* branch, to which all new features should be submitted, there is also a *release*, a *stable*, and a *maintenance* branch; the *release* branch is updated from the -*develop* as part of a feature release, and *stable* (together with -*release*) are updated from *develop* when a stable release is made. In -between stable releases, selected bug fixes and infrastructure updates -are back-ported from the *develop* branch to the *maintenance* branch -and occasionally merged to *stable* as an update release. +*develop* branch as part of a "feature release", and *stable* (together +with *release*) are updated from *develop* when a "stable release" is +made. In between stable releases, selected bug fixes and infrastructure +updates are back-ported from the *develop* branch to the *maintenance* +branch and occasionally merged to *stable* as an update release. Furthermore, the naming of the release tags now follow the pattern "patch_" to simplify comparisons between releases. diff --git a/doc/src/Install_git.rst b/doc/src/Install_git.rst index b6d3ced0a5..45e364a226 100644 --- a/doc/src/Install_git.rst +++ b/doc/src/Install_git.rst @@ -28,16 +28,16 @@ provides `limited support for subversion clients `_. You can follow the LAMMPS development on 4 different git branches: -* **release** : this branch is updated with every patch or feature release; - updates are always "fast-forward" merges from *develop* -* **develop** : this branch follows the ongoing development and - is updated with every merge commit of a pull request -* **stable** : this branch is updated from the *release* branch with - every stable release version and also has selected bug fixes with every - update release when the *maintenance* branch is merged into it -* **maintenance** : this branch collects back-ported bug fixes from the - *develop* branch to the *stable* branch. It is used to update *stable* - for update releases and it synchronized with *stable* at each stable release. +* **develop** : this branch follows the ongoing development and is + updated with every merge commit of a pull request +* **release** : this branch is updated with every "feature release"; + updates are always "fast-forward" merges from *develop* +* **maintenance** : this branch collects back-ported bug fixes from the + *develop* branch to the *stable* branch. It is used to update the + *stable* branch for "stable update releases". +* **stable** : this branch is updated from the *release* branch with + every "stable release" version and also has selected bug fixes with + every "update release" when the *maintenance* branch is merged into it To access the git repositories on your box, use the clone command to create a local copy of the LAMMPS repository with a command like: diff --git a/doc/src/Manual_version.rst b/doc/src/Manual_version.rst index 8fb28fef84..f123267acf 100644 --- a/doc/src/Manual_version.rst +++ b/doc/src/Manual_version.rst @@ -3,45 +3,25 @@ What does a LAMMPS version mean The LAMMPS "version" is the date when it was released, such as 1 May 2014. LAMMPS is updated continuously, and we aim to keep it working -correctly and reliably at all times. You can follow its development -in a public `git repository on GitHub `_. - -Modifications of the LAMMPS source code (like bug fixes, code refactors, -updates to existing features, or addition of new features) are organized -into pull requests. Pull requests will be merged into the *develop* -branch of the git repository after they pass automated testing and code -review by the LAMMPS developers. When a sufficient number of changes -have accumulated *and* the *develop* branch version passes an extended -set of automated tests, we release it as a *feature release*, which are -currently made every 4 to 8 weeks. The *release* branch of the git -repository is updated with every such release. A summary of the most -important changes of the patch releases are on `this website page -`_. More detailed release notes are -`available on GitHub `_. - -Once or twice a year, we have a "stabilization period" where we apply -only bug fixes and small, non-intrusive changes to the *develop* -branch. At the same time, the code is subjected to more detailed and -thorough manual testing than the default automated testing. Also, -several variants of static code analysis are run to improve the overall -code quality, consistency, and compliance with programming standards, -best practices and style conventions. - -The release after such a stabilization period is called a *stable* -version and both, the *release* and the *stable* branches are updated -with it. Between stable releases, we collect back-ported bug fixes and -updates from the *develop* branch in the *maintenance* branch. From the -*maintenance* branch we make occasional update releases and update the -*stable* branch accordingly. +correctly and reliably at all times. Also, several variants of static +code analysis are run regularly to maintain or improve the overall code +quality, consistency, and compliance with programming standards, best +practices and style conventions. You can follow its development in a +public `git repository on GitHub `_. Each version of LAMMPS contains all the documented *features* up to and including its version date. For recently added features, we add markers to the documentation at which specific LAMMPS version a feature or keyword was added or significantly changed. -The version date is printed to the screen and log file every time you run -LAMMPS. It is also in the file src/version.h and in the LAMMPS -directory name created when you unpack a tarball. And it is on the +Identifying the Version +^^^^^^^^^^^^^^^^^^^^^^^ + +The version date is printed to the screen and log file every time you +run LAMMPS. There also is an indication, if a LAMMPS binary was +compiled from version with modifications **after** a release. +It is also visible in the file src/version.h and in the LAMMPS directory +name created when you unpack a downloaded tarball. And it is on the first page of the :doc:`manual `. * If you browse the HTML pages of the online version of the LAMMPS @@ -53,3 +33,48 @@ first page of the :doc:`manual `. * If you browse the HTML pages included in your downloaded tarball, they describe the version you have, which may be older than the online version. + +Development +^^^^^^^^^^^ + +Modifications of the LAMMPS source code (like bug fixes, code +refactoring, updates to existing features, or addition of new features) +are organized into pull requests. Pull requests will be merged into the +*develop* branch of the git repository after they pass automated testing +and code review by the LAMMPS developers. + +Feature Releases +^^^^^^^^^^^^^^^^ + +When a sufficient number of new features and updates have accumulated +*and* the LAMMPS version on the *develop* branch passes an extended set +of automated tests, we release it as a *feature release*, which are +currently made every 4 to 8 weeks. The *release* branch of the git +repository is updated with every such *feature release* and a tag in the +format ``patch_1May2014`` is added. A summary of the most important +changes of these releases for the current year are posted on `this +website page `_. More detailed release +notes are `available on GitHub +`_. + +Stable Releases +^^^^^^^^^^^^^^^ + +About once a year, we release a *stable release* version of LAMMPS. +This is done after a "stabilization period" where we apply only bug +fixes and small, non-intrusive changes to the *develop* branch but no +new features. At the same time, the code is subjected to more detailed +and thorough manual testing than the default automated testing. +After such a *stable release*, both the *release* and the *stable* +branches are updated and two tags are applied, a ``patch_1May2014`` format +and a ``stable_1May2014`` format tag. + +Stable Release Updates +^^^^^^^^^^^^^^^^^^^^^^ + +Between *stable releases*, we collect bug fixes and updates back-ported +from the *develop* branch in a branch called *maintenance*. From the +*maintenance* branch we make occasional *stable update releases* and +update the *stable* branch accordingly. The first update to the +``stable_1May2014`` release would be tagged as +``stable_1May2014_update1``. These updates contain no new features. From af222711aedc10d95baa35541e222d40b94c7605 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Mon, 18 Dec 2023 15:52:30 -0600 Subject: [PATCH 113/305] Added text to explain the output of ctest -V a bit more --- doc/src/Developer_unittest.rst | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index c0cdee6c09..5cb7a0ebde 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -569,6 +569,10 @@ For example, the following output snippet shows the failed unit test Expected: (err) <= (epsilon) Actual: 0.00022892713393549854 vs 0.0001 +The failed assertions provide line numbers in the test source +(e.g. ``test_main.cpp:56``), from which one can understand what +specific assertion failed. + Note that the force style engine runs one of a small number of systems in a rather off-equilibrium configuration with a few atoms for a few steps, writes data and restart files, uses :doc:`the clear command @@ -606,15 +610,18 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V +``ctest`` with the ``-V`` flag also shows the exact command line +of the test. One can then use ``gdb --args`` to furthere debug and +catch exceptions with the test command, for example, + +.. code-block:: bash + + gdb --args /path/to/lammps/build/test_pair_style "/path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml" + + It is recommended to configure the build with ``-D BUILD_SHARED_LIBS=on`` and use a custom linker to shorten the build time during recompilation. Installing `ccache` in your development environment helps speed up recompilation by caching previous compilations and detecting when the same compilation is being done again. Please see :doc:`Build_development` for further details. - - - - - - From 8e4871c5e1d5527a0e10ac2a30e5048a14db5e55 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Mon, 18 Dec 2023 16:37:31 -0700 Subject: [PATCH 114/305] Tune team params --- src/KOKKOS/pair_kokkos.h | 61 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index a160f84d95..bd543bbbba 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -19,10 +19,12 @@ #ifndef LMP_PAIR_KOKKOS_H #define LMP_PAIR_KOKKOS_H -#include "Kokkos_Macros.hpp" #include "pair.h" // IWYU pragma: export #include "neighbor_kokkos.h" #include "neigh_list_kokkos.h" +#include "math_special.h" +#include "update.h" +#include "Kokkos_Macros.hpp" #include "Kokkos_ScatterView.hpp" namespace LAMMPS_NS { @@ -907,24 +909,21 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t -int GetTeamSize(FunctorStyle& KOKKOS_GPU_ARG(functor), int KOKKOS_GPU_ARG(inum), - int KOKKOS_GPU_ARG(reduce_flag), int team_size, int KOKKOS_GPU_ARG(vector_length)) { +template +int GetMaxNeighs(NeighStyle* list) +{ + auto d_ilist = list->d_ilist; + auto d_numneigh = list->d_numneigh; + int inum = list->inum; -#ifdef LMP_KOKKOS_GPU - int team_size_max; + int maxneigh = 0; + Kokkos::parallel_reduce(inum, LAMMPS_LAMBDA(const int ii, int &maxneigh) { + const int i = d_ilist[ii]; + const int num_neighs = d_numneigh[i]; + maxneigh = MAX(maxneigh,num_neighs); + }, Kokkos::Max(maxneigh)); - if (reduce_flag) - team_size_max = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelReduceTag()); - else - team_size_max = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelForTag()); - - if (team_size*vector_length > team_size_max) - team_size = team_size_max/vector_length; -#else - team_size = 1; -#endif - return team_size; + return maxneigh; } // Submit ParallelFor for NEIGHFLAG=HALF,HALFTHREAD,FULL @@ -939,19 +938,35 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (fpair->lmp->kokkos->neigh_thread) { - int vector_length = 8; - int atoms_per_team = 32; + static int vectorsize = 0; + static int lastcall = -1; + +#if defined(LMP_KOKKOS_GPU) + + #if defined(KOKKOS_ENABLE_HIP) + int max_vectorsize = 64; + #else + int max_vectorsize = 32; + #endif + + if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { + lastcall = fpair->lmp->update->ntimestep; + vectorsize = GetMaxNeighs(list); + vectorsize = MathSpecial::powint(2,int(log2(vectorsize))); // round down to nearest power of 2 + vectorsize = MIN(vectorsize,max_vectorsize); + } +#else + vectorsize = 1; +#endif if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - atoms_per_team = GetTeamSize(ff, list->inum, (fpair->eflag || fpair->vflag), atoms_per_team, vector_length); - Kokkos::TeamPolicy > policy(list->inum,atoms_per_team,vector_length); + Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } else { PairComputeFunctor ff(fpair,list); - atoms_per_team = GetTeamSize(ff, list->inum, (fpair->eflag || fpair->vflag), atoms_per_team, vector_length); - Kokkos::TeamPolicy > policy(list->inum,atoms_per_team,vector_length); + Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } From 6c798412b459079b1d0275b3147c480d743b17ed Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Mon, 18 Dec 2023 18:52:42 -0500 Subject: [PATCH 115/305] fix typo and remove unnecessary quotes --- doc/src/Developer_unittest.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/Developer_unittest.rst b/doc/src/Developer_unittest.rst index 5cb7a0ebde..b48c3b4e17 100644 --- a/doc/src/Developer_unittest.rst +++ b/doc/src/Developer_unittest.rst @@ -611,12 +611,12 @@ and run the test with verbose output. For example, env TEST_ARGS=-v ctest -R ^MolPairStyle:lj_cut_coul_long -V ``ctest`` with the ``-V`` flag also shows the exact command line -of the test. One can then use ``gdb --args`` to furthere debug and +of the test. One can then use ``gdb --args`` to further debug and catch exceptions with the test command, for example, .. code-block:: bash - gdb --args /path/to/lammps/build/test_pair_style "/path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml" + gdb --args /path/to/lammps/build/test_pair_style /path/to/lammps/unittest/force-styles/tests/mol-pair-lj_cut_coul_long.yaml It is recommended to configure the build with ``-D From e98df7018b5e73fa86bfb2ffce1077d5781fb3e7 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Tue, 19 Dec 2023 08:17:43 +0800 Subject: [PATCH 116/305] update the example files --- examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP | 1 - examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP | 1 + examples/PACKAGES/interlayer/ilp_tmds/in.mos2 | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP create mode 120000 examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP diff --git a/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP b/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP deleted file mode 120000 index 75dd894eef..0000000000 --- a/examples/PACKAGES/interlayer/ilp_tmds/MoS2.ILP +++ /dev/null @@ -1 +0,0 @@ -../../../../potentials/MoS2.ILP \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP b/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP new file mode 120000 index 0000000000..70f7ea18df --- /dev/null +++ b/examples/PACKAGES/interlayer/ilp_tmds/TMD.ILP @@ -0,0 +1 @@ +../../../../potentials/TMD.ILP \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 b/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 index b77f2fe719..0db4ec12d5 100644 --- a/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 +++ b/examples/PACKAGES/interlayer/ilp_tmds/in.mos2 @@ -12,7 +12,7 @@ mass 4 95.94 pair_style hybrid/overlay sw/mod sw/mod ilp/tmd 16.0 pair_coeff * * sw/mod 1 tmd.sw.mod Mo S S NULL NULL NULL pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL Mo S S -pair_coeff * * ilp/tmd MoS2.ILP Mo S S Mo S S +pair_coeff * * ilp/tmd TMD.ILP Mo S S Mo S S # Calculate the pair potential compute 0 all pair ilp/tmd From ab29200c60ad807ed3c7415738eb8c62737a3d50 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 19 Dec 2023 10:26:52 -0700 Subject: [PATCH 117/305] More tuning --- src/KOKKOS/pair_kokkos.h | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index bd543bbbba..55fe01f6d2 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -65,6 +65,7 @@ struct PairComputeFunctor { typename AT::t_f_array f; typename AT::t_efloat_1d d_eatom; typename AT::t_virial_array d_vatom; + int inum; using KKDeviceType = typename KKDevice::value; using DUP = NeedDup_v; @@ -97,6 +98,7 @@ struct PairComputeFunctor { dup_f = Kokkos::Experimental::create_scatter_view(c.f); dup_eatom = Kokkos::Experimental::create_scatter_view(c.d_eatom); dup_vatom = Kokkos::Experimental::create_scatter_view(c.d_vatom); + inum = list.inum; }; // Set copymode = 1 so parent allocations aren't destructed by copies of the style @@ -287,7 +289,6 @@ struct PairComputeFunctor { auto a_f = dup_f.template access::value>(); - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; @@ -364,13 +365,11 @@ struct PairComputeFunctor { auto a_f = dup_f.template access::value>(); - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); int firstatom = team.league_rank()*atoms_per_team; int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { - const int i = list.d_ilist[ii]; const X_FLOAT xtmp = c.x(i,0); const X_FLOAT ytmp = c.x(i,1); @@ -452,11 +451,9 @@ struct PairComputeFunctor { EV_FLOAT ev; - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, firstatom, lastatom), [&] (const int &ii) { const int i = list.d_ilist[ii]; @@ -616,7 +613,6 @@ struct PairComputeFunctor { EV_FLOAT ev; - const int inum = team.league_size(); const int atoms_per_team = team.team_size(); const int firstatom = team.league_rank()*atoms_per_team; const int lastatom = firstatom + atoms_per_team < inum ? firstatom + atoms_per_team : inum; @@ -926,6 +922,14 @@ int GetMaxNeighs(NeighStyle* list) return maxneigh; } +template +void GetMaxTeamSize(FunctorStyle& functor, int inum, + int &teamsize_max_for, int &teamsize_max_reduce) +{ + teamsize_max_for = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelForTag()); + teamsize_max_reduce = Kokkos::TeamPolicy(inum,Kokkos::AUTO).team_size_max(functor,Kokkos::ParallelReduceTag()); +} + // Submit ParallelFor for NEIGHFLAG=HALF,HALFTHREAD,FULL template EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&PairStyle::EnabledNeighFlags) != 0, NeighListKokkos*> list) { @@ -939,6 +943,7 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (fpair->lmp->kokkos->neigh_thread) { static int vectorsize = 0; + static int atoms_per_team = 0; static int lastcall = -1; #if defined(LMP_KOKKOS_GPU) @@ -952,21 +957,39 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { lastcall = fpair->lmp->update->ntimestep; vectorsize = GetMaxNeighs(list); - vectorsize = MathSpecial::powint(2,int(log2(vectorsize))); // round down to nearest power of 2 + vectorsize = MathSpecial::powint(2,(int(log2(vectorsize) + 0.5))); // round to nearest power of 2 vectorsize = MIN(vectorsize,max_vectorsize); + + int teamsize_max_for,teamsize_max_reduce; + if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { + PairComputeFunctor ff(fpair,list); + GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + } else { + PairComputeFunctor ff(fpair,list); + GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + } + + int teamsize_max = teamsize_max_for; + if (fpair->eflag || fpair->vflag) + teamsize_max = teamsize_max_reduce; + atoms_per_team = teamsize_max/vectorsize; } #else vectorsize = 1; + atoms_per_team = 1; #endif + const int inum = list->inum; + const int num_teams = inum / atoms_per_team + (inum % atoms_per_team ? 1 : 0); + if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); + Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } else { PairComputeFunctor ff(fpair,list); - Kokkos::TeamPolicy > policy(list->inum,Kokkos::AUTO(),vectorsize); + Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); } From 86f87e0f7b85b8d157e790249abf31b54abb3bf3 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Tue, 19 Dec 2023 10:46:41 -0700 Subject: [PATCH 118/305] Update docs --- doc/src/package.rst | 16 ++++++++-------- src/KOKKOS/pair_kokkos.h | 31 ++++++++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/doc/src/package.rst b/doc/src/package.rst index 63a7f095ad..65392845e9 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -474,13 +474,13 @@ If the *neigh/thread* keyword is set to *off*, then the KOKKOS package threads only over atoms. However, for small systems, this may not expose enough parallelism to keep a GPU busy. When this keyword is set to *on*, the KOKKOS package threads over both atoms and neighbors of atoms. When -using *neigh/thread* *on*, a full neighbor list must also be used. Using -*neigh/thread* *on* may be slower for large systems, so this this option -is turned on by default only when there are 16K atoms or less owned by -an MPI rank and when using a full neighbor list. Not all KOKKOS-enabled -potentials support this keyword yet, and only thread over atoms. Many -simple pairwise potentials such as Lennard-Jones do support threading -over both atoms and neighbors. +using *neigh/thread* *on*, the :doc:`newton pair ` setting must +be "off". Using *neigh/thread* *on* may be slower for large systems, so +this this option is turned on by default only when running on one or +more GPUs and there are 16k atoms or less owned by an MPI rank. Not all +KOKKOS-enabled potentials support this keyword yet, and only thread over +atoms. Many simple pairwise potentials such as Lennard-Jones do support +threading over both atoms and neighbors. If the *neigh/transpose* keyword is set to *off*, then the KOKKOS package will use the same memory layout for building the neighbor list on @@ -732,7 +732,7 @@ comm = device, sort = device, neigh/transpose = off, gpu/aware = on. When LAMMPS can safely detect that GPU-aware MPI is not available, the default value of gpu/aware becomes "off". For CPUs or Xeon Phis, the option defaults are neigh = half, neigh/qeq = half, newton = on, binsize = 0.0, comm = no, and sort -= no. The option neigh/thread = on when there are 16K atoms or less on an MPI += no. For GPUs, option neigh/thread = on when there are 16k atoms or less on an MPI rank, otherwise it is "off". These settings are made automatically by the required "-k on" :doc:`command-line switch `. You can change them by using the package kokkos command in your input script or via the :doc:`-pk diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index 55fe01f6d2..eea2cd5316 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -935,8 +935,10 @@ template*> list) { EV_FLOAT ev; + const int inum = list->inum; + if (!fpair->lmp->kokkos->neigh_thread_set) - if (list->inum <= 16384) + if (fpair->lmp->kokkos->ngpus && inum <= 16000) if (NEIGHFLAG == FULL || !fpair->newton_pair) fpair->lmp->kokkos->neigh_thread = 1; @@ -947,26 +949,26 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P static int lastcall = -1; #if defined(LMP_KOKKOS_GPU) - - #if defined(KOKKOS_ENABLE_HIP) - int max_vectorsize = 64; - #else - int max_vectorsize = 32; - #endif - if (!vectorsize || lastcall < fpair->lmp->neighbor->lastcall) { lastcall = fpair->lmp->update->ntimestep; vectorsize = GetMaxNeighs(list); vectorsize = MathSpecial::powint(2,(int(log2(vectorsize) + 0.5))); // round to nearest power of 2 + + #if defined(KOKKOS_ENABLE_HIP) + int max_vectorsize = 64; + #else + int max_vectorsize = 32; + #endif + vectorsize = MIN(vectorsize,max_vectorsize); int teamsize_max_for,teamsize_max_reduce; if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + GetMaxTeamSize(ff, inum, teamsize_max_for, teamsize_max_reduce); } else { PairComputeFunctor ff(fpair,list); - GetMaxTeamSize(ff, list->inum, teamsize_max_for, teamsize_max_reduce); + GetMaxTeamSize(ff, inum, teamsize_max_for, teamsize_max_reduce); } int teamsize_max = teamsize_max_for; @@ -979,7 +981,6 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P atoms_per_team = 1; #endif - const int inum = list->inum; const int num_teams = inum / atoms_per_team + (inum % atoms_per_team ? 1 : 0); if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { @@ -996,13 +997,13 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P } else { if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { PairComputeFunctor ff(fpair,list); - if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(list->inum,ff,ev); - else Kokkos::parallel_for(list->inum,ff); + if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(inum,ff,ev); + else Kokkos::parallel_for(inum,ff); ff.contribute(); } else { PairComputeFunctor ff(fpair,list); - if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(list->inum,ff,ev); - else Kokkos::parallel_for(list->inum,ff); + if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(inum,ff,ev); + else Kokkos::parallel_for(inum,ff); ff.contribute(); } } From e72f186123df9d24f1f4f2bac5bdc4fad8d2a8e6 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Tue, 19 Dec 2023 22:01:18 -0700 Subject: [PATCH 119/305] check ghost vel in pair bpm/spring --- src/BPM/pair_bpm_spring.cpp | 13 +++++++++++++ src/BPM/pair_bpm_spring.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/BPM/pair_bpm_spring.cpp b/src/BPM/pair_bpm_spring.cpp index 1177156359..01cee91b4c 100644 --- a/src/BPM/pair_bpm_spring.cpp +++ b/src/BPM/pair_bpm_spring.cpp @@ -19,6 +19,7 @@ #include "force.h" #include "memory.h" #include "neigh_list.h" +#include "neighbor.h" #include @@ -202,6 +203,18 @@ void PairBPMSpring::coeff(int narg, char **arg) if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients"); } +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairBPMSpring::init_style() +{ + if (comm->ghost_velocity == 0) + error->all(FLERR,"Pair bpm/spring requires ghost atoms store velocity"); + + neighbor->add_request(this); +} + /* ---------------------------------------------------------------------- init for one type pair i,j and corresponding j,i ------------------------------------------------------------------------- */ diff --git a/src/BPM/pair_bpm_spring.h b/src/BPM/pair_bpm_spring.h index 3cb281bff3..c10e4a3400 100644 --- a/src/BPM/pair_bpm_spring.h +++ b/src/BPM/pair_bpm_spring.h @@ -31,6 +31,7 @@ class PairBPMSpring : public Pair { void compute(int, int) override; void settings(int, char **) override; void coeff(int, char **) override; + void init_style() override; double init_one(int, int) override; void write_restart(FILE *) override; void read_restart(FILE *) override; From aa221e2f2bbaa5770eea4db4ae5ba38fcaffc990 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 10:28:53 +0800 Subject: [PATCH 120/305] add new potential file for aip/water/2dm --- .../aip_water_2dm/CBNOH.aip.water.2dm | 1 + .../aip_water_2dm/COH.aip.water.2dm | 1 - potentials/CBNOH.aip.water.2dm | 49 +++++++++++++++++++ potentials/COH.aip.water.2dm | 28 ----------- 4 files changed, 50 insertions(+), 29 deletions(-) create mode 120000 examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm delete mode 120000 examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm create mode 100755 potentials/CBNOH.aip.water.2dm delete mode 100644 potentials/COH.aip.water.2dm diff --git a/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm b/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm new file mode 120000 index 0000000000..60c9c3a8f4 --- /dev/null +++ b/examples/PACKAGES/interlayer/aip_water_2dm/CBNOH.aip.water.2dm @@ -0,0 +1 @@ +../../../../potentials/CBNOH.aip.water.2dm \ No newline at end of file diff --git a/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm b/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm deleted file mode 120000 index fe5cccfcd2..0000000000 --- a/examples/PACKAGES/interlayer/aip_water_2dm/COH.aip.water.2dm +++ /dev/null @@ -1 +0,0 @@ -../../../../potentials/COH.aip.water.2dm \ No newline at end of file diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm new file mode 100755 index 0000000000..5efcc99fcc --- /dev/null +++ b/potentials/CBNOH.aip.water.2dm @@ -0,0 +1,49 @@ +# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127(18), 8704 (2023). +# CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). +# Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions +# The parameters below are fitted against the PBE + MBD-NL (graphene/water) and SCAN (hBN/water) DFT reference data. +# +# -------------------Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ +# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut +# +# For graphene and hydrocarbons +C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 +H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 +C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 +H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 + +# For hBN +B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 +N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 +B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 +N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 + +# For water-graphene +C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 +C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 +Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 +Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 + +# For water-hBN +N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 +N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 +B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 +B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 +Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 +Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 +Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 +Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 + +# # The AIPs for other elements are tunred off +H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 + +Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 diff --git a/potentials/COH.aip.water.2dm b/potentials/COH.aip.water.2dm deleted file mode 100644 index 5325399abe..0000000000 --- a/potentials/COH.aip.water.2dm +++ /dev/null @@ -1,28 +0,0 @@ -# DATE: 2022-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127, 8704 (2023). -# Anisotropic Interfacial Potential (AIP) parameters for water/graphene heterojunctions -# The parameters below are fitted against the PBE + MBD-NL DFT reference data from 2.5 A to 15 A. -# -# ----------------- Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ -# beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut -# For graphene and hydrocarbons -C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 -H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 -C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 -H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 - -# For water-graphene -C Ow 5.45369612 6.18172364 1.25025450 3.34909245 0.68780636 9.05706482 1.23249498 2.77577173 100226.55503127 1.0 2.0 -C Hw 2.55380862 9.68664390 1.96489198 41.77617053 -16.30012807 9.01568534 0.74415463 2.41545571 7409.12856378 1.0 2.0 -Ow C 5.45369612 6.18172364 1.25025450 3.34909245 0.68780636 9.05706482 1.23249498 2.77577173 100226.55503127 1.0 1.2 -Hw C 2.55380862 9.68664390 1.96489198 41.77617053 -16.30012807 9.01568534 0.74415463 2.41545571 7409.12856378 1.0 1.2 - -# # The ILPs for other systems are set to zero -H Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -H Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Ow H 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw H 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 - -Ow Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Ow Hw 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 -Hw Ow 5.45369612 6.18172364 1.25025450 0.00000000 0.00000000 9.05706482 1.23249498 2.77577173 0.00000000 1.0 1.2 From b58203fc52277d181cd4884bd54c952ced3a90ca Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 10:44:03 +0800 Subject: [PATCH 121/305] update the doc file --- doc/src/pair_aip_water_2dm.rst | 37 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index 5cee40edda..f480819ae4 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -22,13 +22,24 @@ Examples .. code-block:: LAMMPS pair_style hybrid/overlay aip/water/2dm 16.0 1 - pair_coeff * * aip/water/2dm COH.aip.water.2dm C Ow Hw + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 2 3 1 1 0.1546 10 8.5 - pair_coeff 2 2 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O - pair_coeff 2 3 lj/cut/tip4p/long 0.0 0.0 # O-H - pair_coeff 3 3 lj/cut/tip4p/long 0.0 0.0 # H-H - pair_coeff * * aip/water/2dm COH.aip.water.2dm C Ow Hw + pair_coeff 2 2 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O + pair_coeff 2 3 lj/cut/tip4p/long 0.0 0.0 # O-H + pair_coeff 3 3 lj/cut/tip4p/long 0.0 0.0 # H-H + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw + + pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 3 4 1 1 0.1546 10 8.5 coul/shield 16.0 1 + pair_coeff 1*2 1*2 none + pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O + pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H + pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm i B N Ow Hw + pair_coeff 1 3 coul/shield 1.333 + pair_coeff 1 4 coul/shield 1.333 + pair_coeff 2 3 coul/shield 1.333 + pair_coeff 2 4 coul/shield 1.333 Description """"""""""" @@ -37,7 +48,7 @@ Description The *aip/water/2dm* style computes the anisotropic interfacial potential (AIP) potential for interfaces of water with two-dimensional (2D) -materials as described in :ref:`(Feng) `. +materials as described in :ref:`(Feng1) ` and :ref:`(Feng2) `. .. math:: @@ -67,7 +78,7 @@ larger than :math:`r_c` :doc:`pair_style ilp_graphene_hbn oxygen-hydrogen bonds and the normal vector of the central oxygen atom is defined as their average. -The provided parameter file, ``COH.aip.water.2dm``, is intended for use +The provided parameter file, ``CBNOH.aip.water.2dm``, is intended for use with *metal* :doc:`units `, with energies in meV. Two additional parameters, *S*, and *rcut* are included in the parameter file. *S* is designed to facilitate scaling of energies; *rcut* is the cutoff for an @@ -77,7 +88,7 @@ the calculation of the normals for all atom pairs. .. note:: The parameters presented in the provided parameter file, - ``COH.aip.water.2dm``, are fitted with the taper function enabled by + ``CBNOH.aip.water.2dm``, are fitted with the taper function enabled by setting the cutoff equal to 16.0 Angstrom. Using a different cutoff or taper function setting should be carefully checked as they can lead to significant errors. These parameters provide a good @@ -134,7 +145,7 @@ if LAMMPS was built with that package. See the :doc:`Build package This pair style requires the newton setting to be *on* for pair interactions. -The ``COH.aip.water.2dm`` potential file provided with LAMMPS is +The ``CBNOH.aip.water.2dm`` potential file provided with LAMMPS is parameterized for *metal* units. You can use this pair style with any LAMMPS units, but you would need to create your own potential file with parameters in the appropriate units, if your simulation does not use @@ -162,6 +173,10 @@ tap_flag = 1 ---------- -.. _Feng: +.. _Feng1: -**(Feng)** Z. Feng and W. Ouyang et al., J. Phys. Chem. C. 127, 8704-8713 (2023). +**(Feng1)** Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C. 127(18), 8704-8713 (2023). + +.. _Feng2: + +**(Feng2)** Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). From b857cd9e39d8363f52a535220381ab8c662279d9 Mon Sep 17 00:00:00 2001 From: oywg11 Date: Thu, 21 Dec 2023 11:31:26 +0800 Subject: [PATCH 122/305] update the doc file --- doc/src/pair_aip_water_2dm.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index f480819ae4..f6c56eff6e 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -35,7 +35,7 @@ Examples pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H - pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm i B N Ow Hw + pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm B N Ow Hw pair_coeff 1 3 coul/shield 1.333 pair_coeff 1 4 coul/shield 1.333 pair_coeff 2 3 coul/shield 1.333 @@ -73,7 +73,7 @@ larger than :math:`r_c` :doc:`pair_style ilp_graphene_hbn .. note:: This pair style uses the atomic normal vector definition from - :ref:`(Feng) `), where the atomic normal vectors of the + :ref:`(Feng1) `), where the atomic normal vectors of the hydrogen atoms are assumed to lie along the corresponding oxygen-hydrogen bonds and the normal vector of the central oxygen atom is defined as their average. From dedbd8f6db58bc6582391477059c2b5e3eccc1ca Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 19 Dec 2023 23:37:07 -0500 Subject: [PATCH 123/305] improvements from clang-tidy --- examples/COUPLE/simple/simple.cpp | 18 +++++++++--------- src/DRUDE/fix_tgnh_drude.cpp | 1 - src/ML-PACE/compute_pace.cpp | 12 ++---------- src/ML-SNAP/compute_sna_atom.cpp | 6 ++---- src/OPENMP/npair_bin_ghost_omp.cpp | 2 +- src/REAXFF/compute_reaxff_atom.cpp | 5 ++--- src/RIGID/fix_rigid_nh_small.cpp | 1 - src/library.cpp | 2 +- src/nstencil_bin.cpp | 4 ++-- src/nstencil_multi.cpp | 4 ++-- src/nstencil_multi_old.cpp | 4 ++-- tools/lammps-gui/main.cpp | 2 +- tools/lammps-gui/preferences.cpp | 2 +- tools/lammps-gui/stdcapture.cpp | 6 +++--- unittest/fortran/wrap_configuration.cpp | 2 +- unittest/utils/test_lepton.cpp | 8 ++++---- 16 files changed, 33 insertions(+), 46 deletions(-) diff --git a/examples/COUPLE/simple/simple.cpp b/examples/COUPLE/simple/simple.cpp index c8727cc81f..f6365ac3de 100644 --- a/examples/COUPLE/simple/simple.cpp +++ b/examples/COUPLE/simple/simple.cpp @@ -67,7 +67,7 @@ int main(int narg, char **arg) FILE *fp; if (me == 0) { fp = fopen(arg[2],"r"); - if (fp == NULL) { + if (fp == nullptr) { printf("ERROR: Could not open LAMMPS input script\n"); MPI_Abort(MPI_COMM_WORLD,1); } @@ -78,14 +78,14 @@ int main(int narg, char **arg) // (could just send it to proc 0 of comm_lammps and let it Bcast) // all LAMMPS procs call input->one() on the line - LAMMPS *lmp = NULL; - if (lammps == 1) lmp = new LAMMPS(0,NULL,comm_lammps); + LAMMPS *lmp = nullptr; + if (lammps == 1) lmp = new LAMMPS(0,nullptr,comm_lammps); int n; char line[1024]; - while (1) { + while (true) { if (me == 0) { - if (fgets(line,1024,fp) == NULL) n = 0; + if (fgets(line,1024,fp) == nullptr) n = 0; else n = strlen(line) + 1; if (n == 0) fclose(fp); } @@ -101,8 +101,8 @@ int main(int narg, char **arg) // put coords back into LAMMPS // run a single step with changed coords - double *x = NULL; - double *v = NULL; + double *x = nullptr; + double *v = nullptr; if (lammps == 1) { lmp->input->one("run 10"); @@ -147,7 +147,7 @@ int main(int narg, char **arg) // create_atoms() to create new ones with old coords, vels // initial thermo should be same as step 20 - int *type = NULL; + int *type = nullptr; if (lammps == 1) { int natoms = static_cast (lmp->atom->natoms); @@ -155,7 +155,7 @@ int main(int narg, char **arg) for (int i = 0; i < natoms; i++) type[i] = 1; lmp->input->one("delete_atoms group all"); - lammps_create_atoms(lmp,natoms,NULL,type,x,v,NULL,0); + lammps_create_atoms(lmp,natoms,nullptr,type,x,v,nullptr,0); lmp->input->one("run 10"); } diff --git a/src/DRUDE/fix_tgnh_drude.cpp b/src/DRUDE/fix_tgnh_drude.cpp index 987408fe63..b23acd349b 100644 --- a/src/DRUDE/fix_tgnh_drude.cpp +++ b/src/DRUDE/fix_tgnh_drude.cpp @@ -1076,7 +1076,6 @@ void FixTGNHDrude::couple() void FixTGNHDrude::remap() { - int i; double oldlo,oldhi; double expfac; diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index 12693a58d9..b96432cfe3 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -73,10 +73,7 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : auto potential_file_name = utils::get_potential_file_path(arg[3]); delete acecimpl->basis_set; acecimpl->basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl->basis_set->cutoffmax; cutmax = acecimpl->basis_set->cutoffmax; - double cuti; - double radelemall = 0.5; //# of rank 1, rank > 1 functions @@ -178,7 +175,6 @@ void ComputePACE::init_list(int /*id*/, NeighList *ptr) void ComputePACE::compute_array() { int ntotal = atom->nlocal + atom->nghost; - double **f = atom->f; invoked_array = update->ntimestep; // grow pace_peratom array if necessary @@ -208,9 +204,6 @@ void ComputePACE::compute_array() // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - SPECIES_TYPE *mus; - NS_TYPE *ns; - LS_TYPE *ls; const int inum = list->inum; const int* const ilist = list->ilist; @@ -234,7 +227,6 @@ void ComputePACE::compute_array() // compute pace 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; const int ntypes = atom->ntypes; @@ -251,8 +243,8 @@ void ComputePACE::compute_array() delete acecimpl->ace; acecimpl->ace = new ACECTildeEvaluator(*acecimpl->basis_set); - acecimpl->ace->compute_projections = 1; - acecimpl->ace->compute_b_grad = 1; + acecimpl->ace->compute_projections = true; + acecimpl->ace->compute_b_grad = true; int n_r1, n_rp = 0; n_r1 = acecimpl->basis_set->total_basis_size_rank1[0]; n_rp = acecimpl->basis_set->total_basis_size[0]; diff --git a/src/ML-SNAP/compute_sna_atom.cpp b/src/ML-SNAP/compute_sna_atom.cpp index 3fe3cca92d..da49b15117 100644 --- a/src/ML-SNAP/compute_sna_atom.cpp +++ b/src/ML-SNAP/compute_sna_atom.cpp @@ -299,7 +299,7 @@ void ComputeSNAAtom::compute_peratom() // ############################################################################## // // ##### Start of section for computing bispectrum on nnn nearest neighbors ##### // // ############################################################################## // - if (nearest_neighbors_mode == true) { + if (nearest_neighbors_mode) { // ##### 1) : consider full neighbor list in rlist memory->create(distsq, jnum, "snann/atom:distsq"); memory->create(rlist, jnum, 3, "snann/atom:rlist"); @@ -308,7 +308,6 @@ void ComputeSNAAtom::compute_peratom() for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; j &= NEIGHMASK; - int jtype = type[j]; const double delx = xtmp - x[j][0]; const double dely = ytmp - x[j][1]; @@ -614,10 +613,9 @@ double * ComputeSNAAtom::tanh_weights(double * rsq, double rcut, double delta, i return w; } -double ComputeSNAAtom::sum_weights(double * rsq, double * w, int ncounts) +double ComputeSNAAtom::sum_weights(double * /*rsq*/, double * w, int ncounts) { double S=0.; - double rloc=0.; for (int i=0; i::build(NeighList *list) #endif NPAIR_OMP_SETUP(nall); - int i, j, k, n, itype, jtype, ibin, bin_start, which, imol, iatom; + int i, j, k, n, itype, jtype, ibin, which, imol, iatom; tagint tagprev; double xtmp, ytmp, ztmp, delx, dely, delz, rsq; int xbin, ybin, zbin, xbin2, ybin2, zbin2; diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index 1b54f3b82a..cc9109915c 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -34,11 +34,10 @@ using namespace ReaxFF; /* ---------------------------------------------------------------------- */ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : - Compute(lmp, narg, arg), - abo(nullptr), neighid(nullptr), bondcount(nullptr), reaxff(nullptr) + Compute(lmp, narg, arg), neighid(nullptr), abo(nullptr), bondcount(nullptr), reaxff(nullptr) { if (atom->tag_consecutive() == 0) - error->all(FLERR,"Atom IDs must be consecutive for compute reaxff/atom"); + error->all(FLERR, "Atom IDs must be consecutive for compute reaxff/atom"); peratom_flag = 1; diff --git a/src/RIGID/fix_rigid_nh_small.cpp b/src/RIGID/fix_rigid_nh_small.cpp index 952dc29032..3ee11e28d2 100644 --- a/src/RIGID/fix_rigid_nh_small.cpp +++ b/src/RIGID/fix_rigid_nh_small.cpp @@ -219,7 +219,6 @@ void FixRigidNHSmall::init() } } - int icompute; if (tcomputeflag) { temperature = modify->get_compute_by_id(id_temp); if (!temperature) diff --git a/src/library.cpp b/src/library.cpp index 1acdfc4787..a629df7b8c 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -709,7 +709,7 @@ void lammps_commands_string(void *handle, const char *str) break; } - lmp->input->one(cmd.c_str()); + lmp->input->one(cmd); } } } diff --git a/src/nstencil_bin.cpp b/src/nstencil_bin.cpp index 2b7c15cff6..ccefa16978 100644 --- a/src/nstencil_bin.cpp +++ b/src/nstencil_bin.cpp @@ -55,9 +55,9 @@ void NStencilBin::create() // Now only include "upper right" bins for half and ortho stencils if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; if (bin_distance(i, j, k) < cutneighmaxsq) stencil[nstencil++] = k * mbiny * mbinx + j * mbinx + i; diff --git a/src/nstencil_multi.cpp b/src/nstencil_multi.cpp index a73215a058..693c415876 100644 --- a/src/nstencil_multi.cpp +++ b/src/nstencil_multi.cpp @@ -115,9 +115,9 @@ void NStencilMulti::create() if (HALF && (!TRI)) { if (half_flag) { if (DIM_3D) { - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; } else { - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; } } } diff --git a/src/nstencil_multi_old.cpp b/src/nstencil_multi_old.cpp index 6d34d8881f..8648e6f73c 100644 --- a/src/nstencil_multi_old.cpp +++ b/src/nstencil_multi_old.cpp @@ -65,9 +65,9 @@ void NStencilMultiOld::create() // Now only include "upper right" bins for half and ortho stencils if (HALF && (!DIM_3D) && (!TRI)) - if (! (j > 0 || (j == 0 && i > 0))) continue; + if (j <= 0 && (j != 0 || i <= 0)) continue; if (HALF && DIM_3D && (!TRI)) - if (! (k > 0 || j > 0 || (j == 0 && i > 0))) continue; + if (k <= 0 && j <= 0 && (j != 0 || i <= 0)) continue; rsq = bin_distance(i, j, k); if (rsq < typesq) { diff --git a/tools/lammps-gui/main.cpp b/tools/lammps-gui/main.cpp index cf09fbb892..d70e9d3e46 100644 --- a/tools/lammps-gui/main.cpp +++ b/tools/lammps-gui/main.cpp @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) LammpsGui w(nullptr, infile); w.show(); - return a.exec(); + return QApplication::exec(); } // Local Variables: diff --git a/tools/lammps-gui/preferences.cpp b/tools/lammps-gui/preferences.cpp index fd01bb5046..27cc106008 100644 --- a/tools/lammps-gui/preferences.cpp +++ b/tools/lammps-gui/preferences.cpp @@ -177,7 +177,7 @@ void Preferences::accept() msg.exec(); const char *path = mystrdup(QCoreApplication::applicationFilePath()); const char *arg0 = mystrdup(QCoreApplication::arguments().at(0)); - execl(path, arg0, (char *)NULL); + execl(path, arg0, (char *)nullptr); } // reformatting settings diff --git a/tools/lammps-gui/stdcapture.cpp b/tools/lammps-gui/stdcapture.cpp index b09aebf053..8be543e70e 100644 --- a/tools/lammps-gui/stdcapture.cpp +++ b/tools/lammps-gui/stdcapture.cpp @@ -38,7 +38,7 @@ StdCapture::StdCapture() : m_oldStdOut(0), m_capturing(false) { // make stdout unbuffered so that we don't need to flush the stream - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, nullptr, _IONBF, 0); m_pipe[READ] = 0; m_pipe[WRITE] = 0; @@ -106,7 +106,7 @@ bool StdCapture::EndCapture() std::string StdCapture::GetChunk() { - if (!m_capturing) return std::string(); + if (!m_capturing) return {}; int bytesRead = 0; buf[0] = '\0'; @@ -120,7 +120,7 @@ std::string StdCapture::GetChunk() if (bytesRead > 0) { buf[bytesRead] = '\0'; } - return std::string(buf); + return {buf}; } std::string StdCapture::GetCapture() diff --git a/unittest/fortran/wrap_configuration.cpp b/unittest/fortran/wrap_configuration.cpp index 5fb744086e..08974d8a08 100644 --- a/unittest/fortran/wrap_configuration.cpp +++ b/unittest/fortran/wrap_configuration.cpp @@ -234,7 +234,7 @@ TEST_F(LAMMPS_configuration, style_count) { Info info(lmp); for (const auto &c : style_category) - EXPECT_EQ(f_lammps_style_count(c.c_str()), info.get_available_styles(c.c_str()).size()); + EXPECT_EQ(f_lammps_style_count(c.c_str()), info.get_available_styles(c).size()); }; TEST_F(LAMMPS_configuration, style_name) diff --git a/unittest/utils/test_lepton.cpp b/unittest/utils/test_lepton.cpp index 7b2c86f05f..a9fa6e3543 100644 --- a/unittest/utils/test_lepton.cpp +++ b/unittest/utils/test_lepton.cpp @@ -129,9 +129,9 @@ TEST(LeptonCustomFunction, zbl) */ class ExampleFunction : public Lepton::CustomFunction { - int getNumArguments() const { return 2; } - double evaluate(const double *arguments) const { return 2.0 * arguments[0] * arguments[1]; } - double evaluateDerivative(const double *arguments, const int *derivOrder) const + int getNumArguments() const override { return 2; } + double evaluate(const double *arguments) const override { return 2.0 * arguments[0] * arguments[1]; } + double evaluateDerivative(const double *arguments, const int *derivOrder) const override { if (derivOrder[0] == 1) { if (derivOrder[1] == 0) @@ -142,7 +142,7 @@ class ExampleFunction : public Lepton::CustomFunction { if (derivOrder[1] == 1 && derivOrder[0] == 0) return 2.0 * arguments[0]; return 0.0; } - Lepton::CustomFunction *clone() const { return new ExampleFunction(); } + Lepton::CustomFunction *clone() const override { return new ExampleFunction(); } }; /** From 3b1559470768a7edec5d2bd01129a229cbc2996d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 15:58:29 -0500 Subject: [PATCH 124/305] update citation comment --- src/REAXFF/fix_reaxff.cpp | 2 +- src/REAXFF/reaxff_allocate.cpp | 2 +- src/REAXFF/reaxff_control.cpp | 2 +- src/REAXFF/reaxff_ffield.cpp | 2 +- src/REAXFF/reaxff_forces.cpp | 2 +- src/REAXFF/reaxff_hydrogen_bonds.cpp | 2 +- src/REAXFF/reaxff_list.cpp | 2 +- src/REAXFF/reaxff_lookup.cpp | 2 +- src/REAXFF/reaxff_multi_body.cpp | 2 +- src/REAXFF/reaxff_nonbonded.cpp | 2 +- src/REAXFF/reaxff_reset_tools.cpp | 2 +- src/REAXFF/reaxff_tool_box.cpp | 2 +- src/REAXFF/reaxff_torsion_angles.cpp | 2 +- src/REAXFF/reaxff_valence_angles.cpp | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/REAXFF/fix_reaxff.cpp b/src/REAXFF/fix_reaxff.cpp index 06941cd8a0..bec16b5d04 100644 --- a/src/REAXFF/fix_reaxff.cpp +++ b/src/REAXFF/fix_reaxff.cpp @@ -19,7 +19,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. ------------------------------------------------------------------------- */ #include "fix_reaxff.h" diff --git a/src/REAXFF/reaxff_allocate.cpp b/src/REAXFF/reaxff_allocate.cpp index ce56668a01..06ebc20f30 100644 --- a/src/REAXFF/reaxff_allocate.cpp +++ b/src/REAXFF/reaxff_allocate.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_control.cpp b/src/REAXFF/reaxff_control.cpp index d914765f45..99e498b428 100644 --- a/src/REAXFF/reaxff_control.cpp +++ b/src/REAXFF/reaxff_control.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_ffield.cpp b/src/REAXFF/reaxff_ffield.cpp index d5761eb343..c22330cc16 100644 --- a/src/REAXFF/reaxff_ffield.cpp +++ b/src/REAXFF/reaxff_ffield.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38, 245-259 (2012). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_forces.cpp b/src/REAXFF/reaxff_forces.cpp index a4edfeee5c..274799c30c 100644 --- a/src/REAXFF/reaxff_forces.cpp +++ b/src/REAXFF/reaxff_forces.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_hydrogen_bonds.cpp b/src/REAXFF/reaxff_hydrogen_bonds.cpp index 6a56675f19..0389db7832 100644 --- a/src/REAXFF/reaxff_hydrogen_bonds.cpp +++ b/src/REAXFF/reaxff_hydrogen_bonds.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_list.cpp b/src/REAXFF/reaxff_list.cpp index 0ff0852a04..2989f717d6 100644 --- a/src/REAXFF/reaxff_list.cpp +++ b/src/REAXFF/reaxff_list.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_lookup.cpp b/src/REAXFF/reaxff_lookup.cpp index c0e7bf2c54..d9ee471caf 100644 --- a/src/REAXFF/reaxff_lookup.cpp +++ b/src/REAXFF/reaxff_lookup.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_multi_body.cpp b/src/REAXFF/reaxff_multi_body.cpp index 2390b54474..855d82623f 100644 --- a/src/REAXFF/reaxff_multi_body.cpp +++ b/src/REAXFF/reaxff_multi_body.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_nonbonded.cpp b/src/REAXFF/reaxff_nonbonded.cpp index 75cbd79b29..e0a8d092b2 100644 --- a/src/REAXFF/reaxff_nonbonded.cpp +++ b/src/REAXFF/reaxff_nonbonded.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_reset_tools.cpp b/src/REAXFF/reaxff_reset_tools.cpp index bebb2e2cfc..9de917e142 100644 --- a/src/REAXFF/reaxff_reset_tools.cpp +++ b/src/REAXFF/reaxff_reset_tools.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_tool_box.cpp b/src/REAXFF/reaxff_tool_box.cpp index 22ef299b41..aa6f831e95 100644 --- a/src/REAXFF/reaxff_tool_box.cpp +++ b/src/REAXFF/reaxff_tool_box.cpp @@ -10,7 +10,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_torsion_angles.cpp b/src/REAXFF/reaxff_torsion_angles.cpp index e9b6bc618d..29233a56dc 100644 --- a/src/REAXFF/reaxff_torsion_angles.cpp +++ b/src/REAXFF/reaxff_torsion_angles.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/REAXFF/reaxff_valence_angles.cpp b/src/REAXFF/reaxff_valence_angles.cpp index ac3e2dbd1e..b46f09d23a 100644 --- a/src/REAXFF/reaxff_valence_angles.cpp +++ b/src/REAXFF/reaxff_valence_angles.cpp @@ -11,7 +11,7 @@ Please cite the related publication: H. M. Aktulga, J. C. Fogarty, S. A. Pandit, A. Y. Grama, "Parallel Reactive Molecular Dynamics: Numerical Methods and - Algorithmic Techniques", Parallel Computing, in press. + Algorithmic Techniques", Parallel Computing, 38 (4-5), 245-259. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as From 2b31a2bed56841c3b4303ef1d3e5655a308587d2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 16:07:58 -0500 Subject: [PATCH 125/305] gracefully handle reaxff parameter files without hydrogen bond parameters --- src/REAXFF/reaxff_ffield.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/REAXFF/reaxff_ffield.cpp b/src/REAXFF/reaxff_ffield.cpp index c22330cc16..6ca8dc6256 100644 --- a/src/REAXFF/reaxff_ffield.cpp +++ b/src/REAXFF/reaxff_ffield.cpp @@ -30,6 +30,7 @@ #include "error.h" #include "memory.h" #include "text_file_reader.h" +#include "tokenizer.h" #include "utils.h" #include @@ -40,6 +41,8 @@ using LAMMPS_NS::utils::open_potential; using LAMMPS_NS::utils::getsyserror; using LAMMPS_NS::utils::uppercase; +using LAMMPS_NS::EOFException; +using LAMMPS_NS::ValueTokenizer; namespace ReaxFF { @@ -538,17 +541,20 @@ namespace ReaxFF { } } - // next line is number of hydrogen bond parameters - - values = reader.next_values(0); - n = values.next_int(); - ++lineno; + // next line is number of hydrogen bond parameters. that block may be missing for (i = 0; i < ntypes; ++i) for (j = 0; j < ntypes; ++j) for (k = 0; k < ntypes; ++k) hbp[i][j][k].r0_hb = -1.0; + auto thisline = reader.next_line(); + if (!thisline) throw EOFException("ReaxFF parameter file has no hydrogen bond parameters"); + + values = ValueTokenizer(thisline); + n = values.next_int(); + ++lineno; + for (i = 0; i < n; ++i) { values = reader.next_values(0); ++lineno; @@ -570,6 +576,8 @@ namespace ReaxFF { } memory->destroy(tor_flag); + } catch (EOFException &e) { + error->warning(FLERR, e.what()); } catch (std::exception &e) { error->one(FLERR,e.what()); } From f8495eaf759bdc975b878b1b947f05d986b25873 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 21 Dec 2023 23:59:35 -0500 Subject: [PATCH 126/305] make certain nbonds is initialized --- src/REAXFF/compute_reaxff_atom.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/REAXFF/compute_reaxff_atom.cpp b/src/REAXFF/compute_reaxff_atom.cpp index cc9109915c..1834de0b4b 100644 --- a/src/REAXFF/compute_reaxff_atom.cpp +++ b/src/REAXFF/compute_reaxff_atom.cpp @@ -44,6 +44,7 @@ ComputeReaxFFAtom::ComputeReaxFFAtom(LAMMPS *lmp, int narg, char **arg) : // initialize output nlocal = -1; + nbonds = 0; prev_nbonds = -1; size_peratom_cols = 3; From a16a1a768c090adba55f2a16437bfd489e4b3977 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 00:04:16 -0500 Subject: [PATCH 127/305] fix cut-n-paste bug and avoid uninitialized data access --- src/EXTRA-FIX/fix_nonaffine_displacement.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EXTRA-FIX/fix_nonaffine_displacement.cpp b/src/EXTRA-FIX/fix_nonaffine_displacement.cpp index ef5481601f..c1de50c41d 100644 --- a/src/EXTRA-FIX/fix_nonaffine_displacement.cpp +++ b/src/EXTRA-FIX/fix_nonaffine_displacement.cpp @@ -71,6 +71,7 @@ FixNonaffineDisplacement::FixNonaffineDisplacement(LAMMPS *lmp, int narg, char * nevery = utils::inumeric(FLERR, arg[3], false, lmp); if (nevery <= 0) error->all(FLERR,"Illegal nevery value {} in fix nonaffine/displacement", nevery); + reference_timestep = update_timestep = offset_timestep = -1; int iarg = 4; if (strcmp(arg[iarg], "integrated") == 0) { nad_style = INTEGRATED; @@ -99,7 +100,7 @@ FixNonaffineDisplacement::FixNonaffineDisplacement(LAMMPS *lmp, int narg, char * if (strcmp(arg[iarg], "fixed") == 0) { reference_style = FIXED; reference_timestep = utils::inumeric(FLERR, arg[iarg + 1], false, lmp); - if (update_timestep < 0) + if (reference_timestep < 0) error->all(FLERR, "Illegal reference timestep {} in fix nonaffine/displacement", arg[iarg + 1]); } else if (strcmp(arg[iarg], "update") == 0) { reference_style = UPDATE; From c4124c099584d4aca84d8a0ad140f0b732c08f5b Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 10:28:45 -0500 Subject: [PATCH 128/305] correct statement about Xeon Phi support --- doc/src/package.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/src/package.rst b/doc/src/package.rst index 63a7f095ad..ba86835694 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -344,12 +344,10 @@ specify additional flags for the runtime build. ---------- -The *intel* style invokes settings associated with the use of the -INTEL package. All of its settings, except the *omp* and *mode* -keywords, are ignored if LAMMPS was not built with Xeon Phi -co-processor support. All of its settings, including the *omp* and -*mode* keyword are applicable if LAMMPS was built with co-processor -support. +The *intel* style invokes settings associated with the use of the INTEL +package. The keywords *balance*, *ghost*, *tpc*, and *tptask* are +**only** applicable if LAMMPS was built with Xeon Phi co-processor +support and are otherwise ignored. The *Nphi* argument sets the number of co-processors per node. This can be set to any value, including 0, if LAMMPS was not From efa37fff7f3cc161b98df5743d24a403109cb4ee Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:31:31 -0500 Subject: [PATCH 129/305] remove cached copy of "layout" since this was not always initialized when used --- src/KOKKOS/grid3d_kokkos.cpp | 4 ++-- src/grid2d.cpp | 24 +++++++++--------------- src/grid2d.h | 1 - src/grid3d.cpp | 24 +++++++++--------------- src/grid3d.h | 1 - 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/KOKKOS/grid3d_kokkos.cpp b/src/KOKKOS/grid3d_kokkos.cpp index 9a82e0157d..7b97c417dd 100644 --- a/src/KOKKOS/grid3d_kokkos.cpp +++ b/src/KOKKOS/grid3d_kokkos.cpp @@ -640,7 +640,7 @@ void Grid3dKokkos::forward_comm(int caller, void *ptr, int which, in MPI_Datatype datatype) { if (caller == KSPACE) { - if (layout != Comm::LAYOUT_TILED) + if (comm->layout != Comm::LAYOUT_TILED) forward_comm_kspace_brick((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); else forward_comm_kspace_tiled((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); @@ -780,7 +780,7 @@ void Grid3dKokkos::reverse_comm(int caller, void *ptr, int which, in MPI_Datatype datatype) { if (caller == KSPACE) { - if (layout != Comm::LAYOUT_TILED) + if (comm->layout != Comm::LAYOUT_TILED) reverse_comm_kspace_brick((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); else reverse_comm_kspace_tiled((KSpace *) ptr,which,nper,k_buf1,k_buf2,datatype); diff --git a/src/grid2d.cpp b/src/grid2d.cpp index 74fd105ec0..d39b7b4a78 100644 --- a/src/grid2d.cpp +++ b/src/grid2d.cpp @@ -114,7 +114,7 @@ Grid2d::Grid2d(LAMMPS *lmp, MPI_Comm gcomm, int gnx, int gny, int ixlo, int ixhi // additional intialization // other constructor invokes this from setup_grid() - initialize(); + Grid2d::initialize(); } /* ---------------------------------------------------------------------- */ @@ -522,7 +522,7 @@ void Grid2d::ghost_grid() // also ensure no other procs use ghost cells beyond +y limit if (yextra) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (comm->myloc[1] == comm->procgrid[1]-1) inyhi = outyhi = ny - 1; } else { if (comm->mysplit[1][1] == 1.0) inyhi = outyhi = ny - 1; @@ -553,15 +553,13 @@ void Grid2d::ghost_grid() void Grid2d::extract_comm_info() { - layout = comm->layout; - // for non TILED layout: // proc xyz lohi = my 4 neighbor procs in this MPI_Comm // these proc IDs can be overridden by caller using set_proc_neighs() // xyz split = copy of 1d vectors in Comm // grid2proc = copy of 3d array in Comm - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { procxlo = comm->procneigh[0][0]; procxhi = comm->procneigh[0][1]; procylo = comm->procneigh[1][0]; @@ -585,7 +583,7 @@ void Grid2d::extract_comm_info() // RCBinfo.cut = this proc's inlo in that dim // Allgather creates the tree of dims and cuts - if (layout == Comm::LAYOUT_TILED) { + if (comm->layout == Comm::LAYOUT_TILED) { rcbinfo = (RCBinfo *) memory->smalloc(nprocs*sizeof(RCBinfo),"grid3d:rcbinfo"); RCBinfo rcbone; @@ -615,7 +613,7 @@ void Grid2d::extract_comm_info() void Grid2d::setup_comm(int &nbuf1, int &nbuf2) { - if (layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); + if (comm->layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); else setup_comm_tiled(nbuf1,nbuf2); } @@ -1039,7 +1037,7 @@ void Grid2d::setup_comm_tiled(int &nbuf1, int &nbuf2) int Grid2d::ghost_adjacent() { - if (layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); + if (comm->layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); return ghost_adjacent_tiled(); } @@ -1085,7 +1083,7 @@ int Grid2d::ghost_adjacent_tiled() void Grid2d::forward_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) forward_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1190,7 +1188,7 @@ forward_comm_tiled(T *ptr, int which, int nper, int nbyte, void Grid2d::reverse_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) reverse_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1314,10 +1312,6 @@ void Grid2d::setup_remap(Grid2d *old, int &nremap_buf1, int &nremap_buf2) deallocate_remap(); - // set layout to current Comm layout - - layout = comm->layout; - // overlaps of my old decomp owned box with all owned boxes in new decomp // noverlap_old = # of overlaps, including self // overlap_old = vector of overlap info in Overlap data struct @@ -1654,7 +1648,7 @@ int Grid2d::compute_overlap(int ghostflag, int *box, int *pbc, Overlap *&overlap // test obox against appropriate layout - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { // find comm->procgrid indices in each dim for box bounds diff --git a/src/grid2d.h b/src/grid2d.h index 43316baad8..8316f840be 100644 --- a/src/grid2d.h +++ b/src/grid2d.h @@ -55,7 +55,6 @@ class Grid2d : protected Pointers { protected: int me, nprocs; - int layout; // not TILED or TILED, same as Comm class MPI_Comm gridcomm; // communicator for this class // usually world, but MSM calls with subset diff --git a/src/grid3d.cpp b/src/grid3d.cpp index c6cff3f317..6ca8f7895c 100644 --- a/src/grid3d.cpp +++ b/src/grid3d.cpp @@ -123,7 +123,7 @@ Grid3d::Grid3d(LAMMPS *lmp, MPI_Comm gcomm, int gnx, int gny, int gnz, // additional intialization // other constructor invokes this from setup_grid() - initialize(); + Grid3d::initialize(); } /* ---------------------------------------------------------------------- */ @@ -577,7 +577,7 @@ void Grid3d::ghost_grid() // also ensure no other procs use ghost cells beyond +z limit if (zextra) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (comm->myloc[2] == comm->procgrid[2]-1) inzhi = outzhi = nz - 1; } else { if (comm->mysplit[2][1] == 1.0) inzhi = outzhi = nz - 1; @@ -613,15 +613,13 @@ void Grid3d::ghost_grid() void Grid3d::extract_comm_info() { - layout = comm->layout; - // for non TILED layout: // proc xyz lohi = my 6 neighbor procs in this MPI_Comm // these proc IDs can be overridden by caller using set_proc_neighs() // xyz split = copy of 1d vectors in Comm // grid2proc = copy of 3d array in Comm - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { procxlo = comm->procneigh[0][0]; procxhi = comm->procneigh[0][1]; procylo = comm->procneigh[1][0]; @@ -649,7 +647,7 @@ void Grid3d::extract_comm_info() // RCBinfo.cut = this proc's inlo in that dim // Allgather creates the tree of dims and cuts - if (layout == Comm::LAYOUT_TILED) { + if (comm->layout == Comm::LAYOUT_TILED) { rcbinfo = (RCBinfo *) memory->smalloc(nprocs*sizeof(RCBinfo),"grid3d:rcbinfo"); RCBinfo rcbone; @@ -680,7 +678,7 @@ void Grid3d::extract_comm_info() void Grid3d::setup_comm(int &nbuf1, int &nbuf2) { - if (layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); + if (comm->layout != Comm::LAYOUT_TILED) setup_comm_brick(nbuf1,nbuf2); else setup_comm_tiled(nbuf1,nbuf2); } @@ -1207,7 +1205,7 @@ void Grid3d::setup_comm_tiled(int &nbuf1, int &nbuf2) int Grid3d::ghost_adjacent() { - if (layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); + if (comm->layout != Comm::LAYOUT_TILED) return ghost_adjacent_brick(); return ghost_adjacent_tiled(); } @@ -1255,7 +1253,7 @@ int Grid3d::ghost_adjacent_tiled() void Grid3d::forward_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) forward_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1360,7 +1358,7 @@ forward_comm_tiled(T *ptr, int which, int nper, int nbyte, void Grid3d::reverse_comm(int caller, void *ptr, int which, int nper, int nbyte, void *buf1, void *buf2, MPI_Datatype datatype) { - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { if (caller == KSPACE) reverse_comm_brick((KSpace *) ptr,which,nper,nbyte, buf1,buf2,datatype); @@ -1484,10 +1482,6 @@ void Grid3d::setup_remap(Grid3d *old, int &nremap_buf1, int &nremap_buf2) deallocate_remap(); - // set layout to current Comm layout - - layout = comm->layout; - // overlaps of my old decomp owned box with all owned boxes in new decomp // noverlap_old = # of overlaps, including self // overlap_old = vector of overlap info in Overlap data struct @@ -1829,7 +1823,7 @@ int Grid3d::compute_overlap(int ghostflag, int *box, int *pbc, Overlap *&overlap return noverlap_list; } - if (layout != Comm::LAYOUT_TILED) { + if (comm->layout != Comm::LAYOUT_TILED) { // find comm->procgrid indices in each dim for box bounds diff --git a/src/grid3d.h b/src/grid3d.h index e4a8e276f5..6a15c2c942 100644 --- a/src/grid3d.h +++ b/src/grid3d.h @@ -57,7 +57,6 @@ class Grid3d : protected Pointers { protected: int me, nprocs; - int layout; // not TILED or TILED, same as Comm class MPI_Comm gridcomm; // communicator for this class // usually world, but MSM calls with subset From c964d8cda83b894cb51b787a9f831567daf4cb81 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 130/305] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From 5b00ad8f719b8956ee0152c0ae757895132e47b0 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 13:37:20 -0500 Subject: [PATCH 131/305] flag error if using INTEL package kspace styles with run style verlet/split --- src/INTEL/fix_intel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/INTEL/fix_intel.cpp b/src/INTEL/fix_intel.cpp index cb60149885..30d119dd6a 100644 --- a/src/INTEL/fix_intel.cpp +++ b/src/INTEL/fix_intel.cpp @@ -553,6 +553,9 @@ void FixIntel::kspace_init_check() if (intel_pair == 0) error->all(FLERR,"Intel styles for kspace require intel pair style."); + + if (utils::strmatch(update->integrate_style, "^verlet/split")) + error->all(FLERR,"Intel styles for kspace are not compatible with run_style verlet/split"); } /* ---------------------------------------------------------------------- */ From 7cfbe775326570d17c6974df6cda89e984fe5a5d Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 13:42:56 -0500 Subject: [PATCH 132/305] report incompatibility of verlet/split with INTEL package kspace styles --- doc/src/run_style.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/run_style.rst b/doc/src/run_style.rst index 0804ce5c82..d2e47c0884 100644 --- a/doc/src/run_style.rst +++ b/doc/src/run_style.rst @@ -329,7 +329,8 @@ Restrictions The *verlet/split* style can only be used if LAMMPS was built with the REPLICA package. Correspondingly the *respa/omp* style is available only if the OPENMP package was included. See the :doc:`Build package -` page for more info. +` page for more info. It is not compatible with +kspace styles from the INTEL package. Whenever using rRESPA, the user should experiment with trade-offs in speed and accuracy for their system, and verify that they are From f147df8b4475e8306296b341f5e7259ac2c90c0d Mon Sep 17 00:00:00 2001 From: oywg11 Date: Sat, 23 Dec 2023 08:29:15 +0800 Subject: [PATCH 133/305] add full list of atom pairs --- potentials/CBNOH.aip.water.2dm | 37 +++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm index 5efcc99fcc..dae5e2c5d4 100755 --- a/potentials/CBNOH.aip.water.2dm +++ b/potentials/CBNOH.aip.water.2dm @@ -3,24 +3,13 @@ # CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). # Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions # The parameters below are fitted against the PBE + MBD-NL (graphene/water) and SCAN (hBN/water) DFT reference data. + +# The parameters for bilayer graphene/graphene, graphene/hBN and hBN/hBN junctions are taken from +# CITATION: Ouyang, Mandelli, Urbakh, Hod, Nano Letters 18, 6009-6016 (2018). # # -------------------Repulsion Potential ------------------++++++++++++++ Vdw Potential ++++++++++++++++************ # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut # -# For graphene and hydrocarbons -C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 -H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 -C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 -H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 - -# For hBN -B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 -N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 -B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 -B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 -N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 -H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 - # For water-graphene C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 @@ -37,6 +26,26 @@ Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2 Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 +# For graphene and hydrocarbons +C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 +H H 3.974540 6.53799 1.080633 0.6700556 0.8333833 15.022371 0.7490632 2.767223 1.6159581E3 1.0 1.2 +C H 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 +H C 2.642950 12.91410 1.020257 0.9750012 25.340996 15.222927 0.8115998 3.887324 5.6874617E3 1.0 1.5 + +# For hBN +B B 3.143737 9.825139 1.936405 2.7848400 14.495957 15.199263 0.7834022 3.682950 49.498013E3 1.0 2.0 +N N 3.443196 7.084490 1.747349 2.9139991 46.508553 15.020370 0.8008370 3.551843 14.810151E3 1.0 2.0 +B N 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +B H 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 +N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.8044028 3.765728 24.669996E3 1.0 2.0 +H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 + +# For graphene-hBN +C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +C N 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 +B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +N C 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 + # # The AIPs for other elements are tunred off H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 From 0515f071727904702c8a940d88ef7ead41576a3e Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 02:51:21 -0500 Subject: [PATCH 134/305] whitespace --- doc/src/pair_aip_water_2dm.rst | 2 +- potentials/CBNOH.aip.water.2dm | 48 +++++++++++++++++----------------- potentials/TMD.ILP | 4 +-- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/src/pair_aip_water_2dm.rst b/doc/src/pair_aip_water_2dm.rst index f6c56eff6e..b84202e69e 100644 --- a/doc/src/pair_aip_water_2dm.rst +++ b/doc/src/pair_aip_water_2dm.rst @@ -31,7 +31,7 @@ Examples pair_coeff * * aip/water/2dm CBNOH.aip.water.2dm C Ow Hw pair_style hybrid/overlay aip/water/2dm 16.0 lj/cut/tip4p/long 3 4 1 1 0.1546 10 8.5 coul/shield 16.0 1 - pair_coeff 1*2 1*2 none + pair_coeff 1*2 1*2 none pair_coeff 3 3 lj/cut/tip4p/long 8.0313e-3 3.1589 # O-O pair_coeff 3 4 lj/cut/tip4p/long 0.0 0.0 # O-H pair_coeff 4 4 lj/cut/tip4p/long 0.0 0.0 # H-H diff --git a/potentials/CBNOH.aip.water.2dm b/potentials/CBNOH.aip.water.2dm index dae5e2c5d4..83205c354f 100755 --- a/potentials/CBNOH.aip.water.2dm +++ b/potentials/CBNOH.aip.water.2dm @@ -1,4 +1,4 @@ -# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# DATE: 2023-12-20 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com # CITATION: Z. Feng, ..., and W. Ouyang, J. Phys. Chem. C 127(18), 8704 (2023). # CITATION: Z. Feng, ..., and W. Ouyang, Langmuir 39(50), 18198-18207 (2023). # Anisotropic Potential (AIP) for water/graphene and water/hBN heterojunctions @@ -11,20 +11,20 @@ # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut # # For water-graphene -C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 -C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 -Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 -Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 +C Ow 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 2.0 +C Hw 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 2.0 +Ow C 5.453696 6.181724 1.250255 3.349092 0.687806 9.057065 1.232495 2.775772 100226.555031 1.0 1.2 +Hw C 2.553809 9.686644 1.964892 41.776171 -16.300128 9.015685 0.744155 2.415456 7409.128564 1.0 1.2 # For water-hBN -N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 -N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 -B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 -B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 -Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 -Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 -Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 -Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 +N Ow 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 2.0 +N Hw 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 2.0 +B Ow 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 2.0 +B Hw 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 2.0 +Ow N 3.530598 16.377816 1.285374 1.717537 1.339337 24.797794 0.771411 3.928357 33589.850651 1.0 1.2 +Hw N 4.029390 5.360546 0.950352 15.945549 -1.486701 10.797276 1.352684 2.293775 41247.181447 1.0 1.2 +Ow B 3.907514 7.842519 2.380078 32.122737 1.190485 17.482482 0.788174 2.368217 139539.370785 1.0 1.2 +Hw B 3.804966 2.356248 1.114761 9.193309 -5.922514 9.000572 1.334703 1.746122 43796.489158 1.0 1.2 # For graphene and hydrocarbons C C 3.205843 7.511126 1.235334 1.528338E-5 37.530428 15.499947 0.7954443 3.681440 25.714535E3 1.0 2.0 @@ -41,18 +41,18 @@ N B 3.295257 7.224311 2.872667 1.3715032 0.4347152 14.594578 0.804402 H B 2.718657 9.214551 3.273063 14.015714 14.760509 15.084752 0.7768383 3.640866 7.9642467E3 1.0 1.5 # For graphene-hBN -C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +C B 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 C N 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 -B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 +B C 3.303662 10.54415 2.926741 16.719972 0.3571734 15.305254 0.7001581 3.097327 30.162869E3 1.0 2.0 N C 3.253564 8.825921 1.059550 18.344740 21.913573 15.000000 0.7234983 3.013117 19.063095E3 1.0 2.0 -# # The AIPs for other elements are tunred off -H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +# The AIPs for other elements are turned off +H Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +H Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw H 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 -Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Ow Hw 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 +Hw Ow 5.453696 6.181724 1.250255 0.000000 0.000000 9.057065 1.232495 2.775772 0.000000 1.0 1.2 diff --git a/potentials/TMD.ILP b/potentials/TMD.ILP index e7a9cbe558..18563199c9 100644 --- a/potentials/TMD.ILP +++ b/potentials/TMD.ILP @@ -1,10 +1,10 @@ -# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com +# DATE: 2021-12-02 UNITS: metal CONTRIBUTOR: Wengen Ouyang w.g.ouyang@gmail.com # CITATION: W. Ouyang, et al., J. Chem. Theory Comput. 17, 7237 (2021). # CITATION: W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). # Interlayer Potential (ILP) for bilayer and bulk Group-VI Transition Metal Dichalcogenides. # The parameters below are fitted against the HSE + MBD-NL DFT reference data. # -# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* +# -------------------- Repulsion Potential -------------------++++++++++++++++ Vdw Potential ++++++++++++++++********* # beta(A) alpha delta(A) epsilon(meV) C(meV) d sR reff(A) C6(meV*A^6) S rcut Mo Mo 5.579450 9.377662 2.027222 144.151775 97.978570 89.437597 2.059031 5.122055 491850.316195 1.0 4.0 W W 5.530854 6.624992 1.983208 0.271792 140.174059 107.392585 1.356333 4.437591 691850.243962 1.0 4.0 From bdb0fc6b7a294b87569a977d413f09faec04600f Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:35:39 -0500 Subject: [PATCH 135/305] replace non-ASCII character --- doc/src/pair_ilp_tmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index b5d9ad60b5..575bafdc91 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -167,4 +167,4 @@ tap_flag = 1 .. _Jiang: -**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820–9830 (2023). +**(Jiang)** W. Jiang, et al., J. Phys. Chem. A, 127, 46, 9820-9830 (2023). From b01065b3986be4be244d45470a975008f6763ea2 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:57:58 -0500 Subject: [PATCH 136/305] use "neato" layout only for lammps-classes.dot to avoid issues with recent graphviz versions --- doc/graphviz/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/graphviz/Makefile b/doc/graphviz/Makefile index a3e0c94c63..00b651e888 100644 --- a/doc/graphviz/Makefile +++ b/doc/graphviz/Makefile @@ -16,8 +16,11 @@ clean: rm -f $(IMGSVG) $(IMGPDF) $(IMGPNG) *~ ifeq ($(HAS_DOT),YES) -$(IMGDIR)/%.png: %.dot +$(IMGDIR)/lammps-classes.png : lammps-classes.dot dot -Tpng -Kneato -o $@ $< + +$(IMGDIR)/%.png: %.dot + dot -Tpng -Kdot -o $@ $< endif ifeq ($(HAS_DOT),NO) From 5d9f38433879fc8c6cb79bc10febf1a94f531b18 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 11:58:40 -0500 Subject: [PATCH 137/305] recreate graphvis images with graphviz 8.1 --- doc/src/JPG/lammps-classes.png | Bin 293226 -> 311133 bytes doc/src/JPG/lammps-invoke-python.png | Bin 37244 -> 38861 bytes doc/src/JPG/pylammps-invoke-lammps.png | Bin 36936 -> 36838 bytes doc/src/JPG/python-invoke-lammps.png | Bin 28981 -> 28457 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/src/JPG/lammps-classes.png b/doc/src/JPG/lammps-classes.png index 71b5af9cccc92fa5594d34a032f5812efca037aa..d7ed506ab3c97e94af00e70769093da1be631d96 100644 GIT binary patch literal 311133 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
  • 678A?NMaTg-R> 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

    =OvmNb+I6y)o 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

    P6Z&oouMBQ;EAb#;G}a*XlN z-ZtizDnvW>Q{TILxB2hBn<~C_S5#Dlu4Mi*Sj^Dq{v#4^0EY?DKE*vUI{HZecoj_c z=ocUhzU4t-I^l

    E6A2d%v57v=SwbaAm?;qyai5LlJaD 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 z�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 $#JP*{*cq%vwLcm25Zs`L#&N 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

    g` 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

    7dv&CM*whQ=^g=|{f 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 H&!7GqHwD-U diff --git a/doc/src/JPG/lammps-invoke-python.png b/doc/src/JPG/lammps-invoke-python.png index d1e25f52ea1fd7944391009f31fe4b176e4812c4..890963a5778cefe8c99c9f2056fd5e5539380f6f 100644 GIT binary patch literal 38861 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쮞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 E4~TtAtN;K2 literal 37244 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 VY~7&?{~Y6TkCdc~C7-|fzW@rQ^Wp#i diff --git a/doc/src/JPG/python-invoke-lammps.png b/doc/src/JPG/python-invoke-lammps.png index 0c456028dbbed2d119f3afc432c6418c08294b44..6e44b8d56e13012c29c86e698912e6c449a65084 100644 GIT binary patch literal 28457 zcmb?@bzGHO*DdNXkW!EigHGuV1(Z;_yStC!N&7s4`KX*rp{Yrj)`1q0L?@il}#DgSN!uAo3 z3|1FIoxWx<8D=5BE$2*Qc(5^4va9iXv0vV(WPSAYsXOV#)!+8Tjt?Y>`e$B(WEZwG z=N>ajeV#JgTbGv%uOuWSP=)V3{rmua$uY0jma2U5pFdq$QG{Xt=bsoQo>BhmlFtv+ zzlloz=ZZ24|L@4x!M~VFVAKBlO+NR3qmcj4%Tg#M(*E-@;WyvJQU7yATN zBh#e+xl^~a&yv>GOlD?g98>5|TgY3+$B7ga24k&#;ga3oVPyZmz6sX%xH#gFkPvh4 zU%!4S30`+b&~x+hvg-as3GOp7F^LNFUK`4GZ%be^2^r^k6Up$$EH6O2HZ_$TgNQxZ zs4Fsvm@8y3Qzj}fQ<3iUbUBHT``weRtu1aozWL3~9{WClb}XTb7nYVA0uzb}suPtq zDi$VQUS1LFu8Nl9WfF+1wqRnF)%f#EuKN#2Nb-0`+S@aErSPv=|GgD+A8Ovs-sE%M z`X^Z?NwB58eZP~wQ1SG_aees4&hd=5{nf>(U0+;W9M0p%g*mtW?)M25vKyc5jl9jHxLNqk5TcTM*&F=0xQHrxMrjpp1}|hX=ps*+I75g;-DW%THfILo@A{ zdzA#mCcIO}#>P%gPj@f&`*K6-_d4m}UguX=)h^oty>b5?VY|QSn3#+&&koB>|J*#G z&d&Mb?~e-0sN0t)@Y&B#Mb&z&#Av8c=Q^deMq_ile08GoIh#puT2|JV7&eofL_W_m z_LP*AAQGN$Biio4Zij1nmX<-KrEChB(n{yMUe9ZSD6iz?<$s*v$S_HywRjS;8KZli zAGQAZ6NbmA{(yks_fk(>uE$leLHj!ghctus5F(+Vy}iAA=;(@w*_9P>Q_~fJ`G$+V zE(n2v8b^zpw{D@LqW){k-F0=J)Vm+IwYK`edlh+{?%C~r3lDcaM5x$0Ikk<9MBTV` zr@z8VBbLLW?K3LQXqBx2Zh_|E`iRebdngV8fp{PuBiUHW;~KBV?UC7LKU8$|mgeTv zP7x{gu{u|_4<9~MRabZPKim}^ZS)qpbLS3&X4QlD7{r{iii(PQt^xuARMPSHl8LmP zD}#d2l?yaGysyt^B-e+H@&aJtZ#=-k5qbUk&ikB^9t1*Zp6AAm8%iOCU8-}B%gqsU zzr)CL62*57Au&uwDdj5fu4X3P>~HXN7Zw&K;C`nY(f7Qzlz^DHD~w#MQ@}=FUszoH zOAhAy=M5)62KZj!Ki6J>XuF$iF;ejP^JiGftU=Y}({jJhpNs65r9;RBB%6IPVmK`D z=#}$iTj}ZO^jp3>+?Z)hl8WQBUHJX5!g{K+Bb-{T!Gq`gXmh?9OQ>$yG1vNXoY!fC zAS^6wF(e8x(%^Y+-tN`!aOpMkTu4X=Zp{>tgYaxco~fr#pR$_|iBoT4&&gFfZL0fX zJ(gY{&fneHdBVdJBjA3d=+;}>C-`Qn#&N9Lo?1>$ZZYwCt9E@@Lkoi&om z*^7$y(S4MSjSUp;cYoiv+4DNCrR|og;b^sC3ulFkN)APDY*-@i_3KlS_wHCD%xrAU z?d^Wi(F9i~^P%^O7BoI z3JMBxfpDUQA0y%46B2r0#rx7E!gGf1XtfQbZBXf#8uw&r)pDYc@;dy4kB8W1G43X4 zYHFfkVk%I+I1i5a{=J31uySmWu@$oM% zFV%|meblv_pBvh)4p3@)of^e**%a_b#>U=%At`yE&A2-m(g))E z1;G#rhLcscRkgK)X%aZT=!8wX^X<8g^*KLs(CjqpT*8(U-7%e=oi(+zAi@_~{BdyG zvRB+z_Rgj+RgP?Gva-ZCPvCrzjUKwki$9|@?;g(AFdHwU-EO=VbaQjFoTv!y?(UZC zbDa<7dLQ7Ys#)t4JXPz=W<6QuJ`C|3>*C^q@@{>YabRHJ{eYgQEH8_CxOTnUA6Rj7 zgi6F&k|EaFC7$rgv`yosZ)hl%w6rt||9&?c0Rcgg`?0n4RE^SVwLx3Zuj%PTtBDHR z*{0iw-N)V+4B+YfI3~7O>W&F|ER@)IbruNK!S>?BR@;4(qns5P^#~Q6 z*1o>b%kyLVy#sw-CZ;@W}Jv6kzHRPVW78UKUcXvKGIN-Kh_+l>i z=+>=UbMy1NySuZwg$??UkFgfU1*`!Ja2UhM3L;?n2x<3ExrGX(&CxP5h4kfvjj?d$ zeD#NfgdMO0odVab_&PgK1q}4`Z2Qg}3EzZ6(Yuvyu+h`qjbb`l^b`e-GEw+zE-o&w zlUYC5GPO%U2A_R=vM*a(TW_Q{OK$O#K{68-zNx3D2RMP1jSZ1lC@v@W(4;Ssl9%@^ z(LMtk8=Kp8k7Btufu3vRDF=sAe#6|He!jknXw5^vw4OQy@O@B6qs%G0QnMxfo+H85yakP&zVZ$Zpy znRTFvgM&l$Hu{CFEsKuVDJ4pEbv1)dgXZCUagGD7MSBQo4&N0S8Cl(aFJGD2pf6;+ zh;xBW(|m59Uqk)WVpbWF_b^0HW= zc0IS}Sy|3<*4s?!glKj%?8wN-(a}+6>q&0pF$=(@r>m1V8@DL@ldVAgR!VC3=xFKe za2+b2Xb#^w{ zool6_qEbF3T2(hTrh+g@)v9%hj*Ag-jh>`Q=g%R^l=e)Cch{RJCMA0B zxfWE252NXLJY-DFts!!uaw`A3wTmmXEkx9*#gTqEX8vQoyU4nuOslQ19P=MnjV|=}2b}$L#E2wV7|buKDk; zPY}L(+tc-noe2>2f}Usn>2L6-y{|pt#7XTh^_*^$_9+S-J=d-imz8}0)tQ=^`D?Qu zmI|$t(&YBJR>V;hDh5XD#6;Xv5npDjadx+(4YjQ|Xn$A!r2cdJHjBr}yYk0(k%td% z1+c_k6WVjdQw}mdCv*?YXv}1HZ%-eQqofq_v?cWk;4XTVg8oKt@A7%0 z8T?}+OiX5{4b{B-eE6_MSjar}@&Hb&@hrEE@{~YTXC`kle0(uOLkgjb9g*skX=Hf- zpt5kj-MG9lR(f%^UWiV_{-vypok}V;4KbOq)@D9cQ$9Xlck`S>>H%_v0;dqxGTe zPl17fZ6{ctii>TlN0zs?q+uz!owrD!6>=JT^Nr}beBNc3KphbrX{>1Vvo8Ry%A zn}7aHZW^H+>WIMR(1(lx=uw4(>;lk6+rWT)xtKTv8a;m{U|Faj_GbrXm?YdUMhdiU zoUG**62O5N9v)V*_AAhjIQ;QqRVxch(C&Pw(A>((Z|WqEA9R_Sne714Pyc?x8m+J* zPVzh=ME*QnLp0CKBmsC)KJjrAIz$3uZ0sgvJiyAjo^MvJjuw-noSmJ)8OMY~FL-@n zkJ$Zi>+amrl5cJ8xqwQ6MyWA|Mx}M@(9rjT0|#U{Y1q`a0%-X&NMEmmLq>+ooI5)^ ze@BZAa!~uBUFeQs^J{4l#bZ!SR{yOJRdHjqScK6D&^H`I2K?Hbt>w!C^Yau3`D3Vq zW)l^RLYD_PaI7Jz8TZDkS)(qNAn>-yh!z$XUx|y~gL zs&`M5yv}IFLP)Zm3LBXWl>DHMLFWNj@lW#0TMbud78O<#8R~dfFI#JBqZ&5sk9(_m zVq#+cxqG+N^V|tCtYR)!WVY#v?wDyXA9QnBZmN@0Q}iAfDg~OqhKD2b@)&}GgE=r` zDoab}_ZGWw+g!dvI4cGEO?LG5hQP`|t@Ziz>C?DpFc1K@9C3l3pB$}DEs{-DdX2G-44rl-`{SXT!OG{5L zHb>B%9dGwRE-rV&7#|YSYx?KW{a4jfM-$`YvGAc$fm%w(`(74)!GY#8teRhS51ZP#Y2J3!2}ShsYwF)uRE3l?L}@MDLFYgd=xV?^Is@CL^IbXCoUeIo-C}aKcHsA z_p%@n^b{&w^-^M=EU>hmNthu&{?WCrVZ73xGCDD_4>(;`R`w?>Cn2Y0^cvqA#b65g zxfH&4e^Cet2_cbW_xR9GXe?{He?~djo=JjOe77;m0*Lekz%K!(CBf0rk$R;Ve({#3 zmj?hR291g!Bw%!#WH;+iz5&VC1Tv(ksA!Syubbv01wArCmn|Wrd}VGJO8GNAJ;AWN zr4}PhOj;xXYdhD+1)CRii*nVuGfzW#xLBuNrZ51 zEI0E8(gX^raEk1AjkU>^mb9jN%`Lzhuz!yrmM>=j`#*AA8=8fl5OC}6hoR@6nYej) zY|kWZI@ z5@4(hkX2e`9j-StHjX47znwm{4XAjw{&@OUmV=z%IR_jONl8h?ea8?I9$|U;0Z%C= z4o*&Gcg>@xI!;kfVuJaHu*6E$Ke_yDHT$FK8~lb=9Cwlb7G=;^t8f4As6<7>r-P*`Ba ze|L8)ZeA+KaLssxudJ-x$6 zI8p<8h7A^#m&Zm{rtIu&$gxu+#^q3dn#aasDyQ87AxVFYiIH$~;{{Bj4|vD}@Mw-J zKk^bt0l?dQy>%B8IIN!2{pA>Tvv0#ml{K(fZBT;y;1EkmOJmN?&c2Q3ekdX$V%(Rg zwaAS%2U(8Ki2-sI^zUCGY=2*UM#x%+45f5EWf&Kzp&H*4b#1@K=dE2aPVMvlR8+(a zUzCQ1Mi;Ow)OE4>C7&6OeGK4LjNtSiK>QCxi}<3?LUTJ0*^B>p(mn^Z2$9mI8sQ%h zpntIPr=_LEG=4^*qGkINi5CJ89-=POdB(GHpd;P6au|_;}5NV`@P`vcD;wUo6fcd3nK?AR!?Umy~=)N%@MSr64E+wGGAB*O#chI*KvI z`#EqDz>9qQ`t>KIxjZ6FH7WstgsiMAbEy*iIWIuP+1B5`=WD8jLGrv&1cw&tOB-}| zh{Vv~U?pN-erT0om86`UDsnKz#KdZxw|k#AUdZbSI9KQacCehR3JVpwipJM=d6{2d z6Qp!BuBD|#Klubd8BXG7U*8faOAY5+e9&WwNJ<9esGR3lPS2%BX%DyR zbh9)J3@uYr3A73sMV4c%kcK}4Lr5+bqBxO!X+3^HJk{V?JAPpTjkhl*sU%=f$P}2e zH8kz8J28CDOb|wpFzV{-)f&C(&Es&SM$`un`wtHp4WJic>*H^Rb$WLGS}B}?^C?+? zNs?zI1n;|*{-@*6gUw>@KrhOmTJ!?&m65@h!^6WE7K1y`IhxG1_#Yz#wc2W;;^Rp; zEk~1gw`O2hP!t27@Y6t3qucqN_HB}20?-z_uncAlCVF})!*AH3=g5$XYlBZtbxeTo zfdt*<79%-U+A;C*-4K)6fRw70qR76l)y<5TdH??UltHWJ``51z%BxjaSXeY0Jk$`m zPSmi=1v-rdYo|An;E~H_I&bd>b?WP2{XaimS&o(X0NBqJ;4Qw4FG$-)djUw(ctU0CeN0&WcoKan_j1j_!k!!a1oO1H8-I9#Ri5~ z^!4jK2)IE2^xq{ZqnNbhTZ>9c@1m$Rh)E01~>hva+rW=Mw{XgS*N(dWgh6(CxQ%boj$}K-_f&ZUC?^ zAWAmZJ;P_@-i9F|U4v+*KKy$iF zVSFI_7u2fl(hpuw-F?-eW$?w&bf$Ti26MBkrw8G7^E#TvAcuV!5|$5i*h(tZ0Q}V} zxxZt&*0>i0L`2}ay1JS>y12S3)tss6>YlEj%>_OmsI-}}gRNko*;G<3F_iGxO?EIF z`E(b{4xkIb-YUoSNb@hR8oe%ec6YN6E?5!yjor&h-qO%5-o+xzdnc|1nP12IlH=jS zhpHMHKuvM0S6U;hL{~o3NjF;`v)#gPyOHkpb}`5HytK5>34G2a2JM(YVSUKXrnMR` z(;Lc?%XvkSL#%^hLctmDOuf@IZJn6p^b^3UStOGKYo)5diYWU}bP>orgTV+Vi7CbA0)k z{J)rV zTpzl@y33=9s+yY41$m?z^~D@m>o3%z(1Ie_PhgmT4h(z)cCR8MjshuN?G{psoPTx^ zJ~=KsPLUBST(+v z5^*D+(M-9$DkJXcl@YQN|MBC8c@5zl3y1wucdYft2iKAVsC564_x`U{!IF#We}4xe z2G8?syY{7g-nQ~MLM7hD1a~cMqW*7EeoTAkf9MwzZC3T$TT-TT+AB-oHdRL? zJ5zw?=63(vfZdlWkHF@KyKs&2c%nDX>VL(~PevNO?`Q=RIHgFd9wqgbFq)J}{^NUW zDjAo78ML~Yy?eUuA7k!#U^@8Vxq!9%uHEk8xIpanKjIxOU+TLm3+QksFj2xg|{d0 z&=)r!TIG6Nk~#iYF-`fE9WAOK&UWLARPTb~+h;tkK1#eJIgu<4jBDXxTJJS((euY! zbw*StBoyY%J2?rWJU_DdAS`D)X?v;hHB3SMXuJnqx|QNvga$|}@MQJs=%dQ-l*c%8BZEH7WZG&g2CQqEAS z@w&eAKeuM!$;Y`=h$dml>+tYBiq42+mzZY5tCmHLDqi(JJ-I1utQe%`gW0YE6(kK8 z{rXA^eK;d1E8{gE)>J2q9Z|T+97{D%5Ar)BO& zV}D0V8piDpEqhj%roM92{>c58PSTQ}f{PMmGpS6J85vJaJv%q1 zC>-nks;~c5$4T$+I2yJ4TReJ1cSEsx`OcY=U>(+9J|!9XH|6u!s}u1vn3@#iwQB<6 zH9?Mh8*dH>l~dz;$cd?nAX~8bXat(!tCj}OuHRK zb|jNi*>P6aZdTl=M$E3FhRID6%Rf1cH31xTVr(_n-USi)KByS$LsXnB;jb)Y?6%AU z)W?pa9_ybM)1z-Ly><_ELth>L{8TIMt4@|-*X$dQ%L%g*yW9%h_SFALEe>h9Dk9cX zy-IJGOYG3b&Yc-#Ds5WcOEcvpK9iD^{Chq_9>dqb_8h~s18yY9b38m>j43=eCKJiO z&@-@pnDW-T`y!ix>ePv8w?hp#QpklW@sr6M3k&PdiJlKz(-_O1!_3 zmya+HWzvse^e)J+`}nM>m+FBN=ZEyTzMc89%w2PRnZPzDQO$;Q$2zg#?*C?qyBqo4 zJ7&s$JVK;OR=OHTT*u1>kV^W0HsDQLV@9;mGSSYq3-#V)K7S?T6knBrc>NQLZn*wn zb2J+N%uJh-nS(r3i~3EJP@##%)r&RnQOMUne2p@uDLBt4ivLho+wShFVJrU2?Z9qZ zrvE2b>juH!bzxP*n_|y!6p2TtPb&|_5%>uw^P{+P;ia-Px{F7p$B5TjKpc2#*q=FD z25H}}MVU~KJnE#J?DW#La*6)eXHaUQQ!}tf_xDkkF2*^V*YF_C8vVkI3L@&&0#;0efJ>8*{l>9lZ< z&EL96Txnax+6*s6D;;oEY9y`tc{HzbLtIipXR6Rl#A4d5BH!F}(%zGQWulnn@g;$8 z$_KK+c87jfK*#4FcDNy#O}C(DHxN`y*LaLSRBYnrOXr@i%3| zz0>nzRnwdQEuzAyQ9_prCdHk2T2+si4F>6~!a0$En^ncecxS9I^JHU0K|Sf*h61p{ zzss5Mn*nmlmc|#wC0`EGxcBT?i*p7a45}-e_bnTz+tY6)J^cqGwmc=RDtciQZH1UX zS7Qd(M|;&1$4Kr*7Y$mj2n)ph=C=k*3jI2@0YidK7u?lu1=kA!H2UAn3DUDI(M_3j!^P>=cG)4!A)D#`k~NpJj4cS=7fsK*pR{@{IrkcX&oCq~=d%Mbq5w^5>+u}W40 zx!&z=y%ssHz&hXi_uYywiKFh`W81@KDTJ$-)lb0m}-`EqErz|MW z?z~1(9kKfSsgw^^dZ1v&yMOR`y|?~M5p6|C!DOBMt`~OA7X{6{wDv4#7G~BvFJ^vz zB}<)t1$SN~_3Jz1RCwYezm#Iywv2Mc<(jfBVOX~4XhcC1;$Pk@7nd|K$uDB6^;G#; zBWbvB!VJ>^8iyloU9FwZP^_uj<+^GvwpNB66A)x^(Q5C7KRA+7AI>k~O-hFp7I|s4 zu&`9NOm<3dM#uPIlv}O6ExQp@k}!k>_pRPT3}lPpcbpZ;*Mr@1bFaPTt5_HE0+ ztx!OI)STX+#50a=3%WaJuNOn`qaKCFL+_9J|Hgvty(?Nc@mR3G8c z@rpx~FO54~hngCHp6c?G-fUBdPHOJh6vDwHdMjazD$xEmKSkxqXOw&Q?$J1JfPRpc zsbWRG3q&B&>H-FaE>H{TJsRFBE8~IERd1pR9qrmkVWFGH$&Ma628JVEZdFw@uy&>l zg^F|%GBQZJ6i95rM$jLF_Vcs17ZaQ~U_GevIAxWPi1jl@dwM0{z$w-_UWQ3N?vWfh zG{jnwQF+cS_}DFewO)jolX+0danZq+wD$|=t6;C#NBhXF4lz$%Q-!(QDjj&T^w&FAshznnJ8hAw zgnmg3)XqbW3aj8$qrr1mLgAq=B?3|R_w8lz@E#C*VxvvCsgCmv)OG1L9ba^2P}1lkb;HVhNdm)`AbjaL(U3?6aCr z47Xlao8{PLl;@v#t~!h#USbU&#?$`pcs8d?bKmD?VPf;c&_dV!CAms#Eu>R#Q0igC zC)<`vRm@S|+Bp=H^=#7VCBb-2VRlIlQ2l{TUg_^uE6G zI9-gwZQDfT;-8(R*G~cEN5bonrRfe9i9ri44h|!b9RK;}pRdu;zx{EjQQq0x8vsKI z49hM!@8sn7`gWG#4)%ffo}Zol1^h^k9cnANq2Y7qSdKE|>ArRv8oGkjr7uJWKUFsA zX=#l`TvosRo{9?+)+uzW)<`~9bmJh-?aDyi6&J4}kXAKiF3(%nU0;!e|0|YD*J%+D zlS#j(-)CVdlxk_fi07-j4U2j}a>;}dd-`tm!0XwO%@a1pu%dolD0yc6-F^ldjpUqb z|BS76eD=8mS^A@af(>K`e>kEsv|RVlFxp1*I32arBZmn+~E5b&UX@55)G#c^^j$$&3W4iF2D*-3KZ`fyYZ4&&Wz( z8+I>`XGR>@T5c_DZc3S()3t{_7E6)s2W3>UtG%V=Yiz6}*zDMkFkh~Itp+=a-NnhS zAKshr$w|$l&5673lai9wTqY{gH8@GQ-@Rb;zgcCsC|SOS9qM}{L33pyg@qFj%VcZI z+t01|U^BCSP=WvZnJ7ZwyY0IUwPF+k;%K?9(b$RV4y;u5r&qY0N16koDVbuWm;j$r zF7~A+j+Nc{FY0lL42Fhw`3MPO`Wvp%A|n5AG3V`+-+y{F1=<^r-NHRvTU&>X(T5=7 zA#G)LsILjrQpp6cD8ot$5f9#9 z{2px-nqJTj(y;p0JY;0 z&EP{GHTSv26&YLqZ_#P?Qr3`maiQB+ifB&$jIUA}^7n5sy(+MjX@IMug;RiS2EaKpCTeeChR zvXTpE&Lyz%8H3*p{jp#?_~+yhZBRZ~41Yg>_1rl;r2MxHX&#tgurAp;IwF4Iy7Pky z>;L<=IQYD7fDP>)I9E^3&Y%y(#l_tP-j&sIlo_sW2aADH-yw)LD=VhhI5_`m_iN&S zPz8J7T}hQwdV3H=KvW6=OE5&iBB*6Z|5sKPO-V^fj#5;XnsM75hQuL}!;?S^Z`&iu zfVO!&U(-Tq_X?rM&cz*Gtgqt-@IDeL0!rsh#nh{4QEaYRKuoz*|4 zONLU{AxzvO8 zebJl`N^DAnVKFJU8P}X!TZaP;Mk;o+M)@Am?5o(v983hU6c~Wd*AM zRpA$b{@oLVzk`F`pomiYvQ#1BC``0eG=2-?ycy)}o)(lnJQ@7uM>uADCyu-!EvpLU zeL+Fro__nQjm9e9*7G(nPxH$4mlizUzv;wwnSEGSSuIxW!>|>U|Kis-6Y0o6#?Lef z$G8n90#G_+yJ|4?QhvFdVSW`|LoG4xA5y6puifh41@(R4X-xN|>TP`&LFK6sezq;0At5vw<{JSzZqYP~PFy zNNEmsQ>hq_N%Qwmwn7Z26_86;JSe#;g-cV;Ge!%)!$1>$p2Jz7T^|e9vB4re5j-Y_ z`Y|rYHAOIWBHg|@lb{vt92`)9hK-#q2_d$(Uf7sJ=I7(X0(O5;(wae?=;-Ro-I)a4 zcL#2Dety0g>;Xvm{rB&C-rnAL&$aWE_`rJtwJ16!qCnVEPwyqD$H?r|70H<6Lxlpq zIV6;VG~NtePY~M%z!VNTdKJh|Qn?CxB1N~5p;d4$Gh5Zs=$ zJujhdEXXIV+4`$9XylIj?Do9qUq1~R8pWIa1Fa!{E0gA4xR6YQXuedFe>*aaadMb? z!=vViV*RM)YkXoC(4Z&V51y;nj23&m`KR@|FRk+%j)9101tW>1og1c*m5|tLCqb*& z;n{H1gfWK|?0s(@?M=>`NMh$2bJzHKRkebo`mJ8rw<9t~wOwl)&c_Wc?ew8HnG1B# zvpf|816p#vNaiPW$4i3 z<;cCD5k7S5!#n5C3*<<*=dZBCxG;0R(O7K;oW9)LJx#-@8mnw=P!>_@PRXCSqdTRu zuPE{8XLXz(4^Cs$aEO4Jb|G3A*O1CUHJcMEr6@IN>sW@5y5&>R#r=ksaJ^q2Z==** zU!CW)+yZ1C0w#=Du%{}~_F5Fyw_Tj>gF_H0kmt;Uc`+Rn%N&X|X^1(N-HO~a`#gfanmXh7iuG^g3I#zjD0^$~O!myfrjTTT_KJ3i2liZG*7u<%r!aWw6gcsCM+Hh#;*WVCK688F3NZ z8RpIVIl@uJok(|*>%K7*xlG%~*`U*c@BIW!KL7wB3*$V0{ydi5OgNlMY7|VzP#yD? zr{-oA@S@()QH5n-hYp`6VbXi4n-Coxot=soToT!K41hOaYNZ+MvZM9xT!7Ob;o+4)Qc*6{&fg`u zd-rbnth+2P7E&99$^fmND7eHx-+cl32Ptt=$|G%pAX4HyeE0$E;}AMVVBd$~j6bC& zGGML*7pVY&ke5e>*FjO6#8A%-4_u%jbGv4FzN}E1Fbt_Y~ zTuAw@d^|N!b)4xEGyR$x^ltx<5Mz*VkqRBsQ$!}N(lUgj5Wx z&vwIk2>7)gL1F|bCjw}UezLo)N_AhWujKKzkwmy~3ao0OCid-}Cbab2w~_fx!zm(n zVZNvjg#R%ZbAdC7Ydd2MYn z$e>Tb@(%W;1n_Tzw?I3e6X{`s?a?zg?*#q7QvjckP(oFe0B$E0%r4)=1&rm{hWv7u z79t9;bWL~q3Pf_RpefRMse7EkKhhqaWs&_^=dE%3ZY~i{QotkpsIL(kzjHrn%qzCv zd}B|3aCpGX!F1_wqgUX{WUe$m`m>=d^@&_FNoHBM#{la-qBD4F^Vin`YxBjmOCc6} z`?5$;$&tB%)Kk7AnQ``w;8I6>-!ixGuh-i=j+JxzlmUErUK{JDtWh=zR8*_c%8qzZ zG}W=#FAXM?hbm2lFRTvz3o!B~Z&Z1V$ch57qLf-#SQvZP5sYcEoJunco`BB?2nip; z5DuO9GV3VP;50owjqE5;u&}U@&Q)l3{DXoPpmAITKUZ$i)xv@yz^iPn+KM$*SVlnK z?cLqzDk>^U#Nfs40eQE$v{WGw>EZ`rHG`;)3jjF`9EpP~X?3!i4LO|wjcz(P@ZToz zMtuGH^}NYYmE-Ych#>w@8PBJh5wOrXa6Qq|#TU zZy_2n$B*cZ3Dwz~*Vp8=IBxkG9UELy_1X8R-1Yht)3i!P(Wv?~y(1#VUoU;p?n=jc zJ*+F|HuKg|6%+UFPp*afD5;0(;a)RGI`5Jo^P;~8|ZlK-Uh4O z+i2z+P+L$ClJ8+~!(V+2EDYL6nSV!=Mk;MfI&yMSa=VT$f>k{QP6dXHz~LiVUd!!X zrHw6R&a^`C?5wORUD;DmQ~Tl4EAPN&+n(B;>b)NCH_CHIV(Y53n)-SH7%@o8%(UOt zKQ+5Ew|fkU+QD2q=^J)c{JS_r?GIP$t;d@6u5tKhoGNeL2V4nNieFz7?}cnE_IBOU zxba{#pVsaEZMFu!?#SjSkOIRMSp3xw&6O60PAO2-*_j$=)ZQo2+iTNJy+2cQFblPN z8TJh>G4}>RDCluN_N_}I86L8G2?ydB`>{FW^f{J@hM+nPOTP0>fj$A!#<<+QCj0*&K_OLg<10Mm`0K& zTiFuM6gV`*A=Ul@R0(-F1KNej2G2OiLCoNKd>urPLU*2Yec@*|n6V3v5j!v#BRz@} zm4)hcnTc)zz^d6o-C2T;QSu#owoaoUY%;u;Soy^%K)#ikMj^1wC`1qodT@j5$q=H3 zM9?D|T=Xf5bjU#l7)mmKV|cti5~`x(83D6egL!If)-w$~;BI>7j-~)zMn3;O#rSlf`p8o*aT71g(~kpZ*>w6)&p-Xi>k@~OasP^^(N-(q4V0wPP~2)8 ze+MSu?arC2l?2h*hDk$kY4qz z^DJU>rf8(`1LnhAg#{pGMdO)ApdSsS|jC_K7vcxM8u$Pq@<-VaBz=h|;yhIfAd{{0U;vxSbj<%gDheN*z|WYNLQ=&-!H)D0Y`CT~4fW=QNwt*J)JTUv z^5T+`CYW=9xQw93fBYC(maCmMiNFQMZZ-ZLl3Ge(p-#F5a^wXVVPrDv?d?_fT)Kkk zlf`cjpE?+U7gIe#Mp9BAB4cB`Jdnd;c=G%+9nyg*70a&IpZqfCX}J=I>9BS}*{pWw zk-l9KXX*z}My9q*`%U9J+l&IgRcsDg{xnpqod6nDD_f$l5m*<|IIaOj^%)aS;Ak2; z)$_48cl+EfGHvYniN|ujp{t0|_i9(SHX*2oe1G$dkIb!eU9fPlJRz@r5AOT6=NI7< zW2dUo#4OGpCcJLL^RFd2E3KFH&8%DeS40z6m)d%UEU~SP*jzivK3+{o<#OHxk)731 z(fvYwn3|I|{FxEUp`}H!u?l-v#f3fjFOWL3Q^)^QN08fgs4Xnv&ndQlY<`H^eZ5#k zr{+Q6A+Q<4TlXrWb9&kNv*e)ef4u;cZV43f#}*!$eX=wMs!j{Re(E+-Ig-zm6ct;* zL#b8|ge<4!6TVspk5%AvR|e7pdnXie{D+`?yTYXD4LHOme}8^JPA@vF^v@=W2!LLA z{Td<~w2U_}qYa~U6qFH|h0&}=_hGPN_>=XI*mJ2ghgEPUW`dFdw$YL%R%a^tP$KZ2 z0zvgrI3*eS5166G2GnM0)rA#T9S4^FmvCDVVPU_Z&i8`*66yH^9TG;b zy1`%l{jwYznU>mp;S11tB0Y%6(f~*cIh+l1Ll!k^$NAv?!=aXb2jlj@0qpGU&5fBP z8NsPAM>D)9Hwbo89+;(o(GUHJ3adxBxGAzUbee<;D&W!N0mm$iX0>#61;Ug*1c0oi zr6rkZ{jq!nemIpgHJMRwyRz3OUh2ZHXtu`<{#vSv-9FqsXgnxsh6~@mh2^?w$|`CW z=X*Jlf(f=%n-hd*ZiPvi&6P{re|R z0)Bi6c(e6Jm?=A3fj5!|QTDdS@Z8X7aQZufy^+XqfI&f_%Scn+_GQ=YmoM<}zj}sA z9UWd>0S)SQZ-68BON4fHD!(td$o34Yhk|3 z08$2wV}%S2DMAC6RiKRV>F6^>&dX*CCa}W;fs&_Hiw6$kt@>kasI)T+iR{9Y9l)=Q zL1PC?25jVgpeGNvJ!EwFkjNFrAX;u=@&AN5a@86Kvr?lrnRxDBSC{87OOa7m7Y{CQ zRv5X1S-+vMkB@nNXl0k!=jp?g|#vXyCGdF>nsIIQYC`KJ`tUR9v;|!{>68yz|BC zt(7J#m{5&?=P2wWF$MH3pfqOZ$RieP#A)10e4mHIvlBevydy^#V7i$ZxNoH~L-Ka( zt&|6w_P}2sZO`;UP5@hP*4di?`RoljENN?Fz!5M`gZ>_jwvDY7kP5&JTx7S{$+V)@ zZthY8>Ovn-VLTou%;4AOhlUhJv$ym>5t*74`GXTLMFrT&d7n$h}RU%vd%cGevU=pK^E)YD7kyebT?)S1=# z&R~ImA4)sY?Z17gm|y%n>w(rQ>}>0~zn1Ow^kjOi0l4s-jD4Umk@owrZ{O?&g>>Qe zuBQ|?EzF)1yhzZk0n;`Z@FeqIF1YBolr}bIcne;HTRbNxnN0^po zvlwQeb!T#W1f-v%toh-3rSwVek*!_nC7(ubsi+cq>H&C?AwyDt7iqFlZ%R zpBWN@Sx`^_ETDn8xulvJAq;#$3#K3|`)+U@6uj7YR2UUy2?&UN-r)Ic*EgMp^zEE zM9Iav&b2nMK-92-@-Cj}gNU~3q8p*5mJ4@XM(;SNKjYxq;mJ5-N-fC< zxSw99w^pTIWNlROBR$8?I*#c0NuA$-3l-9$! zid6WaUAqI5(BG1hW~Q{TEC0fDEIfiE*SjDq%QVHY9m2wRbs!x%FKISN6&xl-N=gdl z>?d&DgzToWf&PjRCGfN$4!Xxk2DPSdly81_ zn7P@HAHm~GKDm|2f8l$T8Np4s$rH6!BZ;YVp}s!ZJ1WybE3ulVacjqO$UX1U^>#ax zO+z}j7bdwX^MVlx13zz;NoCvQBncW?;MUe_KbWujP9gb4xqoo5d3w5`w)Uy@%Xx|% zuwl@H@CU} zPix;94(HakEwM!qC5T>vXhB37y`(77!x$1ZTGSzk-lG!{y_4uhiIUNWL>WYjFlrFJ z6J3nqTlU`1^XvV7yvOnW!*E;oDpx(vb1?`uJvexM2{-^KNeDyoGFHM(>iP!G}1M%=c57p2i z1$cnDB@7OS!x+LqPJfA*;F?-nU!ULEi3N>R2n1qO|0)PnKVB|?ZTD+oAsDpE0V@zB zuEol>U}`{p2`mx9R9`248GS4sJ@zyruPP$qIx#VEWlha6ok~<>BpWB^QsGW05(xo? zN)Xm112F&)`>uh~Ao#%@PR{9rQ#3FKc#aM40cgzvoF^o&%dN*oMfZDFV*ffOS{h#x z$kd$HFgkKB&qq8I6w%Ml_)MhFXR2Aa`JtFS6!0ygZTrcH=_1q@r|D zeVwING2lyQ^K9YKr9(Kjm7jN5>olz;NYJDEBquKT+iUqR8T>CZCxX8#nLBh_!@({b zSTMZiTF%cLm;?3icxHy4_>5W?i-AXahu_>jzCY>8^4O?wuj4o ztEYY%)+I)Mr$>JT8%0OMa3fDeG46N59*6ato;7JMCu@azHrHoR9J9^8P03jXAY>{Yj4 zU#?sV6@eJO9U!HUrx>GV&^*3GpMbmzl(^T|UIU;T2cX#9s+C6n@y8ij`io%|I%pKxi0kr3_2Jk{mtBjHf4!zS>Dr)HO8iGo zRXXmQV6THmj*A);`Ob>EBy1e}jNHb<=x~ntWJ3%L zXkUXW18_a`puhx3Yjg|i_2<^s zdO+)J0x?F27%Kipcj2RR^G88ok^3uHH==ZwA4M1r+j8hEG}FvEiag!$H1;Dk%$;FF z@m#T|BIJ+p#(5WRVeAt>2gq|8XBC<>4hVGpJ?(@;Lqm86porJ$4k+{>CBz3&aK&n} z0W|3^Lxkm+&eB)V!q^=(bP@t`4g3NENOQ4Ubp@WJ1#_gNq?f{T9~feyA|h zVyU~hN7detoxR08IE7Z5@35I0Nt0TYlkH!Pd`ydH^kxF>{PcsRteEknTDhLz(`&im zL2mC7q8{-sF(tiwcSoLc-<%qRf)a=_O@d#!Wjb)+= zusIV=*?F@#Dm)Q;pX7k?Go=sAY`ZCbo9D;-0HDSAdnBdxr$I9mD}l5Z2%X>^8P&zF z^u*9?dizYfn1-(MTz2JvNdtiwD0Gx?Q4vC+Pym%s2bRjs&8^N?3lvj;lFtbAz;X0Q ziBv@qNCuSdtZi=(fS^nfV8wto#z*u9NUz}@xh_Cs2-LrOKLE{^ry5(()EL z@E<_06~wuti(cS?m&$d(uh+msDC+7ojX5CGPLc!3lcxM($3*Rz$yjJfQ!zq-@Ym~g z79A2nig_VMeS7(sBa*O|KAS-=2K#hJjIjv`V@z03L!1EAmo>M#FfmwV1Jgy)TZ37- z5>~Hi_IBlrY$KdPqou0Wsow z8&&yDO3EMRZm2L`KcEKpeZ7fde&9PeA#rOyYw)H1BG){Ltj6%N4gK$9uwQ13U+{{m z6biRihVx>Jr^Ya2d+3=o8F5>xok5w3djZ=k_bH`C^!3gKsX0~AUIMwT7T57`%#WW* z9NaP&j-|+O(`B|#pLLOe=dHnPJI>5cQ z^Txi(W?wtF^7Jmfu-8rE$5dsT8lESQCxjpV%m#J0KY-wW$&UuG9(FOYS z{=s1EGtf4vuBidKejiO+{QAPLCL&VEVP-35wL z00$faa;{4zxz-v9kca~OTwQ(rSy2;&jMdd3f zPn>x6vl{@bf2qlmp3VU5)8!ihet2qnx*e(uG>?|Rc7UH60eAwJj|R~Y0SJjFozFSk zGcT@_PvLnRp1!aiWelxg3m0@;++WaoZ<6@IO~cXlZorgK)VXHfnmuhq_B*+$!HWcu zl8e0B7K7=EO7?J#xKe}Wi~h;{Tb#d*7v-;Si{MK`X1T|9nFx9v~< z@>+7)WPl)%WStoMZ3VR-R2)5n_3FR!3Dy+|A1Le7&fXlBXjqsvij)fTc)D*DDlL4J zNT_f3d23CTST`e>G3~jVt5%3jRy3{P9%w~ef-QiX2gsW_01~_m%wde*#F8^J)j+1$EW2afUE!%!^pV*8a z+4jI5RScINajxWWOSZ0Y^J-}4-nq9y=lr}YU53?vEh7Ux&Um3%x((Aa?wE^abei1u zUV@O_DTBk**&Ja8XS{e!KXJ$b$@*i$+lOs}TdMh!REF_98i$8D|r>oZ#H%Q>{->ucE$Ho8z(I7Vo3s zObtBUs@jJ`Xp@c12ITrPvjG^M7|wz>jW%G4u+wRATHW-V%WxJ;RYI#-5_|EdCXX&X zwT{=_XJl;5dACoigK1&_c>p$k;tQ)CbYRm?Px5fD1`KZnJ?L||H-~rm@^gIU8G9Td^*DlS z*W%;i9s+G*P(=bL4BJg{pt?-zt;-H^`t4djg;1>~-=T`ivUL(uY>CvFXpA}R$P%!b zG=Ebl@Une&Cyz%wrhT@#w;p>Ql~pQE@VsZ!v?*R-=ibUptKYlm!KZH6<-B(0r&u>`~%T zhoKEtsFe^9q9iMX7>S9IvWWp@#>^Rq-cg0R!JaT)P0o2pzmtFk@MOP4eC?Xd>y%a1 z|7rro;33zX+Xq!1+IKfpPP_!?W}B65ACg(@_!w-Yr+;%Hb37r;dnvCZSm#e|SzzNw zgJ;$G$HCi`2#HBjH#zk%@(t55G*;=0gKrw{--Ru4-bmXcjB*)1amSd0L51M{_+_H5 z@(lF;>h`$E=h4UVIzJzRv=}2(C)4c;?K$((<|AXpgB|uH(~c&WEIrX1`qL9t@H} zEth$Vsy;U*=lK}wr)H4%vCL^fK-;_F$YTBRr?`~lju}DID+TdjIYJFDUc}r%$W~3Y zW5gqV(UY57+67gm-3m&e`6X>G6-U_eu`yS@)DNb}o44b4)q3YE&kc=q9RE8fV-07M zYYVjHUtGUGyYW_CsH)P+bG*Ht{D$qwHWlKV8eOXmf!~?(hx?*m2RbuK$Wcnqkx&DwfNaPMNrJYs|Lcj0GiMV3d3HYmw>@K z1I$W{_eew{1u1y@bzVc#O`~R^nsr66E36d9?jx)1q3Ya&SNgLvF3tiBn7h7rFn#D* z_e8d`*xL+$n62!&L2if^l)h*$4B+qV9*Hj@FW-DW4xf{>;m;06+cEDfjmDlNGy&`V zHbFlSE$J5;H0m=pV(q-P$%gk6U&*#JXTQ&S{X#2J8#e30Dxs4XlLiwkW5zyBEthoL zby+k688Pw{Zf6?}-)ULQ?||y0a&Qg+3c>Y{4=H(vI3OwfbcW+k4HA{TR32tf@#44& zdy(NW3DS541Pul&s>HCaGUij8EJg@cNN2&HZC^)pI2kpx=$(Irb}UCk=?rWe4;Ivf z^YZp-YjHX}g&LQ*#l)q-653zH$dho0kRa}=4(ZdqcwsrYiQBL%EvevtY-$$R_OwZ~ zCNdPM#17Lp)Bvl9mm??EI8m=SQSvYQJ@}4Jt}=pOKu4pdX^zCV(+y){!L=+F^KNF| zs7}n~kQqHF@2@g|Avs{Zg>b$nu85?KG`yHI7%Sk+{D{c?OPbc{@=T$Ap4!4ar)I{> zkqdc6-A|${&vI{_yh|-ipDfOMym%fH5`dNaAd9R*G=J`S28Jx_hkqlczZk(&&{$nI zVX3NRnq&jhPu7OQDw5P;$;M-LPB!M68dj28{(nds8@;zYF_+}w)kyCV23|G>5W6R2 zl|4^CHaJyOjow>bn$)roRW-9w@Po&kuDA*I&HJ7lXN`)}&VC%1@^RTc&>9&%s|`r6 zZpjXT`UurIcUXLfb|@&RZTq* zK1H@_+Xa+G4xRwJ&vbE4Redk3s1WfcGm+$MCpzW(u>SpXaz4??$>ZIE``Ejj1QV9V zQd-WAQulCSpV#~iXN(70Sy+`vmr^roF+)RE--7u{`1xMWrnf@%GPSI1g$Qp4{Sd~( zBWt|8BS5@aQQQ4)LP|@nwm?N8B9m3#E8UqSr*m|IibUGFjU^!GN3M*!|B*OwXH(2g z0d$Y&!WHJ43T$?A++8s3%m$)8De%&5Glc+dehpGD z7suNfdSUa+FNT(?e4f+qqaWv;%un#jFo{tpXBcWJ;3<*4nxCr-dY10qpZz`vx9KI} zaGR_y^$Duk^RJvUtE=1ZXi_s6Ds8F-&$r=*E`F(SA=OQ+r)0jeTdp%dDg2_*v8;#UJ{VR9uSI z=O_VD?qj>FH>cz{7tE=@d2?!J<6lq16)Rnk`QgO030t8T%^>Gh7uY&Ei`-?XQtJ15 zJ7-1?VwGUNA?1nPA*jZY^WKkEkyo1zhl95Y6cv0g=t1!ks^?3}0acT^s$^zo+gmb? z*xM!B_CL9iQ-~}Ys}f*M@Z%&asIHh8%&q>Mn&aW$v*bFLNWJ*N!7&s>1CL(HharPA z8ja14VDI36Qb$gpBV!ozv8m{rA{Ppa=btcGdr~^-XkIl_Sf>Era!QtcJ+)VA{2PZG z8huqe3%!+VY9ml)r=QA*uWcz~v!C<*EItPF?>g7Jib(=hLiBjO#pnf*2K-M(6R4vD zbtmZt<=i4~phMo&L{ewD{H-0{0;Irad{0)KeOoxvkv`^E?hEPsKI|O^vZVZ;aB&E7 zt90|{=s3rzbk+Ktwyy`zO)YCaE>gFJIgev1H{MUi$4;A{j!CV+xm#{Nng>!G8>GRdp<%mznsNE|n?>^Q zs{Rmcj&&Pu(4M*Vd8+|cQ_#wXs&fA<^h-Su5$PnXC=L4QEUbL6O;=fMzGeDTv?VT$ z6ZqK}XOfFib7}i7xyxACpi4F*t=7iUuE&@pd#-|DGEyfv%r74l(|`V9$$+H(h+=4w z`#pspwfBudY_s)C^f9?L`iYi1Q_~1%mHo!L*J>8dsJ~@^k=$0laLcyYSl6)Yjy3M6 z@=Vv)-NUU&Q5g9*Vf)bmog*2I=V2}TsSXPUl5YQkv87UrX?=90Ad3>KZce6O1Bqrk zv@Apu<00%FFHj~{|EifB(6u;@;^i;fa0{l5sg{(Nc8zJkP#&qDID%%5*7w0oI>=CH zeW;Ud_hrU(9p`AWabmqQ%x8%`lu536j+JNRvfz z=~%_t)HFai$)eLx8<-6jJUJtV?%$PGZ`1cz95<6LMH?F9QAr4jN7^Osiq8uuD^g$< z7G@#yV|RVHlur567nK!Zq^8?vI;DX}|X z&b%z1U&lZW6*^vmq^hfL$V6f?_4iT*T?>x)O5k@2EtC2H2}uI0%!vMu{;!#x!)K9d zw%(+}zt1(B&!1k0VceKvG`qe(hz);47@uVNO(chft?5Hq|4!%D0}$v~{H0-7hPi z_GeCa4Y*qZ+#vV+uoYPG-dX7{4bcsOc13YxtW2G~(aIEQiBpz3!k=?w$y-pdlcrcC zL?#k4sIfGyjCQ#|$nqrpvU3_B3}}hod?TFPf{d+J#f%CzN=f;ijd+F~x&E5Uq5SP$ zwY=2HQ6K-juiE2C+oXV@#KpDp#RA_ja(fH>H8wVe^HcE~tUfMw&vc<;)+flxH0$eB z07Etuxu$cEC{hZ&XF5sQ3oCwbv8uuYJNZX~z6;m8Q4<`nC?eur3>!&qL)Na^FJ-r}GE z4U^;Pj|!ktPT{)5U`<)Gwqn=+LJ%|3`DX!Z=1@FXM@TiILj3@x(^E7xP<8Q|7|(Tk zPf1HZyg!^0qe5L=D8dq{I@Wl6MkGf^zq;Qa)`5NzQ);}U?2&Nu zpZjNqEEZ{!@LyOJqHcx0BYT$0@Ko2J0CKpEyvV-X1W9Vj)e9$_?m5jr%&H7a3v07u z2%q&^+mcHdO_^r&|JXQz>`zj0Gid;zM3 zPYC(GLl@&t>X#)DfHG+^h92)a!^#==k1N~Jk7@$QTDMam-`n4dd8ahQk48rAxnr#ncIBa*m)+GJS+Qh! zRKNp^*CnVb!;PV|;Ho;Yxgm1@d3E!5r=Ifq_RDV%9gn2A4WT-YJXPCA_SGJ|dDjCw zMy_5(Z8>{Xe@~UVPVv2t_KM%uiFbfZib&M;gW96lJa=hsX=u3*w-KxpCwWh81DQd$ zz8}u(Z)r5l($h!iHJ?NC-H~&d z5-h>MFJ!SFRWy&>lcYmMoVm`E2k zi#R$roMqO`&qX9FCin_HUA4{hk>C%UuxReVx~VQ^DJfmL8bG{>brF@yimg@HJIVZd z(m3(nt31&;r~>b7DdE*2PeOPCXF~Als~ria+4%MBlWV)X92!fHPI%h zI9eVzEFEPa@SXx;bzm0vJt~?X`Y3Lww4fO>r|r1rBGnAZ&PV{B)qpko^kg`sl`mY>yiGjg<}kZ7y~(Dh_3eCHOcQ|go zvP@T+f1Q{A`O%}C*@4x-8HuTM9jM_T7=!SqBB24swa=R#=NpSaxs-Pvuer1R zdu`Wh9007JoJY9FkKC3lO%-&{9+PT;$gCw>h!>ZXJMFRbg9%ysvQ}!)<@DBhVQ{P$ z(U4wlY`dcb9CU~>B&QG3&3D&&O1po|W81u9w6WwG2I9sPsk-t+WS8M6t})?1$AK-x zhU33if#t^h$BI0P5CcilrCS2qZ}PtqZU6sY_RH^JF0Ctg^Zj8m2;JEx^Xm#g2lMv! z1JIQN3XL8f;^0xD?2(D}7HRZr^A?ul)6+oYQF8@wy`rPlJUkwQZO-u@dm^ppBu%xx zo(LZ0oKab`+8Dn1-;YK?{xvggOd*>{opCuj@ZA6HVE^Ygn^0CQre|cdaBu)s<$s<$ zU(3z$pDWXbl!X7jYV9>>3d*Jt+zz3b2DSU0d;Bh5>{|xK; qkkY?X3MQTr=-~eI`xNkl3!9m7@nHfgF>v=Co{FNTLdByOZ~hjmw-E4k`Q1D4=UKFM zAN+MkTS8O_B#s4h8;8U?s+U1y}tE)@5d9uQS zy#DGedu==Mtyf>UxmIoyA)A4$cQ?uFMA;9frc&!exdhzS)JscCgy-*T9Ydv}IV=v> z5Jg2r8HjAWy1F{+)jm`*vUrwrg&gip{`!~lYURa zo58K_*}q<=N8{!ygWQOC@3#amk4J#SRc-BJaM}`#DDYVjgYXgoUE*Saxyu+T1kR>x!K70c=%|1 zR~` zL95QMx|+wl_7J(^Dr#y*^kJT!!rtAJ)6-;HT3W#&A*ux*Zn$0SqD@xYwY0T~<652W zE&360-p9tqhO;2!b)N*e$m3@9lHY_=vDl90-uyvD^l+4ho=XW~ZTD&;jmyG7f6}RZ<>@*xHct*r&_JW4S zXIRrIcg%%0u&{7fC!9?{s;IcQwYxii)r|Ks0|UcEtyB2sczJ)WI;Th=slS((sOR># zqt*0y%bDLd%qFW4bIYH0^%X3LhVwN3j*my59j=EHveMD{!2&+U#Z9d`Sz285OifK4 z(vFOboLybb+?}|*ytJ^hTizHgcD}wmj^VcPalJgt=vp zs;W9UJ^lFcChF(xnvASJ3!^U|D zCQ>tReMQA6C@Ol+#YG?^Bjdk~Y1%@wS}AaSv5yH?I5YDhj6tLA*GmPIkrG2}2*Sm0 z5BO*NIj3Ydr%R2M?_d!M!ATH%_b#L7G~0xZlaq5|VxnhoFjw}XCCJMQjg*u$wNCQ^ z3oC2(fz!cUlkdhUh+0LrhE*pE6of8B4~q9N9bE^HKV zJXrY?$LAEm=VU8oCp_>}EKA5=t=^UI)*Z|Q{q8u(RaTICyJC5imzeWwj|iKan{za( z!^X?ajd|-F_VtK~i49=oDmRT;mBf5RU!Y@Pw03md_DM@a@$m8rx0i%0pi-zU96FZ$ z?)+Ns!TEpg0P>^$zn{;tQgr*%q!yNz!W<-b*~U-zm%~p^U?ZQf8!39GofskCQlNNy zvO0ZCY09p7J-=T1_3PK3bH=P;vdio1zCl59PzVQ}MqW`FNos16B9l?Ja=wVNa`>BX zcXt0i5ZDu zeygWQR{|3g6Lyae&Y~YFAEAPR!XPu;dI_YCHq)@7x-Vb8G$(D%c}_unHBwd@FE#cm zFXzlw%6-nm6HTj}mk}`7+xv`(Nj5nb;#?_Lt?ADbtnV+_& zYHy>WJ|ZIW_4d98+c>iruB9YJ;aqUHIf~u*E+!@&r>#TDuA_3wbra+_)mle((E#FS zaDJNoiCA4P?ENE%I80{Br(JLPr1{|-&rVcYWnFV{a^`<7%2lKpEj7k>I@);l?AZ+~ zE32_G)ApXkw}Vs0g1ll3?<-zQN@DUk9X0m!{9NjcwmRBSwcVc5(EFH@r%~NkVWGpI zS@ZAe@_cZJR9INJ(COGLfS9YHEtIx>^BadwXvzs|HPo#vQ^+zoDurKV_)l-Yef!q& zXd{z@qn}Ol!|2IhyzuDgkbM9Cy`rZJgxoz$%!PkJv zK+ew}x7Z$Twc1DF=jXRFQaJxs14F4-o3ya7kWRhKub_Yl6ANo~qLLLtK`u8oyW_N{ z`PARn_it~nTy9{z%Mk%TKmYrR&tGH&E)`2%CMqlr)&?ac^fy*1$Pr1+km=FX)zzao z&F?3i$<;gTFIlE%L5jZzHws%SY-w3!bY5mUUQkewpwkk7yXD29Gs(!zEXR)f0dh^& z=>(MGEE=zPw=1sBD0ZC&k9#@lPWxq)W$g-@bkO;lqc&)y-=W+D@!`9Zz9>l{Fnu z&CSgLY$eP6K%Nb#FS~2m`H*H?A%(2K?Hdp0EspBPBmCtZ9ULA#eE8bcm0vWFbg}k$ zN}=g!Z*Q)+n?Kt!$ZM@;3U^ZgN5hbm>4W>4gwa5T9OP>j?Z&(E^6~?7WyZq+?(QgE zFHP( zPDs`cSEtL&Y;3oE7|?HIVLaA#*ze@Lp(r5k>jE_ulZ?Mc-qX2+VBK}OD^_Q;NEguD zK7hLHvg6f$uN$}S{QUK+(c>Pr!C)5dcI`1Kz^A7GYUNE8sg(5eC_|`Zng<6%0cHfp z$9Fe+V*@<$A9KaPXGoEW=U-S}W`$chJ6L@yEBjER%K8}{on)yNe9}GG27nnCaP1JB zcxY&7&3**mLPD^YyW`QGQc`|jj&llt?e9&KB7(^Cf}g-g>1~V@;^5$9XlvFvM?%mP zxn4R}S+6!gkaK!!fuET0|_5#{CM zGaN0VNSBG%gB*-W{HkC;2ll!Xu0XZYk^%`jV2j^oDi(eCb5pM)LN3$QfOZJrvSKpV z>gwwIYFjM8YI>bfP1*T%S!LTRy~&2dc_i?A6x7r|pvD*IwA{x*M~&sN(;LcB4W*e> z>pi->xQJl-c=N`M8<61pi}hs^4Cs|(xov*@`0-(~$|mb2H59{@wKZ)sGtDi*Nk9$R zZQ*vCW1+tIj77Tbc#v$;vsPX~?f&}pt3B*G!nYvNfWIm}o@~C^e;ybGp)eDvpUYAQ zf&U0DB6hes>5`L$MSH5o!NbF22TEu*u9hJjfLg~xc_LjaTifQ*QB@u03dk^!i~uKP zQn)W3?sf}YFHG7^BQ8&9&B~#aIiJjWU0m#U$h*PZ}i2~ue;;;c6WDYJ0e+cqoMUDzD1jxpC7HVq5bpcPw1L&nRYHDyo7|O2`kS> zfaiP$PzV9kACbE|A1JWbfWcT`G)M#Q2B#0pcqr%3zkffTz5ULl)9iaX0ePZOY8I|V&PShJI_&A~Uz6x;f$@%&I>2myjJ7Yb{nlMvRQql=jhn=IN zI|;ytkXz>tSk-cWNs7aCOemVe6lG;7m#EZeP#ZDp#m-7eg-?SxE4JU&cD*{)r<9ISncYw9+#k@`~xP(CLWDPrHWg*chXE`o{*m2 z;)6^3PC`NlP9~g&Fja>0kIgis8pv1+k>URcVHd+=m)E$eDa+Qm9&KJX3#iIux-J%p zMIk<*^sG+TyLlVCmh@g>FxDV}Z+kc+6>!(mOJorTc8AE59Imv|FRk}&Zxay}%`U^O z&udbS@Esi;?M;=yhh5|PJoCD;u(qa#Owa&o?1FF``T4LbwaU=CIvu*i2{q{*;8 zP)iB8EMJJH7B!eEQ-t~Y`i_+d@$vD!*U(_+_Y(CdYy$9-Th`gv7fdM?^%=6w<8y^_ zGmXIqt|$OV03kHzs+YgA-FULKwUyA&geZOI_uq#LlV$k;U*EAa*l6cQXP_~ApsT9zS#_e3Yj{zcj=i}vnrDh>|3Teog) zY;3Rr0dJ_ZSy!qo^c_Z>jK@(DL1@Ch2{1ASHZ}?YE&~xDthp{AAfU0aQ89BHcQqNl zR;yglE;pY}gDnCCXk1JV{xtIM&%n3PFH>`7OIG;?*?UKDEfJEo~ zXwzh*;LA6(2TGX&V?Pk1mU~Tp{{BeN09kJd@@$&uQ*z3{SAx>;OY zT>J%}L3!Vps0?}Qbsu6N<4m*OauFT!Dij3+zcrkEE?3#z<2PdYE`@M#ZwVWZxjz=A zgZ!+qg%wSf7Ce-Rdt6*GeN4OQ>~7o5h=V(_b9}51Kc1+v!BAFKzE8^gPDJFp5r=Qa z6aSPoX%WnD?0_L5tt%VCk#o8B66sSOe$Sh?ZaFN++8KQso(AX|zM5s$GZND3w+Jl3G zf9>t$5T5gCm8l3?1!^1|JUk0L4Gj(0H)=Y%Pf&bQuYT8D8x=nctUfuhM`B7? zRK>ydElz+DgFi%6#e4z+?kl88egx8syn%2n*UAP|Qc}H~J(GzFSm-B%D>njn7xCh7| zvF(3OON9qVFJG~GOsP-b6;Zce;^OLCdo*TD%w_43DiO}2-$jf^uhuwHs1v_(9x`|v zcEPeiu5RpDT-oR!D;_@d(C==pVLSy^kHmwS7AieiIE zw6+u~3Ojn|KJh=urfs$2s-&jPG!klRgrJV-Kxw6za_3-Xend@}=cZy}V)BH|;2|6o zWEIAxRr(dN@J24l?pkmLa$_%mEODw>bkCat0s>c8=jL5;d=l_3gQNm>TQNrQqKQTM zcDiTzveZbB1V?-xsI!rP^xpoy_m3acg;)|25?fQX*=6)pR9+D4gL4;qXDa~J;1(=C zf0mW{LszEXO*)jP(GDBa=1(CcRRza-f2nhRIo^$uiRmY-Xf|T3dV1#1H>3hoe|e-{ z8BcZ#?fwGfD+)@=w8q|vwjRBlA|X-HV$d8YW#Zn?8A$9Z?)o_E8vv(+bkYQ@V6{Iz zP|XIBAdbpQ$Zq5TIaW+e45Sm0dC@DNG!nM998k{z2jA7v(P?OC_}A7ZFY!BHvo`B6 zW$o(Nz+sE=sa#IQju50)nu)ZsZu3GYbaSh#t&jp&2ea`3y@hXk)BI#d%mr~lngmph z6g%*>oC_O0(0#W07dAE|6%~K2iFoQp@;Pw;S9)2TVkT_Js^kC{ox@CMb9LcRceX~G zlaoVwHrKN|-;yj#Z4Z#u!rGeda7q#qZ)0<_ysYeNOG_5mgzP=9V@UadiHXnRUC&i-1HHQYNu{lCC45XKR}nNPRfuGQy472VUMqAll}u?EBG zaV`xqItCj6L6eb_lk;Efye*x>gX}O5v^E7Km(;6q>^X~|u&`#p0q#kWSWTSg^sNB> zzl^=q%e zZT;(Wm;fjt2}p>60X6J~kttq%TF*VIp&W~H;$7s47)XO=KcLFdgUd#LfXL4C* zu`Lvoo@jU(u+f6DGD^6Skr5Sc>y_V7$p`R4!_sIrr_CFGJldG9kKeApt{X7&b-OyH zg3Cy<-5mS$=~GXh1`lKrAr+M;WX?M`l=I#{>Cyq7Qm6h+}+)2{2SEN)x%#M ziW`U`sjz8dzZrls2#Bm9^Is)*(oUv7SO|{*0YKhiDUB5hoWmI}GS``bOofHT#aU2r zKr{iQ4Zji`6(tUPV!s%wEPm;SfrX_DFcuOBHZHCw=x+eSoFVMsw|1wTwhj4)F1u?1 zw(8>DzWd;TK9FnBQ7q1nOyMXnv#>k|eHa7;WV&^`+K)${{9I8~2r#)34hEOS+-*>D z*vuv(0tK#~KxxwJifM&()Y{gT1jyQtm@62tC-##k{~$VB|NZ;j(gM7}IVU$4{xJjJ zD*+k|l;Hyu!bju7!{H!(2qjMgbn1Xo6B9!W*MJ~D1BLII%lQ#vQ&~mjACUW|Bw=3= z3Vx4{MuHZ*6Muc-B^E;YQBP0g?{}<7ZX2Ua)He{D;?A+8zrw;q;3S*Zol*k9m31P8 z(BY<%j+r^xosZ#l2u6OmwzeRM=b(dnczPlgy>ZL}) zj*i?Q8TC@sXCGD=RA@r&&&6cOQrPhK9XhDyOO|rAy;L5t3LfP4z6*AdU;+m7wyZ@0B`SS3=H=n zzt_~(4i1qZIFBw%P92p?u8*NIF9Sjb(mubs>J7yKa_Dn*c3f(;*Dh|S7oe&kV+Jay zrluxvK8x7~)Io$U6&2ON;UPP^BTjt$c~f)qW6;(-JyG-W@^GUBRS3nhNb}qjHm%_imDUf@xEeqc0K^3v1Olkk|dPmhk8 zMtf&Qak39`9Y}T(u(Eh~c=q=8zs^RJ{Z*KmnT13|vP_MkMS<=qIPWkO?+nT&lA!hV z^?kOqY-nv2vkAiV(UbtRz?r#H&E#X@s3AJ1uh0(eQMQHA6F^XJ0=Qoc4&FpMgc zkv&2C0X#aD-#~3{sDShk7Z-QE=8;wPskG?f_49l8?l~Wg>LV!tpl|@vG3cFt@!_`` z`xhZ3FHYtID=I2}0*0<~sL6m`@;JdK;4patk&(S>hHO11eW!n4QIJ->qZsu6wqN=G zsJZ$7!HW}=`$--qac}A3KMsjf?w@>^)VHRxrla>_xKwD-^ZWN9Yy3uqj^W|*v}##; zueaF7RTy*3^Yhwb^ca7=rUGDhyzxS!T8{QJkxgBiTyq79H6p%?2M)Ujqd>u>?B4J; z#T}%qwNEc5eUm-jlBg>=&b{mz4{A^OKRw$24{qcC-u<1pQ2tLJ_=lz6>f~KCB5chS zl|c1f!woJ6U!Cx|iiZ}}f-QE&s^N^9>%VJrcpd9%gJNoKmbM%oT0iN}epE{5Z=a~c zhvIrNPh7vjaQ?lSR_`DosG5G3`6cly?50-QU5|-(eQG(y!;dALw@~z&@x1F*{?`j| zR+3{LSnkX27VEo|9RDCBHag#DxT8IwThrx^iwnuu?v|gHj9z0(&DJJMl4hFE&bx$I z>^8m8W9wUD|F>`bOQ)&pVU0$P16+n#b9$lmxTiSD^fiGDI(ldX!FqpXB}TQ(grXBK zWBt$6afX>q?auvf`!6@Y^9{RCA88>Z6t{-+63GuWOi=CyO3ST>fE0c>VS|INS3efF zm8%iSUqKO{&voCu())&1eXn=Q09|v(gp^*Fz@}OxL6NqIOq?;|I8!X^(^>p~o9YjO zVngrIuk#exw>u+gPO zwER6U;HEnV7I6U+`-kCOBBOh_uiI*gU8h_N`%L+|{@1cF_iSx`xvTf5%FSaf3RIl5{~%5AT-*6B5LK>F#{dKRk z2nzP!V5(*kEPvg>EeV9tw#AG8^#eSR6L5P_M&kXHl`7sv|G6A>LVmqyWQ$N^%I6&u zoU*WHOzMxH)(7kSPj!{4QBge?%kn!_C<@D8ia#v2+868mcVkA)8Xbkvb}y~aE4P{1 zuD?hX_y4?m5^-nE1`{ju%ubUEN*PH2N8wr*8V85wOOlXTOYgc$#FK)HOHw+Ab1u!b4SxyKeVWq! ziV#8E|HcXLi_0;mqV*B1y=wt<>m#-2%;np1EnR-FSg#x?>IQW3MJTOSv@yHc^4^jO zJm5g{x+P35LHkV*!AvWtUN}Ka7txb{4<)VntmL39-E}f)&29R2oYMGle`w3yN(-;q zZuSINU^V;UJ5O1j`O|wnKQX;VSdwLYJxK`;`X3hw=}n!td{w16?PcWGem7r+7F=Gt z9h+;}{y}%w9e<1RWU?P$g9Me|#&pNFP_I4lhxoB*bRkL~tJSFEtFOv>+Y6gy*L@ju zDk>SD{{CT#yZ(%k-!avC>=v`QQ7ZJ_NS$f1Y3bs^%&t_fm@3lme|#Rh53Nn*ln<|) zuCYGo>teoM#$$-c?O)bZ(#&KQG<#t&XUS_y_^PKkmnSxgrJ}}Noj~Cv(%V|Cuf2j(mUF- z-+Nm%z_?pTWRXcXsWk8)pIhlc>ZK?M;U4yWF#-iQNHjFmj-JT%YdnGt6~e60f%WaAnOsivL6Bz*HS;+HE-dl}h)$F| zAex(cn}3zxDgR{k`4tz1pU2BkVO07)iC3I5&k3LaGS4gw;?yrwX&b3#r&T`(Ps|Ua zC)4H3N_yW94v!aRMl&8`)402&lehlsN`}O?6%e-Ad}%o;R(FqAYUWRY(XkvzFiF|T zAKH97dmR!w5)-ARqZmosrpq6QK)pce1MEkRy--Ghq%7T`Z zR17WqbH#@S!#&7V5^MeUXQ(eipK$n%vgn@*!PM1*A*fE4?)fDCVz(%ron<4 za*sKdL_E^d3$_(sq~}rcmSlRdBOO+8x5X(6XP3o%?o;uwk{F*?Rd71o;k)&Akil-= z%<}NWY;fB2JaE};0?SN;*C!y3-^~A_!1goh)7d56r2H7c>31;bY%cV`94b4~1I2k!tpy zHbWdZR#`?+IC7iByE}5_QESmcag&Sw!xmHZmn_*7i&a|sMc{Htt?ab(i#NT>TRYnl zG_&)lF)+B?p8mXLyv(wb>p3!r$j`A_w13#T%gM~dLczVkHguFXfzH?VN2@;R*f`#b z^XN`_azjDRW{3aGTjKY}xZN`9? zNFCKGigb?tC{&dj?Y+-f+Zv_HSgg~EDdJZ42nXGScyh0+n83Amm5h4G2@xwUbkl&o{crS?00Hd&WPu%v*iLsQCP&q(|v^43HE`rn~c zQq%JQ1-|?c9=B&F$EU<&1@6*+r*6z_|2|dW8PNUbr@M14oNIdSHy{2$>*=Lg&425( zJ)PL004ye~jy1tm9ZllR9!|FAY8$()=BXL8nk8owlxh95>`im^7T=g0C$=@F*QJv^ zjl@KvoLkM4bfqr2jdIimqb?O=X-nN$Z>M`AFx*U2p4Mlu1Q!*!`~4bEne4My2j|?#ud5T7zuCYJ33bg8Zv16i+mN&T~R}&P2KPMZ^8j7@;!pg7( zuojmopQ}&zJn@6WG#C=R(uv9H4I+yDYc%3S7UJvV*Y&$vyZaUg1|1{UX3r=uhnLxos4t|o zv+MWomH4aW<;7rIDpI~%?d9EPSLQRnzW{a0Fs*jIgwxblaO=*^$-&m@3RNJni!-}_=|~| z`PqL-Dwx?Mfn<4lroMN699SA>I*H6y05=!6>Eevk#IjrkIK#P>K+r4r(SA=<#mbTr zCGAcao5fm3gss);^obPz*cI{aDrZS=inw(f!j+FNdNx-e@k^}$H@Ye%WgICw&dS=V z!q0fokBMis#E;E1^Aa@?Uj+ZGCa|Ai*-MqKZcN!MEbH|rozub#Z+*6Gv6Q4Y&di-J zvdqA6mv{YqcUR>VNE3;Pi6D9txPz1w2c`yeGOcuJ65vQ^uR&3I<#d$O%nx12CxnD7 zAeV!?ftizY8B{UqYk?|_V1Ab~==*enGNMxS=^hx@)B+KGR%l=zXaU&)Y(9GvR^3RO zA;bHVbl%eRbShBBDJdvCq5Tp3>z7AlBmsE+B;)yGb8_fFe^Jn8p(ND;AlE@U90#@f z!1Xv#v~MY5${&`mY3T2xz!W|?3s^peCbH+Wj3n+Q8$2G3m${A`C%L96pQu=B2B{>S zmz?xA*A!AY^o}M(-mBe~2$WP9j|+VRh*Q_ z^9iuh@)WVkkDeU9K!_c#FMr!L82h5H8Iv^DUE9jKq0a90!^-XB{EWEt65sPK(#E7P z@+(G3N1^=h884wF*wEhY>xqFsWUVYjpslTqqDW2)&bm zPomZPif`+xfA@{X={9354oq^&I=4@~DM+;x#>o~| zMpcr#AFveX`P)Tr;rdW{*cXBQ5a~)n+M>V^iEOSwcLHR`P)A7+%RxD80!IuqYBQv= zZxxi5rh)?osmd-=3c__q!EZtnfr*7i%{VSvy`*b=YKqiTFp+taiIp`3`b9oGcAuI% zLmE5(eBH{ORCHq;w+Q+wsOvqhK|*IR-V@XyNP(YEWwDyJWNZq+){UHs*hI3mlXk|r z(Uf7jD%-KfJDp^ckUJ?q{o6PHm)K|QHxgts7nV2i5$7*)gwGuZ3mYaz-2XLN-`Dbc zMztMZ>WkPUclWP+wCPNeX=*Ac?s%VbUV)3uA{tGe<7)u{Gyq{0XYr(I)?))SI26#^5(8U{#C;lW8+@A6_)C5v%#aDiVx3282htl zZ)4i{Ta=omaF@xd{&TZ6&=eb%Tj|T5HDzwMYD_j( zb@X?r!*insv*B@I@$njCiY|Vqp(_~TsH)HV2c14u7J5Duyg*u7OvgFOOhyI4SYvBz z3!W+_lMyE9aw1)O$gUtQtuGi(`Z5)$5)2e|py_|GIj&4URbeszgjpN54b^&c3{iRs zwy0ESrqHRC+(qHx;c-1%rNU!){|`Je)YQ}oU{aHeWI+X^r$4_Ls8s z4-*RIb1v1(7F{6^$wfzb8}m({>~E;`8~0Na3SR3t=}D93x)4{8M*9HonIawY@g(Xl zqFD(xng*fZ#Hg>>Lzu$q*&Iij$XU%;KJ%^}i-TM1w9s^&a$?>8u(%Gt!%;g*bPdn1 z_&kI2n~Av*WjiPz<<_>V+YTOmP~jw-dNSg-6M@sEDbe_+`ImP$=QFBu_fgtFi#4O0 zDCCZ83x_wPDhX}Et!B$Jx3xRR->UB}XTIZCweXm@NVoDocpERHV>=)q>b}t46FogVFxf-5s*HWq z1V7tfUmeX6U`=m4qU0||QMe^>^j=kDVN$}+XrIfR|GapNzl!^;W95p6H4bAzQyQH| z&}~6d@!=#AQ#D*cK|xvkFc#5Q@8A9N0ApkFoT z%UG8H4mD)+<>(S#zA{zIgERwy4-uSW9`x#EGaDNn&~_v{J#`6IQBmmu4KaXB;QnuD zbiWf515lAT;N#eYG=@S*f$tU6>8!Hxk&&JxVGKyh-RQ?}qYJmv!MFlFMrJ0a8Kl`X z1^ok@-cNdZeb8IuzrL^nuOgH!G#hYQ$g3wUP5Q~`td?O+_{q>(IniV2Ouhf?eoEA} zGgedU(UQWH=!Si8cy)S@AJhpUiWJt_Y(BaP`>^XgzCSyo)fvX-0ku+cgA*8!%2lg2 zZ#tZMHcoebfcK-Dw0&dxt8KIkQv$r5E>$7LqeIyOHoF6@qGZzAjjGsVfA zN-XAc|Im**t>Gotk96AA$IsRq%P?6F-N!tz$z0aPdfm-oCh1bhb>1z+a#a7Ol&r+sdLy>h#@{#fX~``2SX68eOv^lR5UuyT&!g$_ zmswNiAFrtlE{}BPqEO3Oo@>6ap<8D*R@T~D#L*oPBk0AU!T8r9g`oW^7u$%Xwe=+`NAA9VihYa7 z2dn86l*>HE}g6$}2v=X@A4$)gyT97`E%q^Nh|D-h4BF;{^TEKTS=j`1ttN zHa3MNC5rxvxa$L%*f2$+4^CI4U3@v7F$z6fmin`e4F$|TfTb-b*SBs!ELWe#)H5Wb=F4d zr`E@i`65}D<1w5r&yQgsKx*h)Xfn9gk)QkPR}#ey@zh|jGW2CDGr}t?!8@7RLK7-( zyEV}Y22UvX4-ylFIR&=iFnK*7Tbio%(P$mkql4TCN6bCRqwUY18{ilMo89T<@$}$P zEf5JZw+m+Yh*U5t**iJGfJrA9$B;f}q%j<9(73Mm=UNLFWh>U|L&Yne1bHZv&_WsY z7N7{LaMeD$u zPJ)5fXkqoPkYk(xB}2YUA8C5R$uCJSRyB__k`(ggcV2U?)Hk^43ayVG9J+j?$6w0c zq93f^jV7)rlMaQQ>i-Zx*zro@P#Vi{3fEn7p;QL|X;Gm?YzRSs3j?NeEM(gJ01~XV$Wt&hD z!EWsf{b6XfW~jL-2194)AwIqr@Dgw>QGvS~dbZ%q><~W|CMtx~J5pvEy|xW4R&e$q zaIZ;!#l>}j^K7)nffX9Wkg$+h2#8+my-F9cNk`(K{j z92)Fx85)SJ`ARHb^q4)M-X(vw8JxLy*VD##TBVGe98$#Pkq) z>)^G5E?fTy=d)*Dzz_nXD~8i`d{D}kAgc7iv5s_iAUob*p+L^@0f?o!aK17Hr1ja_ zdZBVTE*U%_MuE6EI3ED~0P+GuqDm(7FL4SuD%t>%4z?zv!Dj@uMpJL`w--S+biw-; z7knvRy!iR^=WnFXbF`QWX<2|b3rSgfsVJ**cG=_OIq`0;>DnYUGo6IKW^=VC<-R$l zNptDSrzs7^U3P@a1+V>e=+KMw<a%2?=O@c1KAZsG>Q3e`>aNa=k!uZR0%WP) z+1sPBxb8E1r_L+YJurX+MI2cAOKR#5kf*6}D6UrwdJ=BP{>p%g><9TO2<+X3R?E^$ z-SOSvEf@v24h&`F3|Tp=ot~dtU7T3lC*e-nRY9iT$!boRX=7x~ya|^37r-!^pgnA7 zcTD3i&*!JI7|CHuSbwqSYqU0h36oRZzrWndL}U-WHP+&t5;;Dg*!hj<@|<3Je_|T8 zjhTpWal%8j)&UvX!Q7(2Xc~G;=K^VW_|yZl@sR(_S+G+J%AO?=6V=w@f?lOCc>Uw+GM?=^cNA!3P z!87$*EBsi?@LPCssFG+iDHS`nJZiz>+|9Us0|drM#_BW^m&zE^uymd3C`ZOzPH zGDi^jW&=YVk;%C>0Pl>AZu29s!_XpjFEGJu(t8G4UR}aUziUAZiMY6xa5<1(rc2)7x8Uy?_ys ztViFVy0L;PCK5pG9jDnlyofYlgRia~W-t}U88oUqVAu#uBNTQET5X_*d3fAbD>3*3 zA?5AkLt{}7nwNAuzZ9e`@%MIz2>?zW6A-k*kJ~5mpO+A1^TJ@}bEGK_l?%*31i&Hz z!ax-A9q`}Xd|5Xf68%v&EFlgEPN1jq ziYV;g7B(lBGxK+X{F2$xw5|!scYixZ-1VcmvF)h7HK`A@UzrP7#&T*JG`oqECWm=4 zzdSDDdi?mC{Jnk7nAvZSOivHz`7dl3qH-pj39AMhCMpaf#i2R5HWF!ZqHU6T{=|BY z5`;TR!VyePG>I-xDs4TJ*Wq{!Cg}I|yIa*!E*#eI%J4)a9SXUmYB!KrC?Mc#fK;q^ zee|E0!4|SBUxl|gZD?D9b}MIhkdD{XuN@neSQ0wwyr$|u<6OMdTN5Yh@SE(=TEg6z zb}YJcORQJ_45Nwc+MSqFZ1q=}nNMvMMO8=cK3R``L`c@5eExl2d(z!|6Mx2N)3;KN z9yCL3w-Lf4O^rV>bxsxqJHMgs{YYVr@ttbP3K0&Cb>6s)7;)vdp{w;4ul!o-{RZ{= z+5dV0ZZ)6jnN+@tKh)<>msOQr9Kk)59xmmbm#b_uKmy-^0Yw{yH>T}vZH^hr>ute5 zf4bM!x}*kFM*aK?j*17FqJq4O&6R zfxnL4f&>RS%>7PWd=6$8mtYp>FE|##h-YbO83E*QyJkON>xrb-x623{3m}1k>2e>Z%xA#c;kBaBqUPsq=88CFF;J2fl0u{fh!1*IyZ=QCgWvNxnKsfxwZm>t3_tN z)D192z6NPC6Xv$T_61ji{4}7b-hqMoz<3FdJF;Ge#@NWn1nq(18X4Uklv3HioqHEAS!wr55Y!uL-q8vwORkBx>>fw9D^~5qC?M;$Yd_(6ph!S{H9X~VN601>i}<>`hv%-i zf)Ebfh_LWRZIZSa8zHBN&Y6ggb-#{nNKNkxJ$B|+O6DkSZ!CZ2qpun!?n!+(H&Ab{ z)^?uXARs|Y!Tj-k`M9<67`g%-E3dj^tm5=LBA5|sS`2SO?cXEVGuk>j7QlR{4cmt_ z9>U;~q?8m{!(H~($BLoe|26Nyu>#0Fz{+>r_V3?QUfvi`a}g@lE8sbs@8q0*fwbrI z&zQ;Lf;S%K4xzz;gNxe>0y$cpD-3FaVxetn>K`4g?kRvAgz^Q4n2w2wA56Z1MGj$) z$_9h6NEiIhfPxJ2!359@QJ#&MhQY)Az00ka&eLFqWN@2CYa1; zLGeft3)wk5%(b-G^uks2ZfW_8o@JzaI-PoZ(?5N1c2b*|M^KrUDdFb7m;au!tmX_A z`V?U)QDA(OSIXg@*K#=SPc_$od5!{@e+5gtZ+*Q0_yMVL(ETs(xN$Z$H6b6U0K9q1 z30nXb$;!pOHq@)bJvq7?2d0L_(pUpvX|95f>-jt|Enp6ofJa6_=DW zwzsc;F$E6na5$(;i%fp-q{0XIeJREFsCGMCh~VpTuN#Y{PnusCN#YLT}&?Ti0wxLv$9yj?Vj-bn{oMpiM;aS;^MDhCLK9q4E9nA zK0Xo`mrL&R-;Ow$iZpGMGJ#+KgmDCid4EnMK!WK#9e`fk*J=!n$r%+D5ykr5MTY&9 zaBHododDRba^2o#X2DqDLp(g>Fn1TYjbR+@19Sln`Et|}^YiobjDP+7nQ*@V3iB5j z2*jq6{&l9DqzMmVC^VnuAIjJ2ZVjR8?eAA$oTUSSw%p{6sa&SwTK8{wW<+ins9cF~8&k(8K@hrLutW3t^CQHvzOPGW(QgLIa|*jX%$l^s5a1C7L3X5z2q zw&W$uxxxVPhd*CYkYREkBa=ti3D9iyo}~_hpsi z&s#i~Vv>?ML-p4n39Y~1&ZLBgOfa$i5?n4d1pB$kRE;X10^*UUAPjU1rOMCF&wm4p zD%fuKTz{w`-oJ#$W+ zRpCJpEv-2C9h;LkPNkoU^C@}L2pvwK)6e!TX|v2-#W|cAXK`!MZR;AYzYApU?z4D( z$np|hj;ujudvPSp{GHT~yzRsvj(R#eZ($TQ_08ZV%t3(0m8zVtS#%&a-~&V#=B8kW zl(%Q=w%0#;{t{2+_WfvDWj82o^CW6?uE!8liq{LStZ{z5P+W2MsYcZOL{TfP4SKZ% z#T5%4o`3@NFEQSyzQ1Bls_HJuGriM;DdWT`IkhAgcG;BjH9JAT$R<{VVtP$qd|g>x zt$cRT?i$3x$*F&`Gi(2VK&B=ry+hu+<-6!{qP;blxYNG+d)`8%YVwGTOly(xAq>X{ znjQ%xf-)@Ze72hYzywB5diwj5A+V5K9E`g#6KeEc>20EH50Da=1uiTt6;71>3GQ*2 zL4q0$L!!BT9@M&qP1`mAV*CX=Lkr8x^&wBKwZJn9UeM>xn%sh65K2h2FvFiT5t*>3 zInM&;0gS~K8zapAU;k9qFaoav4o?K-|Lkb99R?Rb^zm{u3GGxm1U-4E_}anIQR&Ps zX@lnrG?0OC{RUHfegKUe^_p3H_(R;x;(8l&&tTMVZu7OX>NiI4%c2C{)>Bd*J`Ulz z_Ki=|nCFUTTwgmHiDX~QO(fd2Bv92q?g=%{PJ^z@50DO!EjPgM1MO#@G0?0;39A976<=5s3-a`aRPSdlD@_#(BMH%r$Q$0 zuW_&f@F)G@!_2LG0$zJ2kgXI2^1-x?{H~G`9y}}r%jjitaWN~XV)l-XeT6zLmgB#U z31N~j7WBzSKr3KQO{K&jsQHlHLF<1-^;4iR_wL`I{l(#V;wZMbb+(Wm#cn6lL0uKT+*(>>VIT@do^k+BN@F0sEP z2`p<#$@JB^d)40SwD(zDRh|*VrQef6bo-pUVR;o2jcaRV; z2nYzU7;ZhCxa?7+##y5i04Utp(UDW`j*0+< z=x5O&I>fwW1k>!7V%)(&c#6c;VZK{XOiTxiePkdE>>)S9RJ9k(X(@4&JHVjRjMQ#R*y86x>k0ni2PKhX;js1Z7MYvgX&|y_1Tqs*9~ucl=DQQT_lI z`z^-U|JB=f$5Z{kf0Lv_$|zAJ3CG^DPKkt&ab(NL$jaVi6v_z6rpVqTd#~*5P1*a{ z$M(C<=llKM_xHd1{^!2`^KjPt{aV*`J+J5U`4--rv8mtGpc?UEj*x+!^cecoSh5AA z=d;+12UO=O96?#jLJKVsN*Rq{Ed;GZZm@r0Wn)`$eJU&{sOoDu`-pSA(o z1ChNvgz5O5vr_EY0GcSQ#2s6p{IHs$0hAUzsxk)@fG=ZeY6{Y+KCm`{N?1jTp8XZ5 zMs}9^qQ%^WAUc><(}SB)xrGdtrAQH2>Juk4+V5L^VW{IU18wySy`xlX^+kQU`Ma zWW>_!Cy|EC^H2Eg?OkzlC$8l7ovy?b6mY=|fV&Xji*>Ue(>?lA#i7MLNgnwm5Eg5xLsSHN*Y$8A#*Os+nFA6Gs{SEn$Ed)>xi|M+?Bg`Y(z)! zO#}Dbt|eY4!||7^>L@Qd(|dLlX(*6)x2)xx!+A5NuDw0Xeo65E$(`s$;uzRhbmjn9ztdL!8&DI z>KYnYPbM&R*nEyY;)KR@ZnfW?M*7y#(HKyPa#_R}8l4DZQ_2C)-1hU2pbo>6G1Eoe zhjd&O4NR0qmb}U&4>(rmFQa>^TpfiR*8{R$6$;WZeo z=~y=H5mYap)>@?*j6WgCp>j-??fX_KYEIAmU~{!}hZyFoRA8^yq^~#kWBJ+J^j&k} zgRs5elf?=ByM!0={<^$o66QDynavi|S+4NLNF946^gf*Mz(hpg_};&zH!*USzrN}z zNV%!0U1g3X9p@m9w&D{KPJg4dLGHD;Hdf}meIMcg#kyBCm}S89V3o622|1_%v9@fx zH};N>urGlR3D&j;2tBu8z`)}STB|R?O^b+%*B|OFtOy*OO}a%fam^v5s3kP$`XJ++ z1~DE83IRuauImYr8W0S~gHc!f;h4c%cz}HH9Jc)dS|zNpS5B2JWM9}xf&Llnj%B`d zQt@$A8gr@`a)TE)bcM3V;YxfQ0as&03HOiTz=s7-E7{lbrrPgnS3H%}l-}yLI0sssT|TALwGo}Wd%-0|l0Tf= zpp??HlwFq{EBhIYVC&2bad(DP9P2icJ>r9TZfs;!Xw*ryI|CXdfI7&q`aP(OJoMe_qYT*3;-aIafsq6Y>l?6=K=*1AR|E8@ z=9$7OeL#K~WWSiOrB%8ea0Ay5IG9(-$dc{PJ{-1pbld}^{9ko*c2_>uSp$^GWo`$B zs=5?dKNKjEehWIl1_p#x8Za-c45_S`s@-Uy4S@6TPoJKzCH08=m+$`m@NM?S+#7ZW zk_CNC{@!ZMf%!4l=!2cqVt0kydkKwog?7urOI^=;Pud8-aBo`ytnl(X{*{@Q z-uhsJ!^!0&$VU*^Ru-CHNGLlH*{<{8W&(=W2X6cMgB^<6 zEF7*{BOMyfMFJF?TU+|z_=wH6!In@-S(%xgJrz`;A}2e2d{mke66XQyOM{z%=u81P z3z!~cqSQbY=m$4vy|bv8V++8?E3j#T1ifPXtIWksr*ueW0wirOlp>o_|8zgk_+dt= z7W+B1FE2zHdEa|A6N40o!vn!v?oLF53;!rVn^*nz9)AO&> zmYr#;4ih+~VNQ-buNAGA{K}LRW17c1e#eyfSr|Ik+AbFmD!vHQuNN#oFtdDeUQtQO z+n{}KKhX0UqvM2GYyWq&8J*OFAA3B#Vbf71TiNm$WYNlvD`z_89fRYkmu$bNB2(Vj z`^JxU2BRm>17Z5pkcVK3rl(iMrQ>ZzC9))D1nWs$)Mn3<)i}*15mma^%Fuel+9hBq zEpRvj%k|4x7nB;HYQ*+-hT7a26%w#CUvTM(6tK3jQR2v90bujo^Epvj9AF_xeS5wE z2A-zhLx%XYPapxqVzAIytFNo`!s?mPiaea0wZQ1anr#B0lCuqm2&^G;M@L5(dhq8N zG|d+GKU6dxIl}?SBx+)XFv#2!YMaxX)ZV0d}~vq8sq-f=eBD=txS;WQl+40Ef(wjB|T)Stz6z( z`kLH2>m_r8DKbp6&i5}xZqNNk$vt<^t%#ixyl*xV*$zM!A*Z}hGpXkLcKkye3F(zS z8mq!i#-gObNQ_qwek5k*2YN{7@mpbdUYUpGUA=T7ZW^DdLph-0=w#16Xk>(CN+ADhq?s zU!x2Q>1JuA+e0}!JUpJGjdj|I;kRgm#@N%RPn~KbV%lDtN2G-MRp8IrTZ5SsEN?LM zLBfJ%xO3-g+Yl(zBcR`9gNI;}YxMy8IY>G|nJyHhd{NPJ**tZ5znD#pa;&$gjN`E; z^09a`3P#mLzvq^LJI@v0v-(VBE7K10wTmZTypu#yo!OE~#w4bBl1%j1+j6o^#P-d| z%icxrnDM>)Yhk2k{!*SN_NSYO5yz0F-^NTp1+J1hTbAqCz8~w{vkrOt*gh^#0JU34 zl<9wURLH@54d*B3XGFZH@P8?~1b0(U`zN2jP#$ zWyi$L-5r1!EWigsYg;Iwfcn!6PA!PFmhNs6INWx{QVKilFlL}!h6J}G5vhBdQw}(` zzzxp_SDjRa#9a2BYR@wdsAQ!;&u?U8G(0?v#m+pA)+{fD1GhD^uR#Gm0Y+u{dJPwX zUIMd+4Qoe-MVJ7A?(OS?a)h3hm58acb_o3Gz&;k+3DGk3e1(M!!Fm#m3jRQhMO_gA zZN4M`M39`or6j^PnRdPr1;BZk)fdj+Mj(qo{txZ6Vx1W*d#V0z+gud~B<@X9Q#CRM z6yl56x})?5r)q+ePXyy+ZO3maVIJdW#5up%pUxP$+>>OO%L|>P{`tZEFLK6w&Z{hx zuT|Z8`8+8OnB|0q<)~j1t9WSZvr;xJs-;*BE6GbOoals*%VX&x_dZWMM zs6{N~T`}~>uNRYVe8cpI#Ui(|jB#dAXU>ZAJ#iHUp!q={0N>o+bVYht+O4gv12OTJ ziHPp;^8VO*4~8Lyss&R3&%$u>agRa(3Ia0z^x~pFgd^xOY8Z?b7!Y8U#3u)sTy4~ z1$8|=IzVwWENDM(=2c=+l5n0~R6L@Q*{fOg<}D=bMOZoc$)LowG!AOMFuZCC`;0mz zcZn_(LZLz<5|bk8t3)y$1bmK8frhpiHu)J7??{8K_piMX`y zrB7F?n8G6?x-!;~q|T>T)iVO3)P{fm>WTPS^`x)7<&8Z~6e(?IXi&Rp%5y$!jcL0|{LpxT4tw|^o9ZMTvIG0ecNxi;*j1Y$kXdu(V3i**7+5r$CC*M_{hB`-x&ck%Pma@ zr$2(*wla}bl$6mX@jRUT_;)xeeqfKuP;%<7!;r+fa%rCT$1~bE$Mfz@@6Tf8hV=SO zCTyL84s!z*30sCY*J5{~cMt|T8s0Lf`iSrfLC7Wwl}MK`vm!ZWm$z+(&sG?B_FMy+ z*=b-_&8lwTj4YL?pKT(U5Wfr-J20lB<(?V_KP84s!!JCAdo2$!dim1O z8{M8b)1OJdBrR}p|3tRe(sZc4U0&8`LW+k8!TNGPTawpeA|i0sY47aNGyBNoLiml- zw+30k8fXj>+CbHhb}_}5=xE%54uoYJLI)Li;ec3$yC{nLUmh^stC8yDNSm7LL|))t zC!jYv9{lWhdJBPz!c&b(Z2y!t)m(s*A_{z#?`YBT>jAB@mddX>K3N3%>#Yf?&hP5f z93XhHwp*#Kj4&Q3QppS>eTa`3@Da}NUGCb&SmK+Ecli7*61&IDAE%A1BtTHOzVP&w zt)QX8MajVk8d+2>aviD%cCvDDm87T4G{l!iA-Y89h`*1_6KMXsv*D?pvk~NPm zcZ+V$hoIJ5e)XUGI?n1U=@=YQd<`vai-U8uSsI=Zz&z9A&Lrw?Z2S5sB>T+pezTD( zQDSkr>S6Ac68V!iEBy*9P6jtf*b|5A)~FQ)e=et6%TLulUnyomSNV&$;@}JgR}~*7 zZX90TO-$yv#bGh@E%FJq?Mm~oATJ}|BU@*K{xTmXa+w_KSoIP8mGQ8~#dgEg`!b_; zP<)Ahmi0#TyhM=;Myc`NWnl}5>-hYd0ChOWnvoUoBsdqxFlD?~C^A~ZrK7pJY_M$7 zgX0>R%ZJ#|y1=NDL>mmpzz=k0TX*O5YOXF^%%_fiiCvop?sxd3l7bLR+T&>|Fjb%f zyoVGZbjN!PGlQt~v{uB$cOF@!rqq9Fm}x)R-}{6<9l?&c1Mhj8r@O4IS7UmKi}W9n zcaj#VO27pc4wf7{=1F(md0QSC9#pgjZ}~!c@XFuJ4E5OcxZ@cD(mdTHZ8R6vf~BPH zh1wKtg`5@ryf`|f02xfm>e((CRfUrY#ZZpv6smZ)CeNDw_!eHG##3tawUl;RipyPV z>J8uQ6Y)mdX}4_)+tyYlG2#kSSLw-Yw`_};ezoUv=J)n-5SL%xDrD~H?zx=3Rmk&W zlsa1Dc~oQr(o+xsrWZcOY@k)NHZss_l${aoD+%Yhe*e9eLdVOAQ_i2XSsMIHqw5v# zk}0g7Z6--+5+rCA`hEQs6S-|VHvUC-^5!=Wk-rm2|0urbSYN78d98{6mPoaUvrd7iCwEPjBz+5K{_T ze0wr>zjN$U%$xX*&*!JUshMovdn=*aT~*-NTVDYPvblx_R&;KA|cfXIbs+HZ5hslPbL?^kaZ`i%+X6>fW? zUz%i}B&sAz=jl&;XRdg`KCSVK3zjupIpgf_`|{>T38xBO;Wt@~8Z<4O}IgWL*_iklmJVpHuVfD5klfFh={aakYYYWNuq#iUMAK78X86bmFQ__&xF*=TdeB2^Cu{8R&An!mDE0t1 z^-GY`^2wB7u{#5S$I)k4dr*X#^i~qj=pN;{JTR2B&rGd8l1(w)0&q#hC6IXc=0p~L zs6em3f1lRK=1^-^;7VWEr4xT^pRRKiwn-m~y|i;Y;!F5VbX%;|lf)S3+Y%0g>Y z3Yyz8{t$KG922F|^j#|J&O zZLTg?St-@Tm!%Hs%w9y!*&ix6SB-DKDPfNWc&x_nilqZ3^|I}Q!=Hm?h0#gt7Pn|L zrbwTgDj{gs6CAXkKL6JvT&TX!U-^P_FLll&zP*#n43wD<8-(}Eo1>DRYag=1Sey^1AC%`LbbJse#%!lCszp0Vol?@Y9 zFf#awwJ`mQi77bXdD1m`Sf2%zhJJ1xb*Dg2^(rHxguW9eyO#a!ZS+nerMOm*kv-w~ z4sSvKZEuwd!Ktvid*{~Bh0L?@qq$vj5%0RR^Zzo+NP2{5zaJT?tFnGDek=}`#L30| zXDp}gxNwCuYmzFsDu|iGQ(Z&YdM@~*nkHp(UYg-%OfA)Jyz(QQojV|gJI;SYdDPX+ zxlmMPXt>Zf(2#&R{VR8L{b$Fa9SOsngc)Jta@XOBo?G#Oi0b^4N|9o2~&Sy8RM6@lOXJT=Pb|c)j53g!G(A;i9 z8xOnA3#C4}aBLtUTwXlL{un@NwZk@?%CQH9b`9zd3FYrYy~Mf$&0D5#9Af`7xK<%< zB#%3>kgd1;iNEqxLg2J)^$w#rZRdH4SN9a(nLO`WJ$wg6&?jW(DbGCO<2dZ(VadW1^i=(@v>`GVRyAMV1;@NG&tRZBX7u)_V91J+@^HqkRXl)s$!ak;vA&s1`3K4=3;S~vgpB6lI-)2FRQ zt>t}S1c%I1oMhWDMK{v6B<-I(q`WzTe=lWjDC#CP zr)w+ChYzxL5G!o9J}w@$p*30>4Ek_P*7TLvSGz_wB!XJrNixI{D;P?UyeKcRe(Cs< z7q6Tb@6Ub{wZ_m{WcHYXR6#TzyImPW3QItytmio2D`*u04-^sphW{o_ z92GDr@f4eJ4MHBMgdh)G84K%~v@9Lg)PH5^uVgJy;8UP?pu62y_@gBzCY0c9kmS-c z)JP!e^^+#P`?%TY4nzdzSea-0M{vY$1*8^$`)mh20Ql@wP2P(wNqT?j3rI$WOhSvFf9GF7NDaty*_v4+4xv1?=QL+Y^ir_Q zbYA!n6DvZmuEY;aaSU2Np7b}pHXC)cKd*xzerpu6^fo;H!G%K^_!1{;w=@Esg`pA( zpYrmTM&{U8J{leV9v=0jJ-w*d{rtp!nMz9Bk~3F9HkIhL96g}{>oel$-`(*6Cld!o zW>;}MZ+)Cr+OcTHSTy;1tgFRP^$w4IE(@=CU3DF&LSB;WDtSxb-?b?C1Z+cCVKWn5 zp$hXmdNbcRlhCZOZsQWqk!%Cnpq)l{cm%7e{pci$gO&^>&5l|a8$vm3KAQ`$rxN_2n5TcgPW!C48zoJbAG{lQ1ghQ&G0DwCzav%k$?t5x6pK z9wl*=K2XmHNvsa5m6gT3$U<|&5q#cl*RafB+#V5wQKeBvsv^icD0_l?s>a_G@+Ie& z`hH3`yb>4mH~yyRz}mdq&q(wQA{5fYvX$ok9;SAz?;4K*o<1p$T7`nE2r$(d2_`a> zUs*}(=y5?5Io=tPJjXGm{nG@pUs;cM%JEKo79RR(E8`i@d!+R4q6xG^*WQ+XXFZ-A zNnOVT;L^Z;2*~nbc-O?xc+aSC_vyV;sk_igSy72lFJQnf#dddhMB$r4#wyWbc?=x1L`P6MXL^J>ZfAB)54wP(Z7d^3Tt!1%bShXgJ|Z1eXPXPV;H4xc Date: Sat, 23 Dec 2023 12:06:38 -0500 Subject: [PATCH 138/305] add graph to show relation between branches and versions of LAMMPS --- doc/graphviz/lammps-releases.dot | 34 +++++++++++++++++++++++++++++++ doc/src/JPG/lammps-releases.png | Bin 0 -> 69780 bytes doc/src/Manual_version.rst | 5 +++++ 3 files changed, 39 insertions(+) create mode 100644 doc/graphviz/lammps-releases.dot create mode 100644 doc/src/JPG/lammps-releases.png diff --git a/doc/graphviz/lammps-releases.dot b/doc/graphviz/lammps-releases.dot new file mode 100644 index 0000000000..f641cac029 --- /dev/null +++ b/doc/graphviz/lammps-releases.dot @@ -0,0 +1,34 @@ +// LAMMPS branches and releases +digraph releases { + rankdir="LR"; + github [shape="box" label="Pull Requests\non GitHub" height=0.75]; + github -> develop [label="Merge commits"]; + { + rank = "same"; + work [shape="none" label="Development branches:"] + develop [label="'develop' branch" height=0.75]; + maintenance [label="'maintenance' branch" height=0.75]; + }; + { + rank = "same"; + upload [shape="none" label="Release branches:"] + release [label="'release' branch" height=0.75]; + stable [label="'stable' branch" height=0.75]; + }; + develop -> release [label="Feature release\n(every 4-8 weeks)"]; + release -> stable [label="Stable release\n(once per year)"]; + stable -> maintenance [label="Reset on stable release" style="setlinewidth(2)"]; + develop -> maintenance [label="Backports of bugfixes" style="dashed"]; + maintenance -> stable [label="Updates to stable release"]; + { + rank = "same"; + tag [shape="none" label="Applied tags:"]; + patchtag [shape="box" label="patch_"]; + stabletag [shape="box" label="stable_"]; + updatetag [shape="box" label="stable__update"]; + }; + release -> patchtag [label="feature release" style="dotted"]; + stable -> stabletag [label="stable release" style="dotted"]; + stable -> updatetag [label="update release" style="dotted"]; +} + diff --git a/doc/src/JPG/lammps-releases.png b/doc/src/JPG/lammps-releases.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c317088f3c247cfc37bc27d1747cb0bc671d93 GIT binary patch literal 69780 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�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. Development ^^^^^^^^^^^ +.. figure:: JPG/lammps-releases.png + :figclass: align-center + + Relationships between main branches and tags in the LAMMPS git repository + Modifications of the LAMMPS source code (like bug fixes, code refactoring, updates to existing features, or addition of new features) are organized into pull requests. Pull requests will be merged into the From 9546fd89e52edce0d4f089e1c91554abcf1db0c5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Dec 2023 12:16:53 -0500 Subject: [PATCH 139/305] adjust heading levels --- doc/src/Manual_version.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/src/Manual_version.rst b/doc/src/Manual_version.rst index c8ac1ac7d9..1bfaffaf6d 100644 --- a/doc/src/Manual_version.rst +++ b/doc/src/Manual_version.rst @@ -34,13 +34,16 @@ first page of the :doc:`manual `. describe the version you have, which may be older than the online version. -Development -^^^^^^^^^^^ +LAMMPS releases, branches, and tags +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: JPG/lammps-releases.png :figclass: align-center - Relationships between main branches and tags in the LAMMPS git repository + Relations between releases, main branches, and tags in the LAMMPS git repository + +Development +""""""""""" Modifications of the LAMMPS source code (like bug fixes, code refactoring, updates to existing features, or addition of new features) @@ -49,7 +52,7 @@ are organized into pull requests. Pull requests will be merged into the and code review by the LAMMPS developers. Feature Releases -^^^^^^^^^^^^^^^^ +"""""""""""""""" When a sufficient number of new features and updates have accumulated *and* the LAMMPS version on the *develop* branch passes an extended set @@ -63,7 +66,7 @@ notes are `available on GitHub `_. Stable Releases -^^^^^^^^^^^^^^^ +""""""""""""""" About once a year, we release a *stable release* version of LAMMPS. This is done after a "stabilization period" where we apply only bug @@ -75,7 +78,7 @@ branches are updated and two tags are applied, a ``patch_1May2014`` format and a ``stable_1May2014`` format tag. Stable Release Updates -^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""" Between *stable releases*, we collect bug fixes and updates back-ported from the *develop* branch in a branch called *maintenance*. From the From ded160cd41653fb9a6d4835045d46279d1245124 Mon Sep 17 00:00:00 2001 From: jtclemm Date: Sun, 31 Dec 2023 10:49:59 -0700 Subject: [PATCH 140/305] Generalizing fix update/special/bonds for pair hybrid --- src/fix_update_special_bonds.cpp | 59 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/fix_update_special_bonds.cpp b/src/fix_update_special_bonds.cpp index 159b2a1170..4e8cba47ec 100644 --- a/src/fix_update_special_bonds.cpp +++ b/src/fix_update_special_bonds.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "force.h" #include "modify.h" +#include "neighbor.h" #include "neigh_list.h" #include "pair.h" @@ -72,9 +73,6 @@ void FixUpdateSpecialBonds::setup(int /*vflag*/) force->special_coul[3] != 1.0) error->all(FLERR, "Fix update/special/bonds requires special Coulomb weights = 1,1,1"); // Implies neighbor->special_flag = [X, 2, 1, 1] - - if (utils::strmatch(force->pair_style, "^hybrid")) - error->all(FLERR, "Cannot use fix update/special/bonds with hybrid pair styles"); } /* ---------------------------------------------------------------------- @@ -155,44 +153,47 @@ void FixUpdateSpecialBonds::pre_exchange() void FixUpdateSpecialBonds::pre_force(int /*vflag*/) { - int i1, i2, j, jj, jnum; + int ilist, nlist, i1, i2, j, jj, jnum; int *jlist, *numneigh, **firstneigh; tagint tag1, tag2; + NeighList *list; int nlocal = atom->nlocal; - tagint *tag = atom->tag; - NeighList *list = force->pair->list; // may need to be generalized for pair hybrid* - numneigh = list->numneigh; - firstneigh = list->firstneigh; // In theory could communicate a list of broken bonds to neighboring processors here // to remove restriction that users use Newton bond off - for (auto const &it : new_broken_pairs) { - tag1 = it.first; - tag2 = it.second; - i1 = atom->map(tag1); - i2 = atom->map(tag2); + for (int ilist = 0; ilist < neighbor->nlist; ilist ++) { + list = neighbor->lists[ilist]; + numneigh = list->numneigh; + firstneigh = list->firstneigh; - // Loop through atoms of owned atoms i j - if (i1 < nlocal) { - jlist = firstneigh[i1]; - jnum = numneigh[i1]; - for (jj = 0; jj < jnum; jj++) { - j = jlist[jj]; - j &= SPECIALMASK; // Clear special bond bits - if (tag[j] == tag2) jlist[jj] = j; + for (auto const &it : new_broken_pairs) { + tag1 = it.first; + tag2 = it.second; + i1 = atom->map(tag1); + i2 = atom->map(tag2); + + // Loop through atoms of owned atoms i j + if (i1 < nlocal) { + jlist = firstneigh[i1]; + jnum = numneigh[i1]; + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + j &= SPECIALMASK; // Clear special bond bits + if (tag[j] == tag2) jlist[jj] = j; + } } - } - if (i2 < nlocal) { - jlist = firstneigh[i2]; - jnum = numneigh[i2]; - for (jj = 0; jj < jnum; jj++) { - j = jlist[jj]; - j &= SPECIALMASK; // Clear special bond bits - if (tag[j] == tag1) jlist[jj] = j; + if (i2 < nlocal) { + jlist = firstneigh[i2]; + jnum = numneigh[i2]; + for (jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + j &= SPECIALMASK; // Clear special bond bits + if (tag[j] == tag1) jlist[jj] = j; + } } } } From 0562c3113879f38f8fc6db7afb88830ecb3ae10c Mon Sep 17 00:00:00 2001 From: Mitch Murphy Date: Tue, 2 Jan 2024 04:51:10 -0500 Subject: [PATCH 141/305] added pair/lj/charmmfsw/coul/long/kk and dihedral/charmmfsw/kk so that lammps scripts generated by charmm-gui.org can be run without tweaks --- src/KOKKOS/Install.sh | 4 + src/KOKKOS/dihedral_charmmfsw_kokkos.cpp | 991 ++++++++++++++++++ src/KOKKOS/dihedral_charmmfsw_kokkos.h | 267 +++++ .../pair_lj_charmmfsw_coul_long_kokkos.cpp | 941 +++++++++++++++++ .../pair_lj_charmmfsw_coul_long_kokkos.h | 230 ++++ 5 files changed, 2433 insertions(+) create mode 100644 src/KOKKOS/dihedral_charmmfsw_kokkos.cpp create mode 100644 src/KOKKOS/dihedral_charmmfsw_kokkos.h create mode 100644 src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp create mode 100644 src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh index af80420d7a..462c0cbe57 100755 --- a/src/KOKKOS/Install.sh +++ b/src/KOKKOS/Install.sh @@ -106,6 +106,8 @@ action compute_temp_kokkos.cpp action compute_temp_kokkos.h action dihedral_charmm_kokkos.cpp dihedral_charmm.cpp action dihedral_charmm_kokkos.h dihedral_charmm.h +action dihedral_charmmfsw_kokkos.cpp dihedral_charmmfsw.cpp +action dihedral_charmmfsw_kokkos.h dihedral_charmmfsw.h action dihedral_class2_kokkos.cpp dihedral_class2.cpp action dihedral_class2_kokkos.h dihedral_class2.h action dihedral_harmonic_kokkos.cpp dihedral_harmonic.cpp @@ -310,6 +312,8 @@ action pair_lj_charmm_coul_charmm_kokkos.cpp pair_lj_charmm_coul_charmm.cpp action pair_lj_charmm_coul_charmm_kokkos.h pair_lj_charmm_coul_charmm.h action pair_lj_charmm_coul_long_kokkos.cpp pair_lj_charmm_coul_long.cpp action pair_lj_charmm_coul_long_kokkos.h pair_lj_charmm_coul_long.h +action pair_lj_charmmfsw_coul_long_kokkos.cpp pair_lj_charmmfsw_coul_long.cpp +action pair_lj_charmmfsw_coul_long_kokkos.h pair_lj_charmmfsw_coul_long.h action pair_lj_class2_coul_cut_kokkos.cpp pair_lj_class2_coul_cut.cpp action pair_lj_class2_coul_cut_kokkos.h pair_lj_class2_coul_cut.h action pair_lj_class2_coul_long_kokkos.cpp pair_lj_class2_coul_long.cpp diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp new file mode 100644 index 0000000000..facb723580 --- /dev/null +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp @@ -0,0 +1,991 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + + Contributing authors: + + - Stan Moore (SNL) original DihedralCharmmfswKokkos + + - Mitch Murphy (alphataubio) - DihedralCharmmfswKokkos update (2023/12) + + Based on serial dihedral_charmmfsw.cpp lj-fsw sections (force-switched) + provided by Robert Meissner and Lucio Colombi Ciacchi of Bremen + University, Germany, with additional assistance from + Robert A. Latour, Clemson University. + +------------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------- + + *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** + + (1) first draft version of DihedralCharmmfswKokkos exactly + same as DihedralCharmmfswKokkos but with new class name + + method: track changes from serial kspace dihedral_charmm to + dihedral_charmmfsw and apply to DihedralCharmmfswKokkos + + % diff dihedral_charmm.cpp dihedral_charmmfsw.cpp + +------------------------------------------------------------------------- */ + +/* + 18c21 + < #include "dihedral_charmm.h" + --- + > #include "dihedral_charmmfsw.h" + + */ + +#include "dihedral_charmmfsw_kokkos.h" + +#include "atom_kokkos.h" +#include "atom_masks.h" +#include "error.h" +#include "force.h" +#include "kokkos.h" +#include "math_const.h" +#include "memory_kokkos.h" +#include "neighbor_kokkos.h" +#include "pair.h" + +#include + +using namespace LAMMPS_NS; +using namespace MathConst; + +#define TOLERANCE 0.05 + +/* ---------------------------------------------------------------------- */ + +/* + + 40c43 + < DihedralCharmm::DihedralCharmm(LAMMPS *_lmp) : Dihedral(_lmp) + --- + > DihedralCharmmfsw::DihedralCharmmfsw(LAMMPS *_lmp) : Dihedral(_lmp) + + */ + +template +DihedralCharmmfswKokkos::DihedralCharmmfswKokkos(LAMMPS *lmp) : DihedralCharmmfsw(lmp) +{ + atomKK = (AtomKokkos *) atom; + neighborKK = (NeighborKokkos *) neighbor; + execution_space = ExecutionSpaceFromDevice::space; + datamask_read = X_MASK | F_MASK | Q_MASK | ENERGY_MASK | VIRIAL_MASK | TYPE_MASK; + datamask_modify = F_MASK | ENERGY_MASK | VIRIAL_MASK; + + k_warning_flag = Kokkos::DualView("Dihedral:warning_flag"); + d_warning_flag = k_warning_flag.template view(); + h_warning_flag = k_warning_flag.h_view; + + centroidstressflag = CENTROID_NOTAVAIL; +} + +/* ---------------------------------------------------------------------- */ + +/* + + 48c51 + < DihedralCharmm::~DihedralCharmm() + --- + > DihedralCharmmfsw::~DihedralCharmmfsw() + + */ + +template +DihedralCharmmfswKokkos::~DihedralCharmmfswKokkos() +{ + if (!copymode) { + memoryKK->destroy_kokkos(k_eatom,eatom); + memoryKK->destroy_kokkos(k_vatom,vatom); + } +} + +/* ---------------------------------------------------------------------- */ + +/* + + 73c76 + < double delx, dely, delz, rsq, r2inv, r6inv; + --- + > double delx, dely, delz, rsq, r2inv, r6inv, r; + 255a259,264 + > // modifying coul and LJ force and energies to apply + > // force_shift and force_switch as in CHARMM pairwise + > // LJ interactions between 1-4 atoms should usually be + > // for r < cut_inner, so switching not applied + > + > r = sqrt(rsq); + 258c267 + < else + --- + > else if (dihedflag) + 259a269,270 + > else + > forcecoul = qqrd2e * q[i1] * q[i4] * (sqrt(r2inv) - r * cut_coulinv14 * cut_coulinv14); + 264,265c275,284 + < ecoul = weight[type] * forcecoul; + < evdwl = r6inv * (lj14_3[itype][jtype] * r6inv - lj14_4[itype][jtype]); + --- + > if (dihedflag) + > ecoul = weight[type] * forcecoul; + > else + > ecoul = weight[type] * qqrd2e * q[i1] * q[i4] * + > (sqrt(r2inv) + r * cut_coulinv14 * cut_coulinv14 - 2.0 * cut_coulinv14); + > evdwl14_12 = r6inv * lj14_3[itype][jtype] * r6inv - + > lj14_3[itype][jtype] * cut_lj_inner6inv * cut_lj6inv; + > evdwl14_6 = + > -lj14_4[itype][jtype] * r6inv + lj14_4[itype][jtype] * cut_lj_inner3inv * cut_lj3inv; + > evdwl = evdwl14_12 + evdwl14_6; + + */ + + +/* + + 63c66 + < void DihedralCharmm::compute(int eflag, int vflag) + --- + > void DihedralCharmmfsw::compute(int eflag, int vflag) + + */ + +template +void DihedralCharmmfswKokkos::compute(int eflag_in, int vflag_in) +{ + eflag = eflag_in; + vflag = vflag_in; + + if (lmp->kokkos->neighflag == FULL) + error->all(FLERR,"Dihedral_style charmm/kk requires half neighbor list"); + + ev_init(eflag,vflag,0); + + // ensure pair->ev_tally() will use 1-4 virial contribution + + if (weightflag && vflag_global == VIRIAL_FDOTR) + force->pair->vflag_either = force->pair->vflag_global = 1; + + // reallocate per-atom arrays if necessary + + if (eflag_atom) { + //if(k_eatom.extent(0)destroy_kokkos(k_eatom,eatom); + memoryKK->create_kokkos(k_eatom,eatom,maxeatom,"dihedral:eatom"); + d_eatom = k_eatom.template view(); + k_eatom_pair = Kokkos::DualView("dihedral:eatom_pair",maxeatom); + d_eatom_pair = k_eatom_pair.template view(); + //} + } + if (vflag_atom) { + //if(k_vatom.extent(0)destroy_kokkos(k_vatom,vatom); + memoryKK->create_kokkos(k_vatom,vatom,maxvatom,"dihedral:vatom"); + d_vatom = k_vatom.template view(); + k_vatom_pair = Kokkos::DualView("dihedral:vatom_pair",maxvatom); + d_vatom_pair = k_vatom_pair.template view(); + //} + } + + x = atomKK->k_x.view(); + f = atomKK->k_f.view(); + q = atomKK->k_q.view(); + atomtype = atomKK->k_type.view(); + neighborKK->k_dihedrallist.template sync(); + dihedrallist = neighborKK->k_dihedrallist.view(); + int ndihedrallist = neighborKK->ndihedrallist; + nlocal = atom->nlocal; + newton_bond = force->newton_bond; + qqrd2e = force->qqrd2e; + + h_warning_flag() = 0; + k_warning_flag.template modify(); + k_warning_flag.template sync(); + + copymode = 1; + + // loop over neighbors of my atoms + + EVM_FLOAT evm; + + if (evflag) { + if (newton_bond) { + Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); + } else { + Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); + } + } else { + if (newton_bond) { + Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); + } else { + Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); + } + } + + // error check + + k_warning_flag.template modify(); + k_warning_flag.template sync(); + if (h_warning_flag()) + error->warning(FLERR,"Dihedral problem"); + + if (eflag_global) { + energy += evm.emol; + force->pair->eng_vdwl += evm.evdwl; + force->pair->eng_coul += evm.ecoul; + } + if (vflag_global) { + virial[0] += evm.v[0]; + virial[1] += evm.v[1]; + virial[2] += evm.v[2]; + virial[3] += evm.v[3]; + virial[4] += evm.v[4]; + virial[5] += evm.v[5]; + + force->pair->virial[0] += evm.vp[0]; + force->pair->virial[1] += evm.vp[1]; + force->pair->virial[2] += evm.vp[2]; + force->pair->virial[3] += evm.vp[3]; + force->pair->virial[4] += evm.vp[4]; + force->pair->virial[5] += evm.vp[5]; + } + + // don't yet have dualviews for eatom and vatom in pair_kokkos, + // so need to manually copy these to pair style + + int n = nlocal; + if (newton_bond) n += atom->nghost; + + if (eflag_atom) { + k_eatom.template modify(); + k_eatom.template sync(); + + k_eatom_pair.template modify(); + k_eatom_pair.template sync(); + for (int i = 0; i < n; i++) + force->pair->eatom[i] += k_eatom_pair.h_view(i); + } + + if (vflag_atom) { + k_vatom.template modify(); + k_vatom.template sync(); + + k_vatom_pair.template modify(); + k_vatom_pair.template sync(); + for (int i = 0; i < n; i++) { + force->pair->vatom[i][0] += k_vatom_pair.h_view(i,0); + force->pair->vatom[i][1] += k_vatom_pair.h_view(i,1); + force->pair->vatom[i][2] += k_vatom_pair.h_view(i,2); + force->pair->vatom[i][3] += k_vatom_pair.h_view(i,3); + force->pair->vatom[i][4] += k_vatom_pair.h_view(i,4); + force->pair->vatom[i][5] += k_vatom_pair.h_view(i,5); + } + } + + copymode = 0; +} + +template +template +KOKKOS_INLINE_FUNCTION +void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute, const int &n, EVM_FLOAT& evm) const { + + // The f array is atomic + Kokkos::View::value,Kokkos::MemoryTraits > a_f = f; + + const int i1 = dihedrallist(n,0); + const int i2 = dihedrallist(n,1); + const int i3 = dihedrallist(n,2); + const int i4 = dihedrallist(n,3); + const int type = dihedrallist(n,4); + + // 1st bond + + const F_FLOAT vb1x = x(i1,0) - x(i2,0); + const F_FLOAT vb1y = x(i1,1) - x(i2,1); + const F_FLOAT vb1z = x(i1,2) - x(i2,2); + + // 2nd bond + + const F_FLOAT vb2x = x(i3,0) - x(i2,0); + const F_FLOAT vb2y = x(i3,1) - x(i2,1); + const F_FLOAT vb2z = x(i3,2) - x(i2,2); + + const F_FLOAT vb2xm = -vb2x; + const F_FLOAT vb2ym = -vb2y; + const F_FLOAT vb2zm = -vb2z; + + // 3rd bond + + const F_FLOAT vb3x = x(i4,0) - x(i3,0); + const F_FLOAT vb3y = x(i4,1) - x(i3,1); + const F_FLOAT vb3z = x(i4,2) - x(i3,2); + + const F_FLOAT ax = vb1y*vb2zm - vb1z*vb2ym; + const F_FLOAT ay = vb1z*vb2xm - vb1x*vb2zm; + const F_FLOAT az = vb1x*vb2ym - vb1y*vb2xm; + const F_FLOAT bx = vb3y*vb2zm - vb3z*vb2ym; + const F_FLOAT by = vb3z*vb2xm - vb3x*vb2zm; + const F_FLOAT bz = vb3x*vb2ym - vb3y*vb2xm; + + const F_FLOAT rasq = ax*ax + ay*ay + az*az; + const F_FLOAT rbsq = bx*bx + by*by + bz*bz; + const F_FLOAT rgsq = vb2xm*vb2xm + vb2ym*vb2ym + vb2zm*vb2zm; + const F_FLOAT rg = sqrt(rgsq); + + F_FLOAT rginv,ra2inv,rb2inv; + rginv = ra2inv = rb2inv = 0.0; + if (rg > 0) rginv = 1.0/rg; + if (rasq > 0) ra2inv = 1.0/rasq; + if (rbsq > 0) rb2inv = 1.0/rbsq; + const F_FLOAT rabinv = sqrt(ra2inv*rb2inv); + + F_FLOAT c = (ax*bx + ay*by + az*bz)*rabinv; + F_FLOAT s = rg*rabinv*(ax*vb3x + ay*vb3y + az*vb3z); + + // error check + + if ((c > 1.0 + TOLERANCE || c < (-1.0 - TOLERANCE)) && !d_warning_flag()) + d_warning_flag() = 1; + + if (c > 1.0) c = 1.0; + if (c < -1.0) c = -1.0; + + const int m = d_multiplicity[type]; + F_FLOAT p = 1.0; + F_FLOAT ddf1,df1; + ddf1 = df1 = 0.0; + + for (int i = 0; i < m; i++) { + ddf1 = p*c - df1*s; + df1 = p*s + df1*c; + p = ddf1; + } + + p = p*d_cos_shift[type] + df1*d_sin_shift[type]; + df1 = df1*d_cos_shift[type] - ddf1*d_sin_shift[type]; + df1 *= -m; + p += 1.0; + + if (m == 0) { + p = 1.0 + d_cos_shift[type]; + df1 = 0.0; + } + + E_FLOAT edihedral = 0.0; + if (eflag) edihedral = d_k[type] * p; + + const F_FLOAT fg = vb1x*vb2xm + vb1y*vb2ym + vb1z*vb2zm; + const F_FLOAT hg = vb3x*vb2xm + vb3y*vb2ym + vb3z*vb2zm; + const F_FLOAT fga = fg*ra2inv*rginv; + const F_FLOAT hgb = hg*rb2inv*rginv; + const F_FLOAT gaa = -ra2inv*rg; + const F_FLOAT gbb = rb2inv*rg; + + const F_FLOAT dtfx = gaa*ax; + const F_FLOAT dtfy = gaa*ay; + const F_FLOAT dtfz = gaa*az; + const F_FLOAT dtgx = fga*ax - hgb*bx; + const F_FLOAT dtgy = fga*ay - hgb*by; + const F_FLOAT dtgz = fga*az - hgb*bz; + const F_FLOAT dthx = gbb*bx; + const F_FLOAT dthy = gbb*by; + const F_FLOAT dthz = gbb*bz; + + const F_FLOAT df = -d_k[type] * df1; + + const F_FLOAT sx2 = df*dtgx; + const F_FLOAT sy2 = df*dtgy; + const F_FLOAT sz2 = df*dtgz; + + F_FLOAT f1[3],f2[3],f3[3],f4[3]; + f1[0] = df*dtfx; + f1[1] = df*dtfy; + f1[2] = df*dtfz; + + f2[0] = sx2 - f1[0]; + f2[1] = sy2 - f1[1]; + f2[2] = sz2 - f1[2]; + + f4[0] = df*dthx; + f4[1] = df*dthy; + f4[2] = df*dthz; + + f3[0] = -sx2 - f4[0]; + f3[1] = -sy2 - f4[1]; + f3[2] = -sz2 - f4[2]; + + // apply force to each of 4 atoms + + if (NEWTON_BOND || i1 < nlocal) { + a_f(i1,0) += f1[0]; + a_f(i1,1) += f1[1]; + a_f(i1,2) += f1[2]; + } + + if (NEWTON_BOND || i2 < nlocal) { + a_f(i2,0) += f2[0]; + a_f(i2,1) += f2[1]; + a_f(i2,2) += f2[2]; + } + + if (NEWTON_BOND || i3 < nlocal) { + a_f(i3,0) += f3[0]; + a_f(i3,1) += f3[1]; + a_f(i3,2) += f3[2]; + } + + if (NEWTON_BOND || i4 < nlocal) { + a_f(i4,0) += f4[0]; + a_f(i4,1) += f4[1]; + a_f(i4,2) += f4[2]; + } + + if (EVFLAG) + ev_tally(evm,i1,i2,i3,i4,edihedral,f1,f3,f4, + vb1x,vb1y,vb1z,vb2x,vb2y,vb2z,vb3x,vb3y,vb3z); + + // 1-4 LJ and Coulomb interactions + // tally energy/virial in pair, using newton_bond as newton flag + + if (d_weight[type] > 0.0) { + const int itype = atomtype[i1]; + const int jtype = atomtype[i4]; + + const F_FLOAT delx = x(i1,0) - x(i4,0); + const F_FLOAT dely = x(i1,1) - x(i4,1); + const F_FLOAT delz = x(i1,2) - x(i4,2); + const F_FLOAT rsq = delx*delx + dely*dely + delz*delz; + const F_FLOAT r2inv = 1.0/rsq; + const F_FLOAT r6inv = r2inv*r2inv*r2inv; + + F_FLOAT forcecoul; + if (implicit) forcecoul = qqrd2e * q[i1]*q[i4]*r2inv; + else forcecoul = qqrd2e * q[i1]*q[i4]*sqrt(r2inv); + const F_FLOAT forcelj = r6inv * (d_lj14_1(itype,jtype)*r6inv - d_lj14_2(itype,jtype)); + const F_FLOAT fpair = d_weight[type] * (forcelj+forcecoul)*r2inv; + + F_FLOAT ecoul = 0.0; + F_FLOAT evdwl = 0.0; + if (eflag) { + ecoul = d_weight[type] * forcecoul; + evdwl = r6inv * (d_lj14_3(itype,jtype)*r6inv - d_lj14_4(itype,jtype)); + evdwl *= d_weight[type]; + } + + if (newton_bond || i1 < nlocal) { + a_f(i1,0) += delx*fpair; + a_f(i1,1) += dely*fpair; + a_f(i1,2) += delz*fpair; + } + if (newton_bond || i4 < nlocal) { + a_f(i4,0) -= delx*fpair; + a_f(i4,1) -= dely*fpair; + a_f(i4,2) -= delz*fpair; + } + + if (EVFLAG) ev_tally(evm,i1,i4,evdwl,ecoul,fpair,delx,dely,delz); + } +} + +template +template +KOKKOS_INLINE_FUNCTION +void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute, const int &n) const { + EVM_FLOAT evm; + this->template operator()(TagDihedralCharmmCompute(), n, evm); +} + +/* ---------------------------------------------------------------------- */ + +/* + + 288c307 + < void DihedralCharmm::allocate() + --- + > void DihedralCharmmfsw::allocate() + + */ + +template +void DihedralCharmmfswKokkos::allocate() +{ + DihedralCharmmfsw::allocate(); +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more types +------------------------------------------------------------------------- */ + +/* + + 308c327 + < void DihedralCharmm::coeff(int narg, char **arg) + --- + > void DihedralCharmmfsw::coeff(int narg, char **arg) + + */ + +template +void DihedralCharmmfswKokkos::coeff(int narg, char **arg) +{ + DihedralCharmmfsw::coeff(narg, arg); + + int nd = atom->ndihedraltypes; + typename AT::tdual_ffloat_1d k_k("DihedralCharmm::k",nd+1); + typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmm::multiplicity",nd+1); + typename AT::tdual_ffloat_1d k_shift("DihedralCharmm::shift",nd+1); + typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmm::cos_shift",nd+1); + typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmm::sin_shift",nd+1); + typename AT::tdual_ffloat_1d k_weight("DihedralCharmm::weight",nd+1); + + d_k = k_k.template view(); + d_multiplicity = k_multiplicity.template view(); + d_shift = k_shift.template view(); + d_cos_shift = k_cos_shift.template view(); + d_sin_shift = k_sin_shift.template view(); + d_weight = k_weight.template view(); + + int n = atom->ndihedraltypes; + for (int i = 1; i <= n; i++) { + k_k.h_view[i] = k[i]; + k_multiplicity.h_view[i] = multiplicity[i]; + k_shift.h_view[i] = shift[i]; + k_cos_shift.h_view[i] = cos_shift[i]; + k_sin_shift.h_view[i] = sin_shift[i]; + k_weight.h_view[i] = weight[i]; + } + + k_k.template modify(); + k_multiplicity.template modify(); + k_shift.template modify(); + k_cos_shift.template modify(); + k_sin_shift.template modify(); + k_weight.template modify(); + + k_k.template sync(); + k_multiplicity.template sync(); + k_shift.template sync(); + k_cos_shift.template sync(); + k_sin_shift.template sync(); + k_weight.template sync(); +} + +/* ---------------------------------------------------------------------- + error check and initialize all values needed for force computation +------------------------------------------------------------------------- */ + +/* + + 350c369 + < void DihedralCharmm::init_style() + --- + > void DihedralCharmmfsw::init_style() + 382a402,425 + > + > // constants for applying force switch (LJ) and force_shift (coul) + > // to 1/4 dihedral atoms to match CHARMM pairwise interactions + > + > int itmp; + > int *p_dihedflag = (int *) force->pair->extract("dihedflag", itmp); + > auto p_cutljinner = (double *) force->pair->extract("cut_lj_inner", itmp); + > auto p_cutlj = (double *) force->pair->extract("cut_lj", itmp); + > auto p_cutcoul = (double *) force->pair->extract("cut_coul", itmp); + > + > if (p_cutcoul == nullptr || p_cutljinner == nullptr || p_cutlj == nullptr || + > p_dihedflag == nullptr) + > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); + > + > dihedflag = *p_dihedflag; + > cut_coul14 = *p_cutcoul; + > cut_lj_inner14 = *p_cutljinner; + > cut_lj14 = *p_cutlj; + > + > cut_coulinv14 = 1 / cut_coul14; + > cut_lj_inner3inv = (1 / cut_lj_inner14) * (1 / cut_lj_inner14) * (1 / cut_lj_inner14); + > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; + > cut_lj3inv = (1 / cut_lj14) * (1 / cut_lj14) * (1 / cut_lj14); + > cut_lj6inv = cut_lj3inv * cut_lj3inv; + + */ + +template +void DihedralCharmmfswKokkos::init_style() +{ + DihedralCharmmfsw::init_style(); + + int n = atom->ntypes; + DAT::tdual_ffloat_2d k_lj14_1("DihedralCharmm:lj14_1",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_2("DihedralCharmm:lj14_2",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_3("DihedralCharmm:lj14_3",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_4("DihedralCharmm:lj14_4",n+1,n+1); + + d_lj14_1 = k_lj14_1.template view(); + d_lj14_2 = k_lj14_2.template view(); + d_lj14_3 = k_lj14_3.template view(); + d_lj14_4 = k_lj14_4.template view(); + + + if (weightflag) { + int n = atom->ntypes; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + k_lj14_1.h_view(i,j) = lj14_1[i][j]; + k_lj14_2.h_view(i,j) = lj14_2[i][j]; + k_lj14_3.h_view(i,j) = lj14_3[i][j]; + k_lj14_4.h_view(i,j) = lj14_4[i][j]; + } + } + } + + k_lj14_1.template modify(); + k_lj14_2.template modify(); + k_lj14_3.template modify(); + k_lj14_4.template modify(); + + k_lj14_1.template sync(); + k_lj14_2.template sync(); + k_lj14_3.template sync(); + k_lj14_4.template sync(); +} + +/* ---------------------------------------------------------------------- + proc 0 reads coeffs from restart file, bcasts them +------------------------------------------------------------------------- */ + +/* + + 402c445 + < void DihedralCharmm::read_restart(FILE *fp) + --- + > void DihedralCharmmfsw::read_restart(FILE *fp) + + */ +template +void DihedralCharmmfswKokkos::read_restart(FILE *fp) +{ + DihedralCharmmfsw::read_restart(fp); + + int nd = atom->ndihedraltypes; + typename AT::tdual_ffloat_1d k_k("DihedralCharmm::k",nd+1); + typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmm::multiplicity",nd+1); + typename AT::tdual_ffloat_1d k_shift("DihedralCharmm::shift",nd+1); + typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmm::cos_shift",nd+1); + typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmm::sin_shift",nd+1); + typename AT::tdual_ffloat_1d k_weight("DihedralCharmm::weight",nd+1); + + d_k = k_k.template view(); + d_multiplicity = k_multiplicity.template view(); + d_shift = k_shift.template view(); + d_cos_shift = k_cos_shift.template view(); + d_sin_shift = k_sin_shift.template view(); + d_weight = k_weight.template view(); + + int n = atom->ndihedraltypes; + for (int i = 1; i <= n; i++) { + k_k.h_view[i] = k[i]; + k_multiplicity.h_view[i] = multiplicity[i]; + k_shift.h_view[i] = shift[i]; + k_cos_shift.h_view[i] = cos_shift[i]; + k_sin_shift.h_view[i] = sin_shift[i]; + k_weight.h_view[i] = weight[i]; + } + + k_k.template modify(); + k_multiplicity.template modify(); + k_shift.template modify(); + k_cos_shift.template modify(); + k_sin_shift.template modify(); + k_weight.template modify(); + + k_k.template sync(); + k_multiplicity.template sync(); + k_shift.template sync(); + k_cos_shift.template sync(); + k_sin_shift.template sync(); + k_weight.template sync(); +} + +/* ---------------------------------------------------------------------- + tally energy and virial into global and per-atom accumulators + virial = r1F1 + r2F2 + r3F3 + r4F4 = (r1-r2) F1 + (r3-r2) F3 + (r4-r2) F4 + = (r1-r2) F1 + (r3-r2) F3 + (r4-r3 + r3-r2) F4 + = vb1*f1 + vb2*f3 + (vb3+vb2)*f4 +------------------------------------------------------------------------- */ + +template +//template +KOKKOS_INLINE_FUNCTION +void DihedralCharmmfswKokkos::ev_tally(EVM_FLOAT &evm, const int i1, const int i2, const int i3, const int i4, + F_FLOAT &edihedral, F_FLOAT *f1, F_FLOAT *f3, F_FLOAT *f4, + const F_FLOAT &vb1x, const F_FLOAT &vb1y, const F_FLOAT &vb1z, + const F_FLOAT &vb2x, const F_FLOAT &vb2y, const F_FLOAT &vb2z, + const F_FLOAT &vb3x, const F_FLOAT &vb3y, const F_FLOAT &vb3z) const +{ + E_FLOAT edihedralquarter; + F_FLOAT v[6]; + + if (eflag_either) { + if (eflag_global) { + if (newton_bond) evm.emol += edihedral; + else { + edihedralquarter = 0.25*edihedral; + if (i1 < nlocal) evm.emol += edihedralquarter; + if (i2 < nlocal) evm.emol += edihedralquarter; + if (i3 < nlocal) evm.emol += edihedralquarter; + if (i4 < nlocal) evm.emol += edihedralquarter; + } + } + if (eflag_atom) { + edihedralquarter = 0.25*edihedral; + if (newton_bond || i1 < nlocal) d_eatom[i1] += edihedralquarter; + if (newton_bond || i2 < nlocal) d_eatom[i2] += edihedralquarter; + if (newton_bond || i3 < nlocal) d_eatom[i3] += edihedralquarter; + if (newton_bond || i4 < nlocal) d_eatom[i4] += edihedralquarter; + } + } + + if (vflag_either) { + v[0] = vb1x*f1[0] + vb2x*f3[0] + (vb3x+vb2x)*f4[0]; + v[1] = vb1y*f1[1] + vb2y*f3[1] + (vb3y+vb2y)*f4[1]; + v[2] = vb1z*f1[2] + vb2z*f3[2] + (vb3z+vb2z)*f4[2]; + v[3] = vb1x*f1[1] + vb2x*f3[1] + (vb3x+vb2x)*f4[1]; + v[4] = vb1x*f1[2] + vb2x*f3[2] + (vb3x+vb2x)*f4[2]; + v[5] = vb1y*f1[2] + vb2y*f3[2] + (vb3y+vb2y)*f4[2]; + + if (vflag_global) { + if (newton_bond) { + evm.v[0] += v[0]; + evm.v[1] += v[1]; + evm.v[2] += v[2]; + evm.v[3] += v[3]; + evm.v[4] += v[4]; + evm.v[5] += v[5]; + } else { + if (i1 < nlocal) { + evm.v[0] += 0.25*v[0]; + evm.v[1] += 0.25*v[1]; + evm.v[2] += 0.25*v[2]; + evm.v[3] += 0.25*v[3]; + evm.v[4] += 0.25*v[4]; + evm.v[5] += 0.25*v[5]; + } + if (i2 < nlocal) { + evm.v[0] += 0.25*v[0]; + evm.v[1] += 0.25*v[1]; + evm.v[2] += 0.25*v[2]; + evm.v[3] += 0.25*v[3]; + evm.v[4] += 0.25*v[4]; + evm.v[5] += 0.25*v[5]; + } + if (i3 < nlocal) { + evm.v[0] += 0.25*v[0]; + evm.v[1] += 0.25*v[1]; + evm.v[2] += 0.25*v[2]; + evm.v[3] += 0.25*v[3]; + evm.v[4] += 0.25*v[4]; + evm.v[5] += 0.25*v[5]; + } + if (i4 < nlocal) { + evm.v[0] += 0.25*v[0]; + evm.v[1] += 0.25*v[1]; + evm.v[2] += 0.25*v[2]; + evm.v[3] += 0.25*v[3]; + evm.v[4] += 0.25*v[4]; + evm.v[5] += 0.25*v[5]; + } + } + } + + if (vflag_atom) { + if (newton_bond || i1 < nlocal) { + d_vatom(i1,0) += 0.25*v[0]; + d_vatom(i1,1) += 0.25*v[1]; + d_vatom(i1,2) += 0.25*v[2]; + d_vatom(i1,3) += 0.25*v[3]; + d_vatom(i1,4) += 0.25*v[4]; + d_vatom(i1,5) += 0.25*v[5]; + } + if (newton_bond || i2 < nlocal) { + d_vatom(i2,0) += 0.25*v[0]; + d_vatom(i2,1) += 0.25*v[1]; + d_vatom(i2,2) += 0.25*v[2]; + d_vatom(i2,3) += 0.25*v[3]; + d_vatom(i2,4) += 0.25*v[4]; + d_vatom(i2,5) += 0.25*v[5]; + } + if (newton_bond || i3 < nlocal) { + d_vatom(i3,0) += 0.25*v[0]; + d_vatom(i3,1) += 0.25*v[1]; + d_vatom(i3,2) += 0.25*v[2]; + d_vatom(i3,3) += 0.25*v[3]; + d_vatom(i3,4) += 0.25*v[4]; + d_vatom(i3,5) += 0.25*v[5]; + } + if (newton_bond || i4 < nlocal) { + d_vatom(i4,0) += 0.25*v[0]; + d_vatom(i4,1) += 0.25*v[1]; + d_vatom(i4,2) += 0.25*v[2]; + d_vatom(i4,3) += 0.25*v[3]; + d_vatom(i4,4) += 0.25*v[4]; + d_vatom(i4,5) += 0.25*v[5]; + } + } + } +} + +/* ---------------------------------------------------------------------- + tally eng_vdwl and virial into global and per-atom accumulators + need i < nlocal test since called by bond_quartic and dihedral_charmm +------------------------------------------------------------------------- */ + +template +KOKKOS_INLINE_FUNCTION +void DihedralCharmmfswKokkos::ev_tally(EVM_FLOAT &evm, const int i, const int j, + const F_FLOAT &evdwl, const F_FLOAT &ecoul, const F_FLOAT &fpair, const F_FLOAT &delx, + const F_FLOAT &dely, const F_FLOAT &delz) const +{ + E_FLOAT evdwlhalf,ecoulhalf,epairhalf; + F_FLOAT v[6]; + + + if (eflag_either) { + if (eflag_global) { + if (newton_bond) { + evm.evdwl += evdwl; + evm.ecoul += ecoul; + } else { + evdwlhalf = 0.5*evdwl; + ecoulhalf = 0.5*ecoul; + if (i < nlocal) { + evm.evdwl += evdwlhalf; + evm.ecoul += ecoulhalf; + } + if (j < nlocal) { + evm.evdwl += evdwlhalf; + evm.ecoul += ecoulhalf; + } + } + } + if (eflag_atom) { + epairhalf = 0.5 * (evdwl + ecoul); + if (newton_bond || i < nlocal) d_eatom_pair[i] += epairhalf; + if (newton_bond || j < nlocal) d_eatom_pair[j] += epairhalf; + } + } + + if (vflag_either) { + v[0] = delx*delx*fpair; + v[1] = dely*dely*fpair; + v[2] = delz*delz*fpair; + v[3] = delx*dely*fpair; + v[4] = delx*delz*fpair; + v[5] = dely*delz*fpair; + + if (vflag_global) { + if (newton_bond) { + evm.vp[0] += v[0]; + evm.vp[1] += v[1]; + evm.vp[2] += v[2]; + evm.vp[3] += v[3]; + evm.vp[4] += v[4]; + evm.vp[5] += v[5]; + } else { + if (i < nlocal) { + evm.vp[0] += 0.5*v[0]; + evm.vp[1] += 0.5*v[1]; + evm.vp[2] += 0.5*v[2]; + evm.vp[3] += 0.5*v[3]; + evm.vp[4] += 0.5*v[4]; + evm.vp[5] += 0.5*v[5]; + } + if (j < nlocal) { + evm.vp[0] += 0.5*v[0]; + evm.vp[1] += 0.5*v[1]; + evm.vp[2] += 0.5*v[2]; + evm.vp[3] += 0.5*v[3]; + evm.vp[4] += 0.5*v[4]; + evm.vp[5] += 0.5*v[5]; + } + } + } + + if (vflag_atom) { + if (newton_bond || i < nlocal) { + d_vatom_pair(i,0) += 0.5*v[0]; + d_vatom_pair(i,1) += 0.5*v[1]; + d_vatom_pair(i,2) += 0.5*v[2]; + d_vatom_pair(i,3) += 0.5*v[3]; + d_vatom_pair(i,4) += 0.5*v[4]; + d_vatom_pair(i,5) += 0.5*v[5]; + } + if (newton_bond || j < nlocal) { + d_vatom_pair(j,0) += 0.5*v[0]; + d_vatom_pair(j,1) += 0.5*v[1]; + d_vatom_pair(j,2) += 0.5*v[2]; + d_vatom_pair(j,3) += 0.5*v[3]; + d_vatom_pair(j,4) += 0.5*v[4]; + d_vatom_pair(j,5) += 0.5*v[5]; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +namespace LAMMPS_NS { +template class DihedralCharmmfswKokkos; +#ifdef LMP_KOKKOS_GPU +template class DihedralCharmmfswKokkos; +#endif +} + + + +/* + + + 355c374 + < error->all(FLERR, "Dihedral style charmm must be set to same r-RESPA level as 'pair'"); + --- + > error->all(FLERR, "Dihedral style charmmfsw must be set to same r-RESPA level as 'pair'"); + 357c376 + < error->all(FLERR, "Dihedral style charmm must be set to same r-RESPA level as 'outer'"); + --- + > error->all(FLERR, "Dihedral style charmmfsw must be set to same r-RESPA level as 'outer'"); + 373c392 + < error->all(FLERR, "Dihedral charmm is incompatible with Pair style"); + --- + > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); + 380c399 + < error->all(FLERR, "Dihedral charmm is incompatible with Pair style"); + --- + > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); + + 389c432 + < void DihedralCharmm::write_restart(FILE *fp) + --- + > void DihedralCharmmfsw::write_restart(FILE *fp) + 430c473 + < void DihedralCharmm::write_data(FILE *fp) + --- + > void DihedralCharmmfsw::write_data(FILE *fp) + + */ + +// nothing to do for all these, inherited from DihedralCharmmfsw diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.h b/src/KOKKOS/dihedral_charmmfsw_kokkos.h new file mode 100644 index 0000000000..413945826f --- /dev/null +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.h @@ -0,0 +1,267 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + + *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** + + (1) first draft version of DihedralCharmmfswKokkos exactly + same as DihedralCharmmKokkos but with new class name + + (2) second draft version: nothing changed in header file + + method: track changes from serial kspace dihedral_charmm to + dihedral_charmmfsw and apply to DihedralCharmmKokkos + + % diff dihedral_charmm.h dihedral_charmmfsw.h + +------------------------------------------------------------------------- */ + +/* + + 16c16 + < DihedralStyle(charmm,DihedralCharmm); + --- + > DihedralStyle(charmmfsw,DihedralCharmmfsw); + + */ + +#ifdef DIHEDRAL_CLASS +// clang-format off +DihedralStyle(charmmfsw/kk,DihedralCharmmfswKokkos); +DihedralStyle(charmmfsw/kk/device,DihedralCharmmfswKokkos); +DihedralStyle(charmmfsw/kk/host,DihedralCharmmfswKokkos); +// clang-format on +#else + +/* + + 20,21c20,21 + < #ifndef LMP_DIHEDRAL_CHARMM_H + < #define LMP_DIHEDRAL_CHARMM_H + --- + > #ifndef LMP_DIHEDRAL_CHARMMFSW_H + > #define LMP_DIHEDRAL_CHARMMFSW_H + + */ + +// clang-format off +#ifndef LMP_DIHEDRAL_CHARMMFSW_KOKKOS_H +#define LMP_DIHEDRAL_CHARMMFSW_KOKKOS_H + +#include "dihedral_charmmfsw.h" +#include "kokkos_type.h" +#include "dihedral_charmm_kokkos.h" + +/* + + s_EVM_FLOAT and TagDihedralCharmmCompute conflict because style_dihedral.h + includes both dihedral_charmm_kokkos.h and dihedral_charmmfsw_kokkos.h + so comment out definitions in here and include dihedral_charmm_kokkos.h + in dihedral_charmmfsw_kokkos.h: + + In file included from /Users/mitch/Dropbox/lammps/lammps/src/force.cpp:18: + In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:65:8: error: redefinition of 's_EVM_FLOAT' + struct s_EVM_FLOAT { + ^ + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here + struct s_EVM_FLOAT { + ^ + In file included from /Users/mitch/Dropbox/lammps/lammps/src/force.cpp:18: + In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:104:8: error: redefinition of 'TagDihedralCharmmCompute' + struct TagDihedralCharmmCompute{}; + ^ + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:70:8: note: previous definition is here + struct TagDihedralCharmmCompute{}; + ^ + In file included from /Users/mitch/Dropbox/lammps/lammps/src/lammps.cpp:23: + In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:65:8: error: redefinition of 's_EVM_FLOAT' + struct s_EVM_FLOAT { + ^ + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here + struct s_EVM_FLOAT { + ^ + In file included from /Users/mitch/Dropbox/lammps/lammps/src/lammps.cpp:23: + In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:104:8: error: redefinition of 'TagDihedralCharmmCompute' + struct TagDihedralCharmmCompute{}; + ^ + /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:70:8: note: previous definition is here + struct TagDihedralCharmmCompute{}; + ^ + + */ + +namespace LAMMPS_NS { + +/* +struct s_EVM_FLOAT { + E_FLOAT evdwl; + E_FLOAT ecoul; + E_FLOAT emol; + F_FLOAT v[6]; + F_FLOAT vp[6]; + KOKKOS_INLINE_FUNCTION + s_EVM_FLOAT() { + evdwl = 0; + ecoul = 0; + emol = 0; + v[0] = 0; v[1] = 0; v[2] = 0; + v[3] = 0; v[4] = 0; v[5] = 0; + vp[0] = 0; vp[1] = 0; vp[2] = 0; + vp[3] = 0; vp[4] = 0; vp[5] = 0; + } + + KOKKOS_INLINE_FUNCTION + void operator+=(const s_EVM_FLOAT &rhs) { + evdwl += rhs.evdwl; + ecoul += rhs.ecoul; + emol += rhs.emol; + v[0] += rhs.v[0]; + v[1] += rhs.v[1]; + v[2] += rhs.v[2]; + v[3] += rhs.v[3]; + v[4] += rhs.v[4]; + v[5] += rhs.v[5]; + vp[0] += rhs.vp[0]; + vp[1] += rhs.vp[1]; + vp[2] += rhs.vp[2]; + vp[3] += rhs.vp[3]; + vp[4] += rhs.vp[4]; + vp[5] += rhs.vp[5]; + } +}; +typedef struct s_EVM_FLOAT EVM_FLOAT; + +template +struct TagDihedralCharmmCompute{}; + +*/ + +/* + 27c27 + < class DihedralCharmm : public Dihedral { + --- + > class DihedralCharmmfsw : public Dihedral { + 29,30c29,30 + < DihedralCharmm(class LAMMPS *); + < ~DihedralCharmm() override; + --- + > DihedralCharmmfsw(class LAMMPS *); + > ~DihedralCharmmfsw() override; + + */ + +template +class DihedralCharmmfswKokkos : public DihedralCharmmfsw { + public: + typedef DeviceType device_type; + typedef EVM_FLOAT value_type; + typedef ArrayTypes AT; + + DihedralCharmmfswKokkos(class LAMMPS *); + ~DihedralCharmmfswKokkos() override; + void compute(int, int) override; + void coeff(int, char **) override; + void init_style() override; + void read_restart(FILE *) override; + + template + KOKKOS_INLINE_FUNCTION + void operator()(TagDihedralCharmmCompute, const int&, EVM_FLOAT&) const; + + template + KOKKOS_INLINE_FUNCTION + void operator()(TagDihedralCharmmCompute, const int&) const; + + //template + KOKKOS_INLINE_FUNCTION + void ev_tally(EVM_FLOAT &evm, const int i1, const int i2, const int i3, const int i4, + F_FLOAT &edihedral, F_FLOAT *f1, F_FLOAT *f3, F_FLOAT *f4, + const F_FLOAT &vb1x, const F_FLOAT &vb1y, const F_FLOAT &vb1z, + const F_FLOAT &vb2x, const F_FLOAT &vb2y, const F_FLOAT &vb2z, + const F_FLOAT &vb3x, const F_FLOAT &vb3y, const F_FLOAT &vb3z) const; + + KOKKOS_INLINE_FUNCTION + void ev_tally(EVM_FLOAT &evm, const int i, const int j, + const F_FLOAT &evdwl, const F_FLOAT &ecoul, const F_FLOAT &fpair, const F_FLOAT &delx, + const F_FLOAT &dely, const F_FLOAT &delz) const; + + protected: + + class NeighborKokkos *neighborKK; + + typename AT::t_x_array_randomread x; + typename AT::t_int_1d_randomread atomtype; + typename AT::t_ffloat_1d_randomread q; + typename AT::t_f_array f; + typename AT::t_int_2d dihedrallist; + + typedef typename KKDevice::value KKDeviceType; + Kokkos::DualView k_eatom; + Kokkos::DualView k_vatom; + Kokkos::View > d_eatom; + Kokkos::View > d_vatom; + + Kokkos::DualView k_eatom_pair; + Kokkos::DualView k_vatom_pair; + Kokkos::View > d_eatom_pair; + Kokkos::View > d_vatom_pair; + + int nlocal,newton_bond; + int eflag,vflag; + double qqrd2e; + + Kokkos::DualView k_warning_flag; + typename Kokkos::DualView::t_dev d_warning_flag; + typename Kokkos::DualView::t_host h_warning_flag; + + typename AT::t_ffloat_2d d_lj14_1; + typename AT::t_ffloat_2d d_lj14_2; + typename AT::t_ffloat_2d d_lj14_3; + typename AT::t_ffloat_2d d_lj14_4; + + typename AT::t_ffloat_1d d_k; + typename AT::t_ffloat_1d d_multiplicity; + typename AT::t_ffloat_1d d_shift; + typename AT::t_ffloat_1d d_sin_shift; + typename AT::t_ffloat_1d d_cos_shift; + typename AT::t_ffloat_1d d_weight; + + void allocate() override; +}; + +} + +#endif +#endif + + + +/* + + 38a39,43 + > int implicit, weightflag, dihedflag; + > double cut_lj_inner14, cut_lj14, cut_coul14; + > double evdwl14_12, evdwl14_6, cut_coulinv14; + > double cut_lj_inner3inv, cut_lj_inner6inv, cut_lj3inv, cut_lj6inv; + > + 42d46 + < int implicit, weightflag; + + */ + +// nothing to do here, inherited from DihedralCharmmfsw diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp new file mode 100644 index 0000000000..88efec5fda --- /dev/null +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp @@ -0,0 +1,941 @@ +// clang-format off +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + + Contributing authors: + + - Ray Shan (SNL) - original PairLJCharmmCoulLongKokkos + + - Mitch Murphy (alphataubio) - PairLJCharmmfswCoulLongKokkos update (2023/12) + + Based on serial kspace lj-fsw sections (force-switched) provided by + Robert Meissner and Lucio Colombi Ciacchi of Bremen University, Germany, + with additional assistance from Robert A. Latour, Clemson University + + ------------------------------------------------------------------------- */ + + + +/* ---------------------------------------------------------------------- + + *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** + + (1) first draft version of PairLJCharmmfswCoulLongKokkos almost exactly + same as PairLJCharmmCoulLongKokkos but with new class name + + method: track changes from serial kspace pair_lj_charmm_coul_long to + pair_lj_charmmfsw_coul_long and apply to PairLJCharmmCoulLongKokkos + + ISSUES: + + (A) charmm denom_lj_inv cache , is it to optimize code because division + is slower that multiplication ?? + + + + ------------------------------------------------------------------------- */ + + +/* + 19c23 + < #include "pair_lj_charmm_coul_long.h" + --- + > #include "pair_lj_charmmfsw_coul_long.h" + + */ + +#include "pair_lj_charmmfsw_coul_long_kokkos.h" + +#include "atom_kokkos.h" +#include "atom_masks.h" +#include "error.h" +#include "force.h" +#include "kokkos.h" +#include "memory_kokkos.h" +#include "neigh_list.h" +#include "neigh_request.h" +#include "neighbor.h" +#include "respa.h" +#include "update.h" + +#include +#include + +using namespace LAMMPS_NS; + + +#define EWALD_F 1.12837917 +#define EWALD_P 0.3275911 +#define A1 0.254829592 +#define A2 -0.284496736 +#define A3 1.421413741 +#define A4 -1.453152027 +#define A5 1.061405429 + +/* ---------------------------------------------------------------------- */ + +/* + 47c51 + < PairLJCharmmCoulLong::PairLJCharmmCoulLong(LAMMPS *lmp) : Pair(lmp) + --- + > PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(LAMMPS *lmp) : Pair(lmp) + 55a60,72 + > + > // short-range/long-range flag accessed by DihedralCharmmfsw + > + > dihedflag = 1; + > + > // switch qqr2e from LAMMPS value to CHARMM value + > + > if (strcmp(update->unit_style,"real") == 0) { + > if ((comm->me == 0) && (force->qqr2e != force->qqr2e_charmm_real)) + > error->message(FLERR,"Switching to CHARMM coulomb energy" + > " conversion constant"); + > force->qqr2e = force->qqr2e_charmm_real; + > } + + */ + +// added superclass constructor to inherit from PairLJCharmmfswCoulLong + +template +PairLJCharmmfswCoulLongKokkos::PairLJCharmmfswCoulLongKokkos(LAMMPS *lmp):PairLJCharmmfswCoulLong(lmp) +{ + + // pair_lj_charmmfsw_coul_long_kokkos.cpp:112:28: error: qualified reference to 'PairLJCharmmfswCoulLong' is a constructor name rather than a type in this context + // ??? PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(lmp); + + respa_enable = 0; + + kokkosable = 1; + atomKK = (AtomKokkos *) atom; + execution_space = ExecutionSpaceFromDevice::space; + datamask_read = X_MASK | F_MASK | TYPE_MASK | Q_MASK | ENERGY_MASK | VIRIAL_MASK; + datamask_modify = F_MASK | ENERGY_MASK | VIRIAL_MASK; +} + +/* ---------------------------------------------------------------------- */ + +/* + + 60c77 + < PairLJCharmmCoulLong::~PairLJCharmmCoulLong() + --- + > PairLJCharmmfswCoulLong::~PairLJCharmmfswCoulLong() + 61a79,87 + > // switch qqr2e back from CHARMM value to LAMMPS value + > + > if (update && strcmp(update->unit_style,"real") == 0) { + > if ((comm->me == 0) && (force->qqr2e == force->qqr2e_charmm_real)) + > error->message(FLERR,"Restoring original LAMMPS coulomb energy" + > " conversion constant"); + > force->qqr2e = force->qqr2e_lammps_real; + > } + > + + */ + +// added superclass constructor to inherit from PairLJCharmmfswCoulLong + +template +PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() +{ + + // pair_lj_charmmfsw_coul_long_kokkos.cpp:150:28: error: qualified reference to 'PairLJCharmmfswCoulLong' is a constructor name rather than a type in this context + // ??? PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(); + + if (copymode) return; + + if (allocated) { + memoryKK->destroy_kokkos(k_eatom,eatom); + memoryKK->destroy_kokkos(k_vatom,vatom); + memoryKK->destroy_kokkos(k_cutsq,cutsq); + } +} + +/* ---------------------------------------------------------------------- */ + +/* + 87c112 + < void PairLJCharmmCoulLong::compute(int eflag, int vflag) + --- + > void PairLJCharmmfswCoulLong::compute(int eflag, int vflag) + 90c115 + < double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,ecoul,fpair; + --- + > double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,evdwl12,evdwl6,ecoul,fpair; + 92c117 + < double r,r2inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; + --- + > double r,rinv,r2inv,r3inv,r6inv,rsq,forcecoul,forcelj,factor_coul,factor_lj; + 94c119 + < double philj,switch1,switch2; + --- + > double switch1; + 96d120 + < double rsq; + 174,179c198,200 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + < } + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + > } + 205d225 + < evdwl = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + 207,209c227,240 + < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < evdwl *= switch1; + --- + > r = sqrt(rsq); + > rinv = 1.0/r; + > r3inv = rinv*rinv*rinv; + > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * + > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); + > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * + > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); + > evdwl = evdwl12 + evdwl6; + > } else { + > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - + > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + > evdwl6 = -lj4[itype][jtype]*r6inv + + > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; + > evdwl = evdwl12 + evdwl6; + + */ + +template +void PairLJCharmmfswCoulLongKokkos::compute(int eflag_in, int vflag_in) +{ + eflag = eflag_in; + vflag = vflag_in; + + if (neighflag == FULL) no_virial_fdotr_compute = 1; + + ev_init(eflag,vflag,0); + + // reallocate per-atom arrays if necessary + + if (eflag_atom) { + memoryKK->destroy_kokkos(k_eatom,eatom); + memoryKK->create_kokkos(k_eatom,eatom,maxeatom,"pair:eatom"); + d_eatom = k_eatom.view(); + } + if (vflag_atom) { + memoryKK->destroy_kokkos(k_vatom,vatom); + memoryKK->create_kokkos(k_vatom,vatom,maxvatom,"pair:vatom"); + d_vatom = k_vatom.view(); + } + + atomKK->sync(execution_space,datamask_read); + k_cutsq.template sync(); + k_params.template sync(); + if (eflag || vflag) atomKK->modified(execution_space,datamask_modify); + else atomKK->modified(execution_space,F_MASK); + + x = atomKK->k_x.view(); + c_x = atomKK->k_x.view(); + f = atomKK->k_f.view(); + q = atomKK->k_q.view(); + type = atomKK->k_type.view(); + nlocal = atom->nlocal; + nall = atom->nlocal + atom->nghost; + special_lj[0] = force->special_lj[0]; + special_lj[1] = force->special_lj[1]; + special_lj[2] = force->special_lj[2]; + special_lj[3] = force->special_lj[3]; + special_coul[0] = force->special_coul[0]; + special_coul[1] = force->special_coul[1]; + special_coul[2] = force->special_coul[2]; + special_coul[3] = force->special_coul[3]; + qqrd2e = force->qqrd2e; + newton_pair = force->newton_pair; + + // loop over neighbors of my atoms + + copymode = 1; + + EV_FLOAT ev; + if (ncoultablebits) + ev = pair_compute,CoulLongTable<1> > + (this,(NeighListKokkos*)list); + else + ev = pair_compute,CoulLongTable<0> > + (this,(NeighListKokkos*)list); + + + if (eflag) { + eng_vdwl += ev.evdwl; + eng_coul += ev.ecoul; + } + if (vflag_global) { + virial[0] += ev.v[0]; + virial[1] += ev.v[1]; + virial[2] += ev.v[2]; + virial[3] += ev.v[3]; + virial[4] += ev.v[4]; + virial[5] += ev.v[5]; + } + + if (eflag_atom) { + k_eatom.template modify(); + k_eatom.template sync(); + } + + if (vflag_atom) { + k_vatom.template modify(); + k_vatom.template sync(); + } + + if (vflag_fdotr) pair_virial_fdotr_compute(this); + + copymode = 0; +} + +/* ---------------------------------------------------------------------- + compute LJ CHARMM pair force between atoms i and j + ---------------------------------------------------------------------- */ +template +template +KOKKOS_INLINE_FUNCTION +F_FLOAT PairLJCharmmfswCoulLongKokkos:: +compute_fpair(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, + const int& itype, const int& jtype) const { + const F_FLOAT r2inv = 1.0/rsq; + const F_FLOAT r6inv = r2inv*r2inv*r2inv; + F_FLOAT forcelj, switch1, switch2, englj; + + forcelj = r6inv * + ((STACKPARAMS?m_params[itype][jtype].lj1:params(itype,jtype).lj1)*r6inv - + (STACKPARAMS?m_params[itype][jtype].lj2:params(itype,jtype).lj2)); + + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + switch2 = 12.0*rsq * (cut_ljsq-rsq) * (rsq-cut_lj_innersq) / denom_lj; + englj = r6inv * + ((STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*r6inv - + (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)); + forcelj = forcelj*switch1 + englj*switch2; + } + + return forcelj*r2inv; +} + +/* ---------------------------------------------------------------------- + compute LJ CHARMM pair potential energy between atoms i and j + ---------------------------------------------------------------------- */ +template +template +KOKKOS_INLINE_FUNCTION +F_FLOAT PairLJCharmmfswCoulLongKokkos:: +compute_evdwl(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, + const int& itype, const int& jtype) const { + const F_FLOAT r2inv = 1.0/rsq; + const F_FLOAT r6inv = r2inv*r2inv*r2inv; + F_FLOAT englj, switch1; + + englj = r6inv * + ((STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*r6inv - + (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)); + + if (rsq > cut_lj_innersq) { + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + englj *= switch1; + } + + return englj; + +} + +/* ---------------------------------------------------------------------- + compute coulomb pair force between atoms i and j + ---------------------------------------------------------------------- */ +template +template +KOKKOS_INLINE_FUNCTION +F_FLOAT PairLJCharmmfswCoulLongKokkos:: +compute_fcoul(const F_FLOAT& rsq, const int& /*i*/, const int&j, + const int& /*itype*/, const int& /*jtype*/, + const F_FLOAT& factor_coul, const F_FLOAT& qtmp) const { + if (Specialisation::DoTable && rsq > tabinnersq) { + union_int_float_t rsq_lookup; + rsq_lookup.f = rsq; + const int itable = (rsq_lookup.i & ncoulmask) >> ncoulshiftbits; + const F_FLOAT fraction = (rsq_lookup.f - d_rtable[itable]) * d_drtable[itable]; + const F_FLOAT table = d_ftable[itable] + fraction*d_dftable[itable]; + F_FLOAT forcecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + const F_FLOAT table = d_ctable[itable] + fraction*d_dctable[itable]; + const F_FLOAT prefactor = qtmp*q[j] * table; + forcecoul -= (1.0-factor_coul)*prefactor; + } + return forcecoul/rsq; + } else { + const F_FLOAT r = sqrt(rsq); + const F_FLOAT grij = g_ewald * r; + const F_FLOAT expm2 = exp(-grij*grij); + const F_FLOAT t = 1.0 / (1.0 + EWALD_P*grij); + const F_FLOAT rinv = 1.0/r; + const F_FLOAT erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; + const F_FLOAT prefactor = qqrd2e * qtmp*q[j]*rinv; + F_FLOAT forcecoul = prefactor * (erfc + EWALD_F*grij*expm2); + if (factor_coul < 1.0) forcecoul -= (1.0-factor_coul)*prefactor; + + return forcecoul*rinv*rinv; + } +} + +/* ---------------------------------------------------------------------- + compute coulomb pair potential energy between atoms i and j + ---------------------------------------------------------------------- */ +template +template +KOKKOS_INLINE_FUNCTION +F_FLOAT PairLJCharmmfswCoulLongKokkos:: +compute_ecoul(const F_FLOAT& rsq, const int& /*i*/, const int&j, + const int& /*itype*/, const int& /*jtype*/, const F_FLOAT& factor_coul, const F_FLOAT& qtmp) const { + if (Specialisation::DoTable && rsq > tabinnersq) { + union_int_float_t rsq_lookup; + rsq_lookup.f = rsq; + const int itable = (rsq_lookup.i & ncoulmask) >> ncoulshiftbits; + const F_FLOAT fraction = (rsq_lookup.f - d_rtable[itable]) * d_drtable[itable]; + const F_FLOAT table = d_etable[itable] + fraction*d_detable[itable]; + F_FLOAT ecoul = qtmp*q[j] * table; + if (factor_coul < 1.0) { + const F_FLOAT table = d_ctable[itable] + fraction*d_dctable[itable]; + const F_FLOAT prefactor = qtmp*q[j] * table; + ecoul -= (1.0-factor_coul)*prefactor; + } + return ecoul; + } else { + const F_FLOAT r = sqrt(rsq); + const F_FLOAT grij = g_ewald * r; + const F_FLOAT expm2 = exp(-grij*grij); + const F_FLOAT t = 1.0 / (1.0 + EWALD_P*grij); + const F_FLOAT erfc = t * (A1+t*(A2+t*(A3+t*(A4+t*A5)))) * expm2; + const F_FLOAT prefactor = qqrd2e * qtmp*q[j]/r; + F_FLOAT ecoul = prefactor * erfc; + if (factor_coul < 1.0) ecoul -= (1.0-factor_coul)*prefactor; + return ecoul; + } +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +template +void PairLJCharmmfswCoulLongKokkos::allocate() +{ + PairLJCharmmfswCoulLong::allocate(); + + int n = atom->ntypes; + + memory->destroy(cutsq); + memoryKK->create_kokkos(k_cutsq,cutsq,n+1,n+1,"pair:cutsq"); + d_cutsq = k_cutsq.template view(); + + d_cut_ljsq = typename AT::t_ffloat_2d("pair:cut_ljsq",n+1,n+1); + + d_cut_coulsq = typename AT::t_ffloat_2d("pair:cut_coulsq",n+1,n+1); + + k_params = Kokkos::DualView("PairLJCharmmCoulLong::params",n+1,n+1); + params = k_params.template view(); +} + +template +void PairLJCharmmfswCoulLongKokkos::init_tables(double cut_coul, double *cut_respa) +{ + Pair::init_tables(cut_coul,cut_respa); + + typedef typename ArrayTypes::t_ffloat_1d table_type; + typedef typename ArrayTypes::t_ffloat_1d host_table_type; + + int ntable = 1; + for (int i = 0; i < ncoultablebits; i++) ntable *= 2; + + + // Copy rtable and drtable + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + for (int i = 0; i < ntable; i++) { + h_table(i) = rtable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_rtable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + for (int i = 0; i < ntable; i++) { + h_table(i) = drtable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_drtable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + // Copy ftable and dftable + for (int i = 0; i < ntable; i++) { + h_table(i) = ftable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_ftable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + for (int i = 0; i < ntable; i++) { + h_table(i) = dftable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_dftable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + // Copy ctable and dctable + for (int i = 0; i < ntable; i++) { + h_table(i) = ctable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_ctable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + for (int i = 0; i < ntable; i++) { + h_table(i) = dctable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_dctable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + // Copy etable and detable + for (int i = 0; i < ntable; i++) { + h_table(i) = etable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_etable = d_table; + } + + { + host_table_type h_table("HostTable",ntable); + table_type d_table("DeviceTable",ntable); + + for (int i = 0; i < ntable; i++) { + h_table(i) = detable[i]; + } + Kokkos::deep_copy(d_table,h_table); + d_detable = d_table; + } +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +/* + 682c733 + < void PairLJCharmmCoulLong::init_style() + --- + > void PairLJCharmmfswCoulLong::init_style() + 686c737 + < "Pair style lj/charmm/coul/long requires atom attribute q"); + --- + > "Pair style lj/charmmfsw/coul/long requires atom attribute q"); + 688c739 + < // request regular or rRESPA neighbor list + --- + > // request regular or rRESPA neighbor lists + 705a757,766 + > cut_ljinv = 1.0/cut_lj; + > cut_lj_innerinv = 1.0/cut_lj_inner; + > cut_lj3 = cut_lj * cut_lj * cut_lj; + > cut_lj3inv = cut_ljinv * cut_ljinv * cut_ljinv; + > cut_lj_inner3inv = cut_lj_innerinv * cut_lj_innerinv * cut_lj_innerinv; + > cut_lj_inner3 = cut_lj_inner * cut_lj_inner * cut_lj_inner; + > cut_lj6 = cut_ljsq * cut_ljsq * cut_ljsq; + > cut_lj6inv = cut_lj3inv * cut_lj3inv; + > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; + > cut_lj_inner6 = cut_lj_innersq * cut_lj_innersq * cut_lj_innersq; + 709,711c770,773 + < denom_lj = ( (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + < (cut_ljsq-cut_lj_innersq) ); + < denom_lj_inv = 1.0 / denom_lj; + --- + > denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + > (cut_ljsq-cut_lj_innersq); + > denom_lj12 = 1.0/(cut_lj6 - cut_lj_inner6); + > denom_lj6 = 1.0/(cut_lj3 - cut_lj_inner3); + 718,730d779 + < cut_in_off = cut_respa[0]; + < cut_in_on = cut_respa[1]; + < cut_out_on = cut_respa[2]; + < cut_out_off = cut_respa[3]; + < + < cut_in_diff = cut_in_on - cut_in_off; + < cut_out_diff = cut_out_off - cut_out_on; + < cut_in_diff_inv = 1.0 / (cut_in_diff); + < cut_out_diff_inv = 1.0 / (cut_out_diff); + < cut_in_off_sq = cut_in_off*cut_in_off; + < cut_in_on_sq = cut_in_on*cut_in_on; + < cut_out_on_sq = cut_out_on*cut_out_on; + < cut_out_off_sq = cut_out_off*cut_out_off; + + */ + +template +void PairLJCharmmfswCoulLongKokkos::init_style() +{ + PairLJCharmmfswCoulLong::init_style(); + + Kokkos::deep_copy(d_cut_ljsq,cut_ljsq); + Kokkos::deep_copy(d_cut_coulsq,cut_coulsq); + + // error if rRESPA with inner levels + + if (update->whichflag == 1 && utils::strmatch(update->integrate_style,"^respa")) { + int respa = 0; + if (((Respa *) update->integrate)->level_inner >= 0) respa = 1; + if (((Respa *) update->integrate)->level_middle >= 0) respa = 2; + if (respa) + error->all(FLERR,"Cannot use Kokkos pair style with rRESPA inner/middle"); + } + + // adjust neighbor list request for KOKKOS + + neighflag = lmp->kokkos->neighflag; + auto request = neighbor->find_request(this); + request->set_kokkos_host(std::is_same_v && + !std::is_same_v); + request->set_kokkos_device(std::is_same_v); + if (neighflag == FULL) request->enable_full(); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +template +double PairLJCharmmfswCoulLongKokkos::init_one(int i, int j) +{ + double cutone = PairLJCharmmfswCoulLong::init_one(i,j); + + k_params.h_view(i,j).lj1 = lj1[i][j]; + k_params.h_view(i,j).lj2 = lj2[i][j]; + k_params.h_view(i,j).lj3 = lj3[i][j]; + k_params.h_view(i,j).lj4 = lj4[i][j]; + //k_params.h_view(i,j).offset = offset[i][j]; + k_params.h_view(i,j).cut_ljsq = cut_ljsq; + k_params.h_view(i,j).cut_coulsq = cut_coulsq; + + k_params.h_view(j,i) = k_params.h_view(i,j); + if (i(); + k_params.template modify(); + + return cutone; +} + +namespace LAMMPS_NS { +template class PairLJCharmmfswCoulLongKokkos; +#ifdef LMP_KOKKOS_GPU +template class PairLJCharmmfswCoulLongKokkos; +#endif +} + + + + +/* + 80d105 + < memory->destroy(offset); + 598c650 + < void PairLJCharmmCoulLong::allocate() + --- + > void PairLJCharmmfswCoulLong::allocate() + 622d673 + < memory->create(offset,n+1,n+1,"pair:offset"); + 631c682 + < void PairLJCharmmCoulLong::settings(int narg, char **arg) + --- + > void PairLJCharmmfswCoulLong::settings(int narg, char **arg) + 645c696 + < void PairLJCharmmCoulLong::coeff(int narg, char **arg) + --- + > void PairLJCharmmfswCoulLong::coeff(int narg, char **arg) + 752c801 + < double PairLJCharmmCoulLong::init_one(int i, int j) + --- + > double PairLJCharmmfswCoulLong::init_one(int i, int j) + 790c839 + < void PairLJCharmmCoulLong::write_restart(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::write_restart(FILE *fp) + 811c860 + < void PairLJCharmmCoulLong::read_restart(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::read_restart(FILE *fp) + 842c891 + < void PairLJCharmmCoulLong::write_restart_settings(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::write_restart_settings(FILE *fp) + 857c906 + < void PairLJCharmmCoulLong::read_restart_settings(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::read_restart_settings(FILE *fp) + 882c931 + < void PairLJCharmmCoulLong::write_data(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::write_data(FILE *fp) + 893c942 + < void PairLJCharmmCoulLong::write_data_all(FILE *fp) + --- + > void PairLJCharmmfswCoulLong::write_data_all(FILE *fp) + 903c952 + < double PairLJCharmmCoulLong::single(int i, int j, int itype, int jtype, + --- + > double PairLJCharmmfswCoulLong::single(int i, int j, int itype, int jtype, + 908,909c957,958 + < double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; + < double switch1,switch2,fraction,table,forcecoul,forcelj,phicoul,philj; + --- + > double r,rinv,r2inv,r3inv,r6inv,grij,expm2,t,erfc,prefactor; + > double switch1,fraction,table,forcecoul,forcelj,phicoul,philj,philj12,philj6; + 911a961,962 + > r = sqrt(rsq); + > rinv = 1.0/r; + 939c990,991 + < r6inv = r2inv*r2inv*r2inv; + --- + > r3inv = rinv*rinv*rinv; + > r6inv = r3inv*r3inv; + 943,947c995,996 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + 965d1013 + < philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + 967,969c1015,1025 + < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < philj *= switch1; + --- + > philj12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * + > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); + > philj6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * + > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); + > philj = philj12 + philj6; + > } else { + > philj12 = r6inv*lj3[itype][jtype]*r6inv - + > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + > philj6 = -lj4[itype][jtype]*r6inv + + > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; + > philj = philj12 + philj6; + 979c1035 + < void *PairLJCharmmCoulLong::extract(const char *str, int &dim) + --- + > void *PairLJCharmmfswCoulLong::extract(const char *str, int &dim) + 988a1045,1047 + > + > // info extracted by dihedral_charmmfsw + > + 989a1049,1051 + > if (strcmp(str,"cut_lj_inner") == 0) return (void *) &cut_lj_inner; + > if (strcmp(str,"cut_lj") == 0) return (void *) &cut_lj; + > if (strcmp(str,"dihedflag") == 0) return (void *) &dihedflag; + + + */ + +// nothing to do for all these, inherited from PairLJCharmmfswCoulLong + + + + +/* + + 226c257 + < void PairLJCharmmCoulLong::compute_inner() + --- + > void PairLJCharmmfswCoulLong::compute_inner() + 248a280,286 + > double cut_out_on = cut_respa[0]; + > double cut_out_off = cut_respa[1]; + > + > double cut_out_diff = cut_out_off - cut_out_on; + > double cut_out_on_sq = cut_out_on*cut_out_on; + > double cut_out_off_sq = cut_out_off*cut_out_off; + > + 284c322 + < rsw = (sqrt(rsq) - cut_out_on)*cut_out_diff_inv; + --- + > rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + 303c341 + < void PairLJCharmmCoulLong::compute_middle() + --- + > void PairLJCharmmfswCoulLong::compute_middle() + 308c346 + < double philj,switch1,switch2; + --- + > double switch1; + 326a365,376 + > double cut_in_off = cut_respa[0]; + > double cut_in_on = cut_respa[1]; + > double cut_out_on = cut_respa[2]; + > double cut_out_off = cut_respa[3]; + > + > double cut_in_diff = cut_in_on - cut_in_off; + > double cut_out_diff = cut_out_off - cut_out_on; + > double cut_in_off_sq = cut_in_off*cut_in_off; + > double cut_in_on_sq = cut_in_on*cut_in_on; + > double cut_out_on_sq = cut_out_on*cut_out_on; + > double cut_out_off_sq = cut_out_off*cut_out_off; + > + 361,365c411,412 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + 370c417 + < rsw = (sqrt(rsq) - cut_in_off)*cut_in_diff_inv; + --- + > rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; + 374c421 + < rsw = (sqrt(rsq) - cut_out_on)*cut_out_diff_inv; + --- + > rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; + 393c440 + < void PairLJCharmmCoulLong::compute_outer(int eflag, int vflag) + --- + > void PairLJCharmmfswCoulLong::compute_outer(int eflag, int vflag) + 396c443 + < double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,ecoul,fpair; + --- + > double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,evdwl6,evdwl12,ecoul,fpair; + 398c445 + < double r,r2inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; + --- + > double r,rinv,r2inv,r3inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; + 400c447 + < double philj,switch1,switch2; + --- + > double switch1; + 422a470,476 + > double cut_in_off = cut_respa[2]; + > double cut_in_on = cut_respa[3]; + > + > double cut_in_diff = cut_in_on - cut_in_off; + > double cut_in_off_sq = cut_in_off*cut_in_off; + > double cut_in_on_sq = cut_in_on*cut_in_on; + > + 448a503 + > r6inv = r2inv*r2inv*r2inv; + 489d543 + < r6inv = r2inv*r2inv*r2inv; + 493,497c547,548 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + 533d583 + < r6inv = r2inv*r2inv*r2inv; + 536,538c586,598 + < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < evdwl *= switch1; + --- + > rinv = sqrt(r2inv); + > r3inv = r2inv*rinv; + > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * + > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); + > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * + > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); + > evdwl = evdwl12 + evdwl6; + > } else { + > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - + > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + > evdwl6 = -lj4[itype][jtype]*r6inv + + > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; + > evdwl = evdwl12 + evdwl6; + 561d620 + < r6inv = r2inv*r2inv*r2inv; + 565,569c624,625 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + 572d627 + < r6inv = r2inv*r2inv*r2inv; + 576,580c631,632 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + + */ + +// kokkos doesnt support respa, so ignore compute_inner / compute_middle / compute_outer diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h new file mode 100644 index 0000000000..e9a6b5486f --- /dev/null +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h @@ -0,0 +1,230 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------- + + *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** + + (1) first draft version of PairLJCharmmfswCoulLongKokkos exactly + same as PairLJCharmmCoulLongKokkos but with new class name + + method: track changes from serial kspace pair_lj_charmm_coul_long to + pair_lj_charmmfsw_coul_long and apply to PairLJCharmmfswCoulLongKokkos + + % diff pair_lj_charmm_coul_long.h pair_lj_charmmfsw_coul_long.h + + +------------------------------------------------------------------------- */ + +/* + 16c16 + < PairStyle(lj/charmm/coul/long,PairLJCharmmCoulLong); + --- + > PairStyle(lj/charmmfsw/coul/long,PairLJCharmmfswCoulLong); + + */ + +#ifdef PAIR_CLASS +// clang-format off +PairStyle(lj/charmmfsw/coul/long/kk,PairLJCharmmfswCoulLongKokkos); +PairStyle(lj/charmmfsw/coul/long/kk/device,PairLJCharmmfswCoulLongKokkos); +PairStyle(lj/charmmfsw/coul/long/kk/host,PairLJCharmmfswCoulLongKokkos); +// clang-format on +#else + +/* + + 20,21c20,21 + < #ifndef LMP_PAIR_LJ_CHARMM_COUL_LONG_H + < #define LMP_PAIR_LJ_CHARMM_COUL_LONG_H + --- + > #ifndef LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_H + > #define LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_H + + */ + +// clang-format off +#ifndef LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_KOKKOS_H +#define LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_KOKKOS_H + +#include "pair_kokkos.h" +#include "pair_lj_charmmfsw_coul_long.h" +#include "neigh_list_kokkos.h" + +namespace LAMMPS_NS { + +/* + + 27c27 +< class PairLJCharmmCoulLong : public Pair { +--- +> class PairLJCharmmfswCoulLong : public Pair { + + */ + +template +class PairLJCharmmfswCoulLongKokkos : public PairLJCharmmfswCoulLong { + public: + enum {EnabledNeighFlags=FULL|HALFTHREAD|HALF}; + enum {COUL_FLAG=1}; + typedef DeviceType device_type; + typedef ArrayTypes AT; + + /* + + 29,30c29,30 + < PairLJCharmmCoulLong(class LAMMPS *); + < ~PairLJCharmmCoulLong() override; + --- + > PairLJCharmmfswCoulLong(class LAMMPS *); + > ~PairLJCharmmfswCoulLong() override; + + */ + + PairLJCharmmfswCoulLongKokkos(class LAMMPS *); + ~PairLJCharmmfswCoulLongKokkos() override; + + void compute(int, int) override; + + void init_tables(double cut_coul, double *cut_respa) override; + void init_style() override; + double init_one(int, int) override; + + protected: + + /* + 52c52,54 + < double cut_lj_inner, cut_lj; + --- + > int dihedflag; + > + > double cut_lj_inner, cut_lj, cut_ljinv, cut_lj_innerinv; + 53a56,57 + > double cut_lj3inv, cut_lj_inner3inv, cut_lj3, cut_lj_inner3; + > double cut_lj6inv, cut_lj_inner6inv, cut_lj6, cut_lj_inner6; + 56,60c60 + < double cut_in_off, cut_in_on, cut_out_off, cut_out_on; + < double cut_in_diff, cut_out_diff; + < double cut_in_diff_inv, cut_out_diff_inv; + < double cut_in_off_sq, cut_in_on_sq, cut_out_off_sq, cut_out_on_sq; + < double denom_lj, denom_lj_inv; + --- + > double denom_lj, denom_lj12, denom_lj6; + + */ + + // almost nothing to do here, inherited from PairLJCharmmfswCoulLong + // only temporarily need cut_lj_innersq, denom_coul protected variables + // (removed from pair_lj_charmm_coul_long to pair_lj_charmmfsw_coul_long) + // to compile draft version 1, can be removed by draft version 2 + + + + template + KOKKOS_INLINE_FUNCTION + F_FLOAT compute_fpair(const F_FLOAT& rsq, const int& i, const int&j, + const int& itype, const int& jtype) const; + + template + KOKKOS_INLINE_FUNCTION + F_FLOAT compute_fcoul(const F_FLOAT& rsq, const int& i, const int&j, const int& itype, + const int& jtype, const F_FLOAT& factor_coul, const F_FLOAT& qtmp) const; + + template + KOKKOS_INLINE_FUNCTION + F_FLOAT compute_evdwl(const F_FLOAT& rsq, const int& i, const int&j, + const int& itype, const int& jtype) const; + + template + KOKKOS_INLINE_FUNCTION + F_FLOAT compute_ecoul(const F_FLOAT& rsq, const int& i, const int&j, + const int& itype, const int& jtype, const F_FLOAT& factor_coul, const F_FLOAT& qtmp) const; + + Kokkos::DualView k_params; + typename Kokkos::DualView::t_dev_const_um params; + // hardwired to space for 12 atom types + params_lj_coul m_params[MAX_TYPES_STACKPARAMS+1][MAX_TYPES_STACKPARAMS+1]; + + F_FLOAT m_cutsq[MAX_TYPES_STACKPARAMS+1][MAX_TYPES_STACKPARAMS+1]; + F_FLOAT m_cut_ljsq[MAX_TYPES_STACKPARAMS+1][MAX_TYPES_STACKPARAMS+1]; + F_FLOAT m_cut_coulsq[MAX_TYPES_STACKPARAMS+1][MAX_TYPES_STACKPARAMS+1]; + typename AT::t_x_array_randomread x; + typename AT::t_x_array c_x; + typename AT::t_f_array f; + typename AT::t_int_1d_randomread type; + typename AT::t_float_1d_randomread q; + + DAT::tdual_efloat_1d k_eatom; + DAT::tdual_virial_array k_vatom; + typename AT::t_efloat_1d d_eatom; + typename AT::t_virial_array d_vatom; + + int newton_pair; + + typename AT::tdual_ffloat_2d k_cutsq; + typename AT::t_ffloat_2d d_cutsq; + typename AT::t_ffloat_2d d_cut_ljsq; + typename AT::t_ffloat_2d d_cut_coulsq; + + typename AT::t_ffloat_1d_randomread + d_rtable, d_drtable, d_ftable, d_dftable, + d_ctable, d_dctable, d_etable, d_detable; + + int neighflag; + int nlocal,nall,eflag,vflag; + + double special_coul[4]; + double special_lj[4]; + double qqrd2e; + + void allocate() override; + + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute>(PairLJCharmmfswCoulLongKokkos*, + NeighListKokkos*); + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend struct PairComputeFunctor>; + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute_neighlist>(PairLJCharmmfswCoulLongKokkos*,NeighListKokkos*); + friend EV_FLOAT pair_compute>(PairLJCharmmfswCoulLongKokkos*, + NeighListKokkos*); + friend void pair_virial_fdotr_compute(PairLJCharmmfswCoulLongKokkos*); + +}; + +} + +#endif +#endif + From c1446ddd92882760d1478299ffea8f1be71a04c8 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Tue, 2 Jan 2024 11:02:59 -0500 Subject: [PATCH 142/305] improve error messages --- src/compute_pair.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compute_pair.cpp b/src/compute_pair.cpp index 2788b632d2..e789adbc89 100644 --- a/src/compute_pair.cpp +++ b/src/compute_pair.cpp @@ -30,7 +30,7 @@ enum { EPAIR, EVDWL, ECOUL }; ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), pstyle(nullptr), pair(nullptr), one(nullptr) { - if (narg < 4) error->all(FLERR, "Illegal compute pair command"); + if (narg < 4) utils::missing_cmd_args(FLERR, "compute pair", error); scalar_flag = 1; extscalar = 1; @@ -63,7 +63,7 @@ ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : else if (strcmp(arg[iarg], "ecoul") == 0) evalue = ECOUL; else - error->all(FLERR, "Illegal compute pair command"); + error->all(FLERR, "Unknown compute pair keyword {}", arg[iarg]); ++iarg; } @@ -75,7 +75,7 @@ ComputePair::ComputePair(LAMMPS *lmp, int narg, char **arg) : pair = force->pair_match(pstyle, 1, nsub); } - if (!pair) error->all(FLERR, "Unrecognized pair style in compute pair command"); + if (!pair) error->all(FLERR, "Unrecognized pair style {} in compute pair command", pstyle); npair = pair->nextra; if (npair) { @@ -104,7 +104,7 @@ void ComputePair::init() // recheck for pair style in case it has been deleted pair = force->pair_match(pstyle, 1, nsub); - if (!pair) error->all(FLERR, "Unrecognized pair style in compute pair command"); + if (!pair) error->all(FLERR, "Unrecognized pair style {} in compute pair command", pstyle); } /* ---------------------------------------------------------------------- */ From 3a1d3bb64d604f48e76a930e88eefd9de062b472 Mon Sep 17 00:00:00 2001 From: Mitch Murphy Date: Wed, 3 Jan 2024 02:42:15 -0500 Subject: [PATCH 143/305] second draft... applied changes to compute methods --- src/KOKKOS/dihedral_charmmfsw_kokkos.cpp | 95 ++++---- src/KOKKOS/dihedral_charmmfsw_kokkos.h | 28 +-- .../pair_lj_charmmfsw_coul_long_kokkos.cpp | 206 +++++++++--------- 3 files changed, 169 insertions(+), 160 deletions(-) diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp index facb723580..b309f3d97f 100644 --- a/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp @@ -18,7 +18,7 @@ - Stan Moore (SNL) original DihedralCharmmfswKokkos - - Mitch Murphy (alphataubio) - DihedralCharmmfswKokkos update (2023/12) + - Mitch Murphy (alphataubio) - DihedralCharmmfswKokkos update (2024/01) Based on serial dihedral_charmmfsw.cpp lj-fsw sections (force-switched) provided by Robert Meissner and Lucio Colombi Ciacchi of Bremen @@ -138,21 +138,7 @@ DihedralCharmmfswKokkos::~DihedralCharmmfswKokkos() 259a269,270 > else > forcecoul = qqrd2e * q[i1] * q[i4] * (sqrt(r2inv) - r * cut_coulinv14 * cut_coulinv14); - 264,265c275,284 - < ecoul = weight[type] * forcecoul; - < evdwl = r6inv * (lj14_3[itype][jtype] * r6inv - lj14_4[itype][jtype]); - --- - > if (dihedflag) - > ecoul = weight[type] * forcecoul; - > else - > ecoul = weight[type] * qqrd2e * q[i1] * q[i4] * - > (sqrt(r2inv) + r * cut_coulinv14 * cut_coulinv14 - 2.0 * cut_coulinv14); - > evdwl14_12 = r6inv * lj14_3[itype][jtype] * r6inv - - > lj14_3[itype][jtype] * cut_lj_inner6inv * cut_lj6inv; - > evdwl14_6 = - > -lj14_4[itype][jtype] * r6inv + lj14_4[itype][jtype] * cut_lj_inner3inv * cut_lj3inv; - > evdwl = evdwl14_12 + evdwl14_6; - + */ @@ -225,15 +211,15 @@ void DihedralCharmmfswKokkos::compute(int eflag_in, int vflag_in) if (evflag) { if (newton_bond) { - Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); + Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); } else { - Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); + Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,ndihedrallist),*this,evm); } } else { if (newton_bond) { - Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); + Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); } else { - Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); + Kokkos::parallel_for(Kokkos::RangePolicy >(0,ndihedrallist),*this); } } @@ -303,7 +289,7 @@ void DihedralCharmmfswKokkos::compute(int eflag_in, int vflag_in) template template KOKKOS_INLINE_FUNCTION -void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute, const int &n, EVM_FLOAT& evm) const { +void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmfswCompute, const int &n, EVM_FLOAT& evm) const { // The f array is atomic Kokkos::View::value,Kokkos::MemoryTraits > a_f = f; @@ -480,11 +466,38 @@ void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute if (dihedflag) + > ecoul = weight[type] * forcecoul; + > else + > ecoul = weight[type] * qqrd2e * q[i1] * q[i4] * + > (sqrt(r2inv) + r * cut_coulinv14 * cut_coulinv14 - 2.0 * cut_coulinv14); + > evdwl14_12 = r6inv * lj14_3[itype][jtype] * r6inv - + > lj14_3[itype][jtype] * cut_lj_inner6inv * cut_lj6inv; + > evdwl14_6 = + > -lj14_4[itype][jtype] * r6inv + lj14_4[itype][jtype] * cut_lj_inner3inv * cut_lj3inv; + > evdwl = evdwl14_12 + evdwl14_6; + */ + + const F_FLOAT r = sqrt(rsq); F_FLOAT ecoul = 0.0; F_FLOAT evdwl = 0.0; + F_FLOAT evdwl14_12, evdwl14_6; if (eflag) { - ecoul = d_weight[type] * forcecoul; - evdwl = r6inv * (d_lj14_3(itype,jtype)*r6inv - d_lj14_4(itype,jtype)); + if (dihedflag) + ecoul = d_weight[type] * forcecoul; + else + ecoul = d_weight[type] * qqrd2e * q[i1] * q[i4] * + (sqrt(r2inv) + r * cut_coulinv14 * cut_coulinv14 - 2.0 * cut_coulinv14); + evdwl14_12 = r6inv * d_lj14_3(itype,jtype) * r6inv - + d_lj14_3(itype,jtype) * cut_lj_inner6inv * cut_lj6inv; + evdwl14_6 = + -d_lj14_4(itype,jtype) * r6inv + d_lj14_4(itype,jtype) * cut_lj_inner3inv * cut_lj3inv; + evdwl = evdwl14_12 + evdwl14_6; evdwl *= d_weight[type]; } @@ -506,9 +519,9 @@ void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute template KOKKOS_INLINE_FUNCTION -void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmCompute, const int &n) const { +void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmfswCompute, const int &n) const { EVM_FLOAT evm; - this->template operator()(TagDihedralCharmmCompute(), n, evm); + this->template operator()(TagDihedralCharmmfswCompute(), n, evm); } /* ---------------------------------------------------------------------- */ @@ -547,12 +560,12 @@ void DihedralCharmmfswKokkos::coeff(int narg, char **arg) DihedralCharmmfsw::coeff(narg, arg); int nd = atom->ndihedraltypes; - typename AT::tdual_ffloat_1d k_k("DihedralCharmm::k",nd+1); - typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmm::multiplicity",nd+1); - typename AT::tdual_ffloat_1d k_shift("DihedralCharmm::shift",nd+1); - typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmm::cos_shift",nd+1); - typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmm::sin_shift",nd+1); - typename AT::tdual_ffloat_1d k_weight("DihedralCharmm::weight",nd+1); + typename AT::tdual_ffloat_1d k_k("DihedralCharmmfsw::k",nd+1); + typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmmfsw::multiplicity",nd+1); + typename AT::tdual_ffloat_1d k_shift("DihedralCharmmfsw::shift",nd+1); + typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmmfsw::cos_shift",nd+1); + typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmmfsw::sin_shift",nd+1); + typename AT::tdual_ffloat_1d k_weight("DihedralCharmmfsw::weight",nd+1); d_k = k_k.template view(); d_multiplicity = k_multiplicity.template view(); @@ -630,10 +643,10 @@ void DihedralCharmmfswKokkos::init_style() DihedralCharmmfsw::init_style(); int n = atom->ntypes; - DAT::tdual_ffloat_2d k_lj14_1("DihedralCharmm:lj14_1",n+1,n+1); - DAT::tdual_ffloat_2d k_lj14_2("DihedralCharmm:lj14_2",n+1,n+1); - DAT::tdual_ffloat_2d k_lj14_3("DihedralCharmm:lj14_3",n+1,n+1); - DAT::tdual_ffloat_2d k_lj14_4("DihedralCharmm:lj14_4",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_1("DihedralCharmmfsw:lj14_1",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_2("DihedralCharmmfsw:lj14_2",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_3("DihedralCharmmfsw:lj14_3",n+1,n+1); + DAT::tdual_ffloat_2d k_lj14_4("DihedralCharmmfsw:lj14_4",n+1,n+1); d_lj14_1 = k_lj14_1.template view(); d_lj14_2 = k_lj14_2.template view(); @@ -682,12 +695,12 @@ void DihedralCharmmfswKokkos::read_restart(FILE *fp) DihedralCharmmfsw::read_restart(fp); int nd = atom->ndihedraltypes; - typename AT::tdual_ffloat_1d k_k("DihedralCharmm::k",nd+1); - typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmm::multiplicity",nd+1); - typename AT::tdual_ffloat_1d k_shift("DihedralCharmm::shift",nd+1); - typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmm::cos_shift",nd+1); - typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmm::sin_shift",nd+1); - typename AT::tdual_ffloat_1d k_weight("DihedralCharmm::weight",nd+1); + typename AT::tdual_ffloat_1d k_k("DihedralCharmmfsw::k",nd+1); + typename AT::tdual_ffloat_1d k_multiplicity("DihedralCharmmfsw::multiplicity",nd+1); + typename AT::tdual_ffloat_1d k_shift("DihedralCharmmfsw::shift",nd+1); + typename AT::tdual_ffloat_1d k_cos_shift("DihedralCharmmfsw::cos_shift",nd+1); + typename AT::tdual_ffloat_1d k_sin_shift("DihedralCharmmfsw::sin_shift",nd+1); + typename AT::tdual_ffloat_1d k_weight("DihedralCharmmfsw::weight",nd+1); d_k = k_k.template view(); d_multiplicity = k_multiplicity.template view(); diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.h b/src/KOKKOS/dihedral_charmmfsw_kokkos.h index 413945826f..8b57b28d0c 100644 --- a/src/KOKKOS/dihedral_charmmfsw_kokkos.h +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.h @@ -78,14 +78,6 @@ DihedralStyle(charmmfsw/kk/host,DihedralCharmmfswKokkos); /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here struct s_EVM_FLOAT { ^ - In file included from /Users/mitch/Dropbox/lammps/lammps/src/force.cpp:18: - In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:104:8: error: redefinition of 'TagDihedralCharmmCompute' - struct TagDihedralCharmmCompute{}; - ^ - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:70:8: note: previous definition is here - struct TagDihedralCharmmCompute{}; - ^ In file included from /Users/mitch/Dropbox/lammps/lammps/src/lammps.cpp:23: In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:65:8: error: redefinition of 's_EVM_FLOAT' @@ -94,14 +86,6 @@ DihedralStyle(charmmfsw/kk/host,DihedralCharmmfswKokkos); /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here struct s_EVM_FLOAT { ^ - In file included from /Users/mitch/Dropbox/lammps/lammps/src/lammps.cpp:23: - In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:104:8: error: redefinition of 'TagDihedralCharmmCompute' - struct TagDihedralCharmmCompute{}; - ^ - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:70:8: note: previous definition is here - struct TagDihedralCharmmCompute{}; - ^ */ @@ -146,10 +130,12 @@ struct s_EVM_FLOAT { }; typedef struct s_EVM_FLOAT EVM_FLOAT; -template -struct TagDihedralCharmmCompute{}; + */ + +template +struct TagDihedralCharmmfswCompute{}; + -*/ /* 27c27 @@ -181,11 +167,11 @@ class DihedralCharmmfswKokkos : public DihedralCharmmfsw { template KOKKOS_INLINE_FUNCTION - void operator()(TagDihedralCharmmCompute, const int&, EVM_FLOAT&) const; + void operator()(TagDihedralCharmmfswCompute, const int&, EVM_FLOAT&) const; template KOKKOS_INLINE_FUNCTION - void operator()(TagDihedralCharmmCompute, const int&) const; + void operator()(TagDihedralCharmmfswCompute, const int&) const; //template KOKKOS_INLINE_FUNCTION diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp index 88efec5fda..7c1da2479f 100644 --- a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp @@ -18,7 +18,7 @@ - Ray Shan (SNL) - original PairLJCharmmCoulLongKokkos - - Mitch Murphy (alphataubio) - PairLJCharmmfswCoulLongKokkos update (2023/12) + - Mitch Murphy (alphataubio) - PairLJCharmmfswCoulLongKokkos update (2024/01) Based on serial kspace lj-fsw sections (force-switched) provided by Robert Meissner and Lucio Colombi Ciacchi of Bremen University, Germany, @@ -113,9 +113,6 @@ using namespace LAMMPS_NS; template PairLJCharmmfswCoulLongKokkos::PairLJCharmmfswCoulLongKokkos(LAMMPS *lmp):PairLJCharmmfswCoulLong(lmp) { - - // pair_lj_charmmfsw_coul_long_kokkos.cpp:112:28: error: qualified reference to 'PairLJCharmmfswCoulLong' is a constructor name rather than a type in this context - // ??? PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(lmp); respa_enable = 0; @@ -147,15 +144,10 @@ PairLJCharmmfswCoulLongKokkos::PairLJCharmmfswCoulLongKokkos(LAMMPS */ -// added superclass constructor to inherit from PairLJCharmmfswCoulLong - template PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() { - // pair_lj_charmmfsw_coul_long_kokkos.cpp:150:28: error: qualified reference to 'PairLJCharmmfswCoulLong' is a constructor name rather than a type in this context - // ??? PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(); - if (copymode) return; if (allocated) { @@ -186,39 +178,7 @@ PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() > double switch1; 96d120 < double rsq; - 174,179c198,200 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - < } - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - > } - 205d225 - < evdwl = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); - 207,209c227,240 - < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < evdwl *= switch1; - --- - > r = sqrt(rsq); - > rinv = 1.0/r; - > r3inv = rinv*rinv*rinv; - > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * - > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); - > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * - > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); - > evdwl = evdwl12 + evdwl6; - > } else { - > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - - > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; - > evdwl6 = -lj4[itype][jtype]*r6inv + - > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; - > evdwl = evdwl12 + evdwl6; - + */ template @@ -320,20 +280,32 @@ compute_fpair(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, const int& itype, const int& jtype) const { const F_FLOAT r2inv = 1.0/rsq; const F_FLOAT r6inv = r2inv*r2inv*r2inv; - F_FLOAT forcelj, switch1, switch2, englj; + F_FLOAT forcelj, switch1; forcelj = r6inv * ((STACKPARAMS?m_params[itype][jtype].lj1:params(itype,jtype).lj1)*r6inv - (STACKPARAMS?m_params[itype][jtype].lj2:params(itype,jtype).lj2)); if (rsq > cut_lj_innersq) { + + /* + 174,179c198,200 + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < switch2 = 12.0*rsq * (cut_ljsq-rsq) * + < (rsq-cut_lj_innersq) * denom_lj_inv; + < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); + < forcelj = forcelj*switch1 + philj*switch2; + < } + --- + > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; + > forcelj = forcelj*switch1; + > } + + */ + switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - switch2 = 12.0*rsq * (cut_ljsq-rsq) * (rsq-cut_lj_innersq) / denom_lj; - englj = r6inv * - ((STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*r6inv - - (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)); - forcelj = forcelj*switch1 + englj*switch2; + forcelj = forcelj*switch1; } return forcelj*r2inv; @@ -350,20 +322,52 @@ compute_evdwl(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, const int& itype, const int& jtype) const { const F_FLOAT r2inv = 1.0/rsq; const F_FLOAT r6inv = r2inv*r2inv*r2inv; - F_FLOAT englj, switch1; + const F_FLOAT r = sqrt(rsq); + const F_FLOAT rinv = 1.0/r; + const F_FLOAT r3inv = rinv*rinv*rinv; + F_FLOAT englj, englj12, englj6; + + /* + 205d225 + < evdwl = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); + 207,209c227,240 + < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * + < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; + < evdwl *= switch1; + --- + > r = sqrt(rsq); + > rinv = 1.0/r; + > r3inv = rinv*rinv*rinv; + > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * + > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); + > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * + > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); + > evdwl = evdwl12 + evdwl6; + > } else { + > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - + > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + > evdwl6 = -lj4[itype][jtype]*r6inv + + > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; + > evdwl = evdwl12 + evdwl6; + + */ - englj = r6inv * - ((STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*r6inv - - (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)); if (rsq > cut_lj_innersq) { - switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * - (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - englj *= switch1; + englj12 = (STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*cut_lj6* + denom_lj12 * (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); + englj6 = -(STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)* + cut_lj3*denom_lj6 * (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); + englj = englj12 + englj6; + } else { + englj12 = r6inv*lj3[itype][jtype]*r6inv - + lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + englj6 = -(STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)*r6inv + + (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)* + cut_lj_inner3inv*cut_lj3inv; + englj = englj12 + englj6; } - return englj; - } /* ---------------------------------------------------------------------- @@ -458,7 +462,7 @@ void PairLJCharmmfswCoulLongKokkos::allocate() d_cut_coulsq = typename AT::t_ffloat_2d("pair:cut_coulsq",n+1,n+1); - k_params = Kokkos::DualView("PairLJCharmmCoulLong::params",n+1,n+1); + k_params = Kokkos::DualView("PairLJCharmmfswCoulLong::params",n+1,n+1); params = k_params.template view(); } @@ -574,49 +578,11 @@ void PairLJCharmmfswCoulLongKokkos::init_tables(double cut_coul, dou < void PairLJCharmmCoulLong::init_style() --- > void PairLJCharmmfswCoulLong::init_style() - 686c737 - < "Pair style lj/charmm/coul/long requires atom attribute q"); - --- - > "Pair style lj/charmmfsw/coul/long requires atom attribute q"); - 688c739 + 688c739 < // request regular or rRESPA neighbor list --- > // request regular or rRESPA neighbor lists - 705a757,766 - > cut_ljinv = 1.0/cut_lj; - > cut_lj_innerinv = 1.0/cut_lj_inner; - > cut_lj3 = cut_lj * cut_lj * cut_lj; - > cut_lj3inv = cut_ljinv * cut_ljinv * cut_ljinv; - > cut_lj_inner3inv = cut_lj_innerinv * cut_lj_innerinv * cut_lj_innerinv; - > cut_lj_inner3 = cut_lj_inner * cut_lj_inner * cut_lj_inner; - > cut_lj6 = cut_ljsq * cut_ljsq * cut_ljsq; - > cut_lj6inv = cut_lj3inv * cut_lj3inv; - > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; - > cut_lj_inner6 = cut_lj_innersq * cut_lj_innersq * cut_lj_innersq; - 709,711c770,773 - < denom_lj = ( (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * - < (cut_ljsq-cut_lj_innersq) ); - < denom_lj_inv = 1.0 / denom_lj; - --- - > denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * - > (cut_ljsq-cut_lj_innersq); - > denom_lj12 = 1.0/(cut_lj6 - cut_lj_inner6); - > denom_lj6 = 1.0/(cut_lj3 - cut_lj_inner3); - 718,730d779 - < cut_in_off = cut_respa[0]; - < cut_in_on = cut_respa[1]; - < cut_out_on = cut_respa[2]; - < cut_out_off = cut_respa[3]; - < - < cut_in_diff = cut_in_on - cut_in_off; - < cut_out_diff = cut_out_off - cut_out_on; - < cut_in_diff_inv = 1.0 / (cut_in_diff); - < cut_out_diff_inv = 1.0 / (cut_out_diff); - < cut_in_off_sq = cut_in_off*cut_in_off; - < cut_in_on_sq = cut_in_on*cut_in_on; - < cut_out_on_sq = cut_out_on*cut_out_on; - < cut_out_off_sq = cut_out_off*cut_out_off; - + */ template @@ -689,6 +655,8 @@ template class PairLJCharmmfswCoulLongKokkos; + + /* 80d105 < memory->destroy(offset); @@ -706,6 +674,48 @@ template class PairLJCharmmfswCoulLongKokkos; < void PairLJCharmmCoulLong::coeff(int narg, char **arg) --- > void PairLJCharmmfswCoulLong::coeff(int narg, char **arg) + + 686c737 + < "Pair style lj/charmm/coul/long requires atom attribute q"); + --- + > "Pair style lj/charmmfsw/coul/long requires atom attribute q"); + + 705a757,766 + > cut_ljinv = 1.0/cut_lj; + > cut_lj_innerinv = 1.0/cut_lj_inner; + > cut_lj3 = cut_lj * cut_lj * cut_lj; + > cut_lj3inv = cut_ljinv * cut_ljinv * cut_ljinv; + > cut_lj_inner3inv = cut_lj_innerinv * cut_lj_innerinv * cut_lj_innerinv; + > cut_lj_inner3 = cut_lj_inner * cut_lj_inner * cut_lj_inner; + > cut_lj6 = cut_ljsq * cut_ljsq * cut_ljsq; + > cut_lj6inv = cut_lj3inv * cut_lj3inv; + > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; + > cut_lj_inner6 = cut_lj_innersq * cut_lj_innersq * cut_lj_innersq; + 709,711c770,773 + < denom_lj = ( (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + < (cut_ljsq-cut_lj_innersq) ); + < denom_lj_inv = 1.0 / denom_lj; + --- + > denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * + > (cut_ljsq-cut_lj_innersq); + > denom_lj12 = 1.0/(cut_lj6 - cut_lj_inner6); + > denom_lj6 = 1.0/(cut_lj3 - cut_lj_inner3); + 718,730d779 + < cut_in_off = cut_respa[0]; + < cut_in_on = cut_respa[1]; + < cut_out_on = cut_respa[2]; + < cut_out_off = cut_respa[3]; + < + < cut_in_diff = cut_in_on - cut_in_off; + < cut_out_diff = cut_out_off - cut_out_on; + < cut_in_diff_inv = 1.0 / (cut_in_diff); + < cut_out_diff_inv = 1.0 / (cut_out_diff); + < cut_in_off_sq = cut_in_off*cut_in_off; + < cut_in_on_sq = cut_in_on*cut_in_on; + < cut_out_on_sq = cut_out_on*cut_out_on; + < cut_out_off_sq = cut_out_off*cut_out_off; + + 752c801 < double PairLJCharmmCoulLong::init_one(int i, int j) --- From 817c7bac18ab8d126d83a9670a2619e53af1999b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 10:11:30 -0700 Subject: [PATCH 144/305] Optimize Kokkos PACE pair style for GPUs --- src/KOKKOS/pair_pace_kokkos.cpp | 669 ++++++++++++++++++++------------ src/KOKKOS/pair_pace_kokkos.h | 52 +-- 2 files changed, 442 insertions(+), 279 deletions(-) diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 805d7f68bb..85fd458298 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -104,7 +104,8 @@ void PairPACEKokkos::grow(int natom, int maxneigh) if ((int)A.extent(0) < natom) { - MemKK::realloc_kokkos(A, "pace:A", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(A_sph, "pace:A_sph", natom, nelements, idx_sph_max, nradmax + 1); + MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_rho_max, basis_set->rankmax); @@ -115,7 +116,7 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion MemKK::realloc_kokkos(dF_drho, "pace:dF_drho", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion - MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, idx_sph_max, nradmax + 1); MemKK::realloc_kokkos(weights_rank1, "pace:weights_rank1", natom, nelements, nradbase); // hard-core repulsion @@ -129,11 +130,11 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_rho_max, basis_set->rankmax); } - if (((int)ylm.extent(0) < natom) || ((int)ylm.extent(1) < maxneigh)) { + if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { // radial functions - MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, nradmax, lmax + 1); - MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, nradmax, lmax + 1); + MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, lmax + 1, nradmax); + MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, lmax + 1, nradmax); MemKK::realloc_kokkos(gr, "pace:gr", natom, maxneigh, nradbase); MemKK::realloc_kokkos(dgr, "pace:dgr", natom, maxneigh, nradbase); const int max_num_functions = MAX(nradbase, nradmax*(lmax + 1)); @@ -144,12 +145,6 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(cr, "pace:cr", natom, maxneigh); MemKK::realloc_kokkos(dcr, "pace:dcr", natom, maxneigh); - // spherical harmonics - MemKK::realloc_kokkos(plm, "pace:plm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dplm, "pace:dplm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(ylm, "pace:ylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dylm, "pace:dylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - // short neigh list MemKK::realloc_kokkos(d_ncount, "pace:ncount", natom); MemKK::realloc_kokkos(d_mu, "pace:mu", natom, maxneigh); @@ -443,6 +438,7 @@ void PairPACEKokkos::init_style() // spherical harmonics + MemKK::realloc_kokkos(d_idx_sph, "pace:idx_sph", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(alm, "pace:alm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(blm, "pace:blm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(cl, "pace:cl", lmax + 1); @@ -613,7 +609,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) Kokkos::deep_copy(weights, 0.0); Kokkos::deep_copy(weights_rank1, 0.0); - Kokkos::deep_copy(A, 0.0); + Kokkos::deep_copy(A_sph, 0.0); Kokkos::deep_copy(A_rank1, 0.0); Kokkos::deep_copy(rhos, 0.0); Kokkos::deep_copy(rho_core, 0.0); @@ -646,15 +642,6 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) Kokkos::parallel_for("ComputeRadial",policy_radial,*this); } - //ComputeYlm - { - int vector_length = vector_length_default; - int team_size = 16; - check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_ylm(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - Kokkos::parallel_for("ComputeYlm",policy_ylm,*this); - } - //ComputeAi { int vector_length = vector_length_default; @@ -693,7 +680,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) int vector_length = vector_length_default; int team_size = team_size_default; check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); + typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); Kokkos::parallel_for("ComputeDerivative",policy_derivative,*this); } @@ -898,28 +885,6 @@ void PairPACEKokkos::operator() (TagPairPACEComputeRadial, const typ /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::operator() (TagPairPACEComputeYlm, const typename Kokkos::TeamPolicy::member_type& team) const -{ - // Extract the atom number - int ii = team.team_rank() + team.team_size() * (team.league_rank() % - ((chunk_size+team.team_size()-1)/team.team_size())); - if (ii >= chunk_size) return; - - // Extract the neighbor number - const int jj = team.league_rank() / ((chunk_size+team.team_size()-1)/team.team_size()); - const int ncount = d_ncount(ii); - if (jj >= ncount) return; - - const double xn = d_rhats(ii, jj, 0); - const double yn = d_rhats(ii, jj, 1); - const double zn = d_rhats(ii, jj, 2); - compute_ylm(ii,jj,xn,yn,zn,lmax); -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typename Kokkos::TeamPolicy::member_type& team) const @@ -941,13 +906,127 @@ void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typenam Kokkos::atomic_add(&A_rank1(ii, mu_j, n), gr(ii, jj, n) * Y00); // rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - Kokkos::atomic_add(&A(ii, mu_j, n, idx).re, fr(ii, jj, n, l) * ylm(ii, jj, idx).re); - Kokkos::atomic_add(&A(ii, mu_j, n, idx).im, fr(ii, jj, n, l) * ylm(ii, jj, idx).im); + + // Compute plm and ylm + + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm, phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm = phase * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; } } @@ -961,17 +1040,35 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEConjugateAi, const int& ii) const { - //complex conjugate A's (for NEGATIVE (-m) terms) - // for rank > 1 for (int mu_j = 0; mu_j < nelements; mu_j++) { - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { + + // transpose + + int idx_sph = 0; + + for (int m = 0; m <= lmax; m++) { + for (int l = m; l <= lmax; l++) { + const int idx = l * (l + 1) + m; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idx, n) = A_sph(ii, mu_j, idx_sph, n); + } + + idx_sph++; + } + } + + // complex conjugate A's (for NEGATIVE (-m) terms) + // for rank > 1 + + for (int l = 0; l <= lmax; l++) { //fill in -m part in the outer loop using the same m <-> -m symmetry as for Ylm - for (int m = 1; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idxm = l * (l + 1) - m; // (l, -m) - const int factor = m % 2 == 0 ? 1 : -1; - A(ii, mu_j, n, idxm) = A(ii, mu_j, n, idx).conj() * (double)factor; + for (int m = 1; m <= l; m++) { + const int idx = l * (l + 1) + m; // (l, m) + const int idxm = l * (l + 1) - m; // (l, -m) + const int idx_sph = d_idx_sph(idx); + const int factor = m % 2 == 0 ? 1 : -1; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idxm, n) = A_sph(ii, mu_j, idx_sph, n).conj() * (double)factor; } } } @@ -1021,7 +1118,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeRho, const int& i const int l = d_ls(mu_i, offset, t); const int m = d_ms_combs(mu_i, idx_rho, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_rho, t) = A(ii, mu, n - 1, idx); + A_list(ii, idx_rho, t) = A(ii, mu, idx, n - 1); A_forward_prod(ii, idx_rho, t + 1) = A_forward_prod(ii, idx_rho, t) * A_list(ii, idx_rho, t); } @@ -1142,14 +1239,20 @@ void PairPACEKokkos::operator() (TagPairPACEComputeWeights, const in const int n_t = d_ns(mu_i, offset, t); const int l_t = d_ls(mu_i, offset, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) - const complex value = theta * dB; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).re), value.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).im), value.im); + const int idx_sph = d_idx_sph(idx); + if (idx_sph >= 0) { + const complex value = theta * dB; + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).re), value.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).im), value.im); + } // update -m_t (that could also be positive), because the basis is half_basis const int idxm = l_t * (l_t + 1) - m_t; // (l, -m) - const complex valuem = theta * dB.conj() * (double)factor; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).re), valuem.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).im), valuem.im); + const int idxm_sph = d_idx_sph(idxm); + if (idxm_sph >= 0) { + const complex valuem = theta * dB.conj() * (double)factor; + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).re), valuem.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).im), valuem.im); + } } } } @@ -1196,37 +1299,239 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const } // for rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - const double R_over_r = fr(ii, jj, n, l) * rinv; - const double DR = dfr(ii, jj, n, l); - // for m >= 0 - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - complex w = weights(ii, mu_j, n, idx); + // compute plm, dplm, ylm and dylm + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm,dylm[3]; + complex phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + double dplm_idx,dplm_idx1,dplm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + dplm_idx = 0.0; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + dplm_idx = Y00 * sq3; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + dyz.re = dplm_idx; + rdy.re = dyz.re * rz; + + dylm[0].re = -rdy.re * rx; + dylm[0].im = 0.0; + dylm[1].re = -rdy.re * ry; + dylm[1].im = 0.0; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = 0; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + dplm_idx = 0.0; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm = phase * plm_idx; + + dyx.re = plm_idx; + dyx.im = 0.0; + dyy.re = 0.0; + dyy.im = plm_idx; + dyz.re = phase.re * dplm_idx; + dyz.im = phase.im * dplm_idx; + + rdy.re = rx * dyx.re + +rz * dyz.re; + rdy.im = ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = -rdy.im * rx; + dylm[1].re = -rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + // counting for -m cases if m > 0 + w.re *= 2.0; + w.im *= 2.0; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + dplm_idx = 0.0; + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + dyx = mphasem1 * plm_idx; + dyy.re = -dyx.im; + dyy.im = dyx.re; + dyz = phasem * dplm_idx; + + rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; + rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = dyx.im - rdy.im * rx; + dylm[1].re = dyy.re - rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); if (w.re == 0.0 && w.im == 0.0) continue; // counting for -m cases if m > 0 - if (m > 0) { - w.re *= 2.0; - w.im *= 2.0; - } - - complex DY[3]; - DY[0] = dylm(ii, jj, idx, 0); - DY[1] = dylm(ii, jj, idx, 1); - DY[2] = dylm(ii, jj, idx, 2); - const complex Y_DR = ylm(ii, jj, idx) * DR; + w.re *= 2.0; + w.im *= 2.0; complex grad_phi_nlm[3]; - grad_phi_nlm[0] = Y_DR * r_hat[0] + DY[0] * R_over_r; - grad_phi_nlm[1] = Y_DR * r_hat[1] + DY[1] * R_over_r; - grad_phi_nlm[2] = Y_DR * r_hat[2] + DY[2] * R_over_r; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; // real-part multiplication only f_ji[0] += w.real_part_product(grad_phi_nlm[0]); f_ji[1] += w.real_part_product(grad_phi_nlm[1]); f_ji[2] += w.real_part_product(grad_phi_nlm[2]); } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; } } @@ -1364,31 +1669,46 @@ void PairPACEKokkos::v_tally_xyz(EV_FLOAT &ev, const int &i, const i template void PairPACEKokkos::pre_compute_harmonics(int lmax) { + auto h_idx_sph = Kokkos::create_mirror_view(d_idx_sph); auto h_alm = Kokkos::create_mirror_view(alm); auto h_blm = Kokkos::create_mirror_view(blm); auto h_cl = Kokkos::create_mirror_view(cl); auto h_dl = Kokkos::create_mirror_view(dl); - for (int l = 1; l <= lmax; l++) { - const double lsq = l * l; - const double ld = 2 * l; - const double l1 = (4 * lsq - 1); - const double l2 = lsq - ld + 1; - for (int m = 0; m < l - 1; m++) { - const double msq = m * m; - const double a = sqrt((double(l1)) / (double(lsq - msq))); - const double b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + Kokkos::deep_copy(h_idx_sph,-1); + + int idx_sph = 0; + for (int m = 0; m <= lmax; m++) { + const double msq = m * m; + for (int l = m; l <= lmax; l++) { const int idx = l * (l + 1) + m; // (l, m) - h_alm(idx) = a; - h_blm(idx) = b; + h_idx_sph(idx) = idx_sph; + + double a = 0.0; + double b = 0.0; + + if (l > 1 && l != m) { + const double lsq = l * l; + const double ld = 2 * l; + const double l1 = (4 * lsq - 1); + const double l2 = lsq - ld + 1; + + a = sqrt((double(l1)) / (double(lsq - msq))); + b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + } + h_alm(idx_sph) = a; + h_blm(idx_sph) = b; + idx_sph++; } } + idx_sph_max = idx_sph; for (int l = 1; l <= lmax; l++) { h_cl(l) = -sqrt(1.0 + 0.5 / (double(l))); h_dl(l) = sqrt(double(2 * (l - 1) + 3)); } + Kokkos::deep_copy(d_idx_sph, h_idx_sph); Kokkos::deep_copy(alm, h_alm); Kokkos::deep_copy(blm, h_blm); Kokkos::deep_copy(cl, h_cl); @@ -1397,143 +1717,6 @@ void PairPACEKokkos::pre_compute_harmonics(int lmax) /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::compute_barplm(int ii, int jj, double rz, int lmax) const -{ - // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! - // prefactors include 1/sqrt(2) factor compared to reference - - // l=0, m=0 - // plm(ii, jj, 0, 0) = Y00/sq1o4pi; //= sq1o4pi; - plm(ii, jj, 0) = Y00; //= 1; - dplm(ii, jj, 0) = 0.0; - - if (lmax > 0) { - - // l=1, m=0 - plm(ii, jj, 2) = Y00 * sq3 * rz; - dplm(ii, jj, 2) = Y00 * sq3; - - // l=1, m=1 - plm(ii, jj, 3) = -sq3o2 * Y00; - dplm(ii, jj, 3) = 0.0; - - // loop l = 2, lmax - for (int l = 2; l <= lmax; l++) { - for (int m = 0; m < l - 1; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idx1 = (l - 1) * l + m; // (l - 1, m) - const int idx2 = (l - 2) * (l - 1) + m; // (l - 2, m) - plm(ii, jj, idx) = alm(idx) * (rz * plm(ii, jj, idx1) + blm(idx) * plm(ii, jj, idx2)); - dplm(ii, jj, idx) = alm(idx) * (plm(ii, jj, idx1) + rz * dplm(ii, jj, idx1) + blm(idx) * dplm(ii, jj, idx2)); - } - const int idx = l * (l + 1) + l; // (l, l) - const int idx1 = l * (l + 1) + l - 1; // (l, l - 1) - const int idx2 = (l - 1) * l + l - 1; // (l - 1, l - 1) - const double t = dl(l) * plm(ii, jj, idx2); - plm(ii, jj, idx1) = t * rz; - dplm(ii, jj, idx1) = t; - plm(ii, jj, idx) = cl(l) * plm(ii, jj, idx2); - dplm(ii, jj, idx) = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- */ - -template -KOKKOS_INLINE_FUNCTION -void PairPACEKokkos::compute_ylm(int ii, int jj, double rx, double ry, double rz, int lmax) const -{ - // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! - - complex phase; - complex phasem, mphasem1; - complex dyx, dyy, dyz; - complex rdy; - - phase.re = rx; - phase.im = ry; - - // compute barplm - compute_barplm(ii, jj, rz, lmax); - - // m = 0 - for (int l = 0; l <= lmax; l++) { - const int idx = l * (l + 1); - - ylm(ii, jj, idx).re = plm(ii, jj, idx); - ylm(ii, jj, idx).im = 0.0; - - dyz.re = dplm(ii, jj, idx); - rdy.re = dyz.re * rz; - - dylm(ii, jj, idx, 0).re = -rdy.re * rx; - dylm(ii, jj, idx, 0).im = 0.0; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = 0.0; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = 0; - } - // m = 1 - for (int l = 1; l <= lmax; l++) { - const int idx = l * (l + 1) + 1; - - ylm(ii, jj, idx) = phase * plm(ii, jj, idx); - - dyx.re = plm(ii, jj, idx); - dyx.im = 0.0; - dyy.re = 0.0; - dyy.im = plm(ii, jj, idx); - dyz.re = phase.re * dplm(ii, jj, idx); - dyz.im = phase.im * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + +rz * dyz.re; - rdy.im = ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = -rdy.im * rx; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - - // m > 1 - phasem = phase; - for (int m = 2; m <= lmax; m++) { - - mphasem1.re = phasem.re * double(m); - mphasem1.im = phasem.im * double(m); - phasem = phasem * phase; - - for (int l = m; l <= lmax; l++) { - const int idx = l * (l + 1) + m; - - ylm(ii, jj, idx).re = phasem.re * plm(ii, jj, idx); - ylm(ii, jj, idx).im = phasem.im * plm(ii, jj, idx); - - dyx = mphasem1 * plm(ii, jj, idx); - dyy.re = -dyx.im; - dyy.im = dyx.re; - dyz = phasem * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; - rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = dyx.im - rdy.im * rx; - dylm(ii, jj, idx, 1).re = dyy.re - rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - } -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::cutoff_func_poly(const double r, const double r_in, const double delta_in, double &fc, double &dfc) const @@ -1662,11 +1845,11 @@ void PairPACEKokkos::evaluate_splines(const int ii, const int jj, do spline_gk.calcSplines(ii, jj, r, gr, dgr); spline_rnl.calcSplines(ii, jj, r, d_values, d_derivatives); - for (int kk = 0; kk < (int)fr.extent(2); kk++) { - for (int ll = 0; ll < (int)fr.extent(3); ll++) { - const int flatten = kk*fr.extent(3) + ll; - fr(ii, jj, kk, ll) = d_values(ii, jj, flatten); - dfr(ii, jj, kk, ll) = d_derivatives(ii, jj, flatten); + for (int ll = 0; ll < (int)fr.extent(2); ll++) { + for (int kk = 0; kk < (int)fr.extent(3); kk++) { + const int flatten = kk*fr.extent(2) + ll; + fr(ii, jj, ll, kk) = d_values(ii, jj, flatten); + dfr(ii, jj, ll, kk) = d_derivatives(ii, jj, flatten); } } @@ -1686,7 +1869,7 @@ void PairPACEKokkos::SplineInterpolatorKokkos::operator=(const Splin rscalelookup = spline.rscalelookup; num_of_functions = spline.num_of_functions; - lookupTable = t_ace_3d4("lookupTable", ntot+1, num_of_functions); + lookupTable = t_ace_3d4_lr("lookupTable", ntot+1, num_of_functions); auto h_lookupTable = Kokkos::create_mirror_view(lookupTable); for (int i = 0; i < ntot+1; i++) for (int j = 0; j < num_of_functions; j++) @@ -1792,10 +1975,6 @@ double PairPACEKokkos::memory_usage() bytes += MemKK::memory_usage(d_derivatives); bytes += MemKK::memory_usage(cr); bytes += MemKK::memory_usage(dcr); - bytes += MemKK::memory_usage(plm); - bytes += MemKK::memory_usage(dplm); - bytes += MemKK::memory_usage(ylm); - bytes += MemKK::memory_usage(dylm); bytes += MemKK::memory_usage(d_ncount); bytes += MemKK::memory_usage(d_mu); bytes += MemKK::memory_usage(d_rhats); diff --git a/src/KOKKOS/pair_pace_kokkos.h b/src/KOKKOS/pair_pace_kokkos.h index 36486f8628..bb8c5a1f1a 100644 --- a/src/KOKKOS/pair_pace_kokkos.h +++ b/src/KOKKOS/pair_pace_kokkos.h @@ -36,7 +36,6 @@ class PairPACEKokkos : public PairPACE { public: struct TagPairPACEComputeNeigh{}; struct TagPairPACEComputeRadial{}; - struct TagPairPACEComputeYlm{}; struct TagPairPACEComputeAi{}; struct TagPairPACEConjugateAi{}; struct TagPairPACEComputeRho{}; @@ -66,9 +65,6 @@ class PairPACEKokkos : public PairPACE { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeRadial,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION - void operator() (TagPairPACEComputeYlm,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeAi,const typename Kokkos::TeamPolicy::member_type& team) const; @@ -96,7 +92,7 @@ class PairPACEKokkos : public PairPACE { void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -157,12 +153,6 @@ class PairPACEKokkos : public PairPACE { const F_FLOAT &fx, const F_FLOAT &fy, const F_FLOAT &fz, const F_FLOAT &delx, const F_FLOAT &dely, const F_FLOAT &delz) const; - KOKKOS_INLINE_FUNCTION - void compute_barplm(int, int, double, int) const; - - KOKKOS_INLINE_FUNCTION - void compute_ylm(int, int, double, double, double, int) const; - KOKKOS_INLINE_FUNCTION void cutoff_func_poly(const double, const double, const double, double &, double &) const; @@ -194,14 +184,18 @@ class PairPACEKokkos : public PairPACE { typedef Kokkos::View t_ace_1i; typedef Kokkos::View t_ace_2i; + typedef Kokkos::View t_ace_2i_lr; typedef Kokkos::View t_ace_3i; + typedef Kokkos::View t_ace_3i_lr; typedef Kokkos::View t_ace_4i; typedef Kokkos::View t_ace_1d; typedef Kokkos::View t_ace_2d; + typedef Kokkos::View t_ace_2d_lr; typedef Kokkos::View t_ace_2d3; typedef Kokkos::View t_ace_3d; typedef Kokkos::View t_ace_3d3; typedef Kokkos::View t_ace_3d4; + typedef Kokkos::View t_ace_3d4_lr; typedef Kokkos::View t_ace_4d; typedef Kokkos::View t_ace_1c; typedef Kokkos::View t_ace_2c; @@ -248,23 +242,13 @@ class PairPACEKokkos : public PairPACE { void pre_compute_harmonics(int); - KOKKOS_INLINE_FUNCTION - void compute_barplm(double rz, int lmaxi); - - KOKKOS_INLINE_FUNCTION - void compute_ylm(double rx, double ry, double rz, int lmaxi); - + t_ace_4c A_sph; + t_ace_1d d_idx_sph; t_ace_1d alm; t_ace_1d blm; t_ace_1d cl; t_ace_1d dl; - t_ace_3d plm; - t_ace_3d dplm; - - t_ace_3c ylm; - t_ace_4c3 dylm; - // short neigh list t_ace_1i d_ncount; t_ace_2d d_mu; @@ -283,18 +267,18 @@ class PairPACEKokkos : public PairPACE { t_ace_1d d_rho_core_cutoff; t_ace_1d d_drho_core_cutoff; t_ace_1d d_E0vals; - t_ace_2d d_wpre; - t_ace_2d d_mexp; + t_ace_2d_lr d_wpre; + t_ace_2d_lr d_mexp; // tilde t_ace_1i d_idx_rho_count; - t_ace_2i d_rank; - t_ace_2i d_num_ms_combs; - t_ace_2i d_offsets; - t_ace_3i d_mus; - t_ace_3i d_ns; - t_ace_3i d_ls; - t_ace_3i d_ms_combs; + t_ace_2i_lr d_rank; + t_ace_2i_lr d_num_ms_combs; + t_ace_2i_lr d_offsets; + t_ace_3i_lr d_mus; + t_ace_3i_lr d_ns; + t_ace_3i_lr d_ls; + t_ace_3i_lr d_ms_combs; t_ace_3d d_ctildes; t_ace_3d3 f_ij; @@ -304,12 +288,12 @@ class PairPACEKokkos : public PairPACE { int ntot, nlut, num_of_functions; double cutoff, deltaSplineBins, invrscalelookup, rscalelookup; - t_ace_3d4 lookupTable; + t_ace_3d4_lr lookupTable; void operator=(const SplineInterpolator &spline); void deallocate() { - lookupTable = t_ace_3d4(); + lookupTable = t_ace_3d4_lr(); } double memory_usage() { From 163805bc33176f58f9ed579f2fac2f7ed8734ce2 Mon Sep 17 00:00:00 2001 From: Mitch Murphy Date: Wed, 3 Jan 2024 15:18:34 -0500 Subject: [PATCH 145/305] removed scaffolding comments and fixed "(STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)" in compute_evdwl --- src/KOKKOS/dihedral_charmmfsw_kokkos.cpp | 117 ----- src/KOKKOS/dihedral_charmmfsw_kokkos.h | 138 +----- .../pair_lj_charmmfsw_coul_long_kokkos.cpp | 450 +----------------- .../pair_lj_charmmfsw_coul_long_kokkos.h | 84 +--- 4 files changed, 7 insertions(+), 782 deletions(-) diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp index b309f3d97f..831e7d9b22 100644 --- a/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.cpp @@ -27,29 +27,6 @@ ------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - - *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** - - (1) first draft version of DihedralCharmmfswKokkos exactly - same as DihedralCharmmfswKokkos but with new class name - - method: track changes from serial kspace dihedral_charmm to - dihedral_charmmfsw and apply to DihedralCharmmfswKokkos - - % diff dihedral_charmm.cpp dihedral_charmmfsw.cpp - -------------------------------------------------------------------------- */ - -/* - 18c21 - < #include "dihedral_charmm.h" - --- - > #include "dihedral_charmmfsw.h" - - */ - #include "dihedral_charmmfsw_kokkos.h" #include "atom_kokkos.h" @@ -526,15 +503,6 @@ void DihedralCharmmfswKokkos::operator()(TagDihedralCharmmfswCompute /* ---------------------------------------------------------------------- */ -/* - - 288c307 - < void DihedralCharmm::allocate() - --- - > void DihedralCharmmfsw::allocate() - - */ - template void DihedralCharmmfswKokkos::allocate() { @@ -545,15 +513,6 @@ void DihedralCharmmfswKokkos::allocate() set coeffs for one or more types ------------------------------------------------------------------------- */ -/* - - 308c327 - < void DihedralCharmm::coeff(int narg, char **arg) - --- - > void DihedralCharmmfsw::coeff(int narg, char **arg) - - */ - template void DihedralCharmmfswKokkos::coeff(int narg, char **arg) { @@ -603,40 +562,6 @@ void DihedralCharmmfswKokkos::coeff(int narg, char **arg) error check and initialize all values needed for force computation ------------------------------------------------------------------------- */ -/* - - 350c369 - < void DihedralCharmm::init_style() - --- - > void DihedralCharmmfsw::init_style() - 382a402,425 - > - > // constants for applying force switch (LJ) and force_shift (coul) - > // to 1/4 dihedral atoms to match CHARMM pairwise interactions - > - > int itmp; - > int *p_dihedflag = (int *) force->pair->extract("dihedflag", itmp); - > auto p_cutljinner = (double *) force->pair->extract("cut_lj_inner", itmp); - > auto p_cutlj = (double *) force->pair->extract("cut_lj", itmp); - > auto p_cutcoul = (double *) force->pair->extract("cut_coul", itmp); - > - > if (p_cutcoul == nullptr || p_cutljinner == nullptr || p_cutlj == nullptr || - > p_dihedflag == nullptr) - > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); - > - > dihedflag = *p_dihedflag; - > cut_coul14 = *p_cutcoul; - > cut_lj_inner14 = *p_cutljinner; - > cut_lj14 = *p_cutlj; - > - > cut_coulinv14 = 1 / cut_coul14; - > cut_lj_inner3inv = (1 / cut_lj_inner14) * (1 / cut_lj_inner14) * (1 / cut_lj_inner14); - > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; - > cut_lj3inv = (1 / cut_lj14) * (1 / cut_lj14) * (1 / cut_lj14); - > cut_lj6inv = cut_lj3inv * cut_lj3inv; - - */ - template void DihedralCharmmfswKokkos::init_style() { @@ -681,14 +606,6 @@ void DihedralCharmmfswKokkos::init_style() proc 0 reads coeffs from restart file, bcasts them ------------------------------------------------------------------------- */ -/* - - 402c445 - < void DihedralCharmm::read_restart(FILE *fp) - --- - > void DihedralCharmmfsw::read_restart(FILE *fp) - - */ template void DihedralCharmmfswKokkos::read_restart(FILE *fp) { @@ -968,37 +885,3 @@ template class DihedralCharmmfswKokkos; #endif } - - -/* - - - 355c374 - < error->all(FLERR, "Dihedral style charmm must be set to same r-RESPA level as 'pair'"); - --- - > error->all(FLERR, "Dihedral style charmmfsw must be set to same r-RESPA level as 'pair'"); - 357c376 - < error->all(FLERR, "Dihedral style charmm must be set to same r-RESPA level as 'outer'"); - --- - > error->all(FLERR, "Dihedral style charmmfsw must be set to same r-RESPA level as 'outer'"); - 373c392 - < error->all(FLERR, "Dihedral charmm is incompatible with Pair style"); - --- - > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); - 380c399 - < error->all(FLERR, "Dihedral charmm is incompatible with Pair style"); - --- - > error->all(FLERR, "Dihedral charmmfsw is incompatible with Pair style"); - - 389c432 - < void DihedralCharmm::write_restart(FILE *fp) - --- - > void DihedralCharmmfsw::write_restart(FILE *fp) - 430c473 - < void DihedralCharmm::write_data(FILE *fp) - --- - > void DihedralCharmmfsw::write_data(FILE *fp) - - */ - -// nothing to do for all these, inherited from DihedralCharmmfsw diff --git a/src/KOKKOS/dihedral_charmmfsw_kokkos.h b/src/KOKKOS/dihedral_charmmfsw_kokkos.h index 8b57b28d0c..c3842ca01d 100644 --- a/src/KOKKOS/dihedral_charmmfsw_kokkos.h +++ b/src/KOKKOS/dihedral_charmmfsw_kokkos.h @@ -11,31 +11,6 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ -/* ---------------------------------------------------------------------- - - *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** - - (1) first draft version of DihedralCharmmfswKokkos exactly - same as DihedralCharmmKokkos but with new class name - - (2) second draft version: nothing changed in header file - - method: track changes from serial kspace dihedral_charmm to - dihedral_charmmfsw and apply to DihedralCharmmKokkos - - % diff dihedral_charmm.h dihedral_charmmfsw.h - -------------------------------------------------------------------------- */ - -/* - - 16c16 - < DihedralStyle(charmm,DihedralCharmm); - --- - > DihedralStyle(charmmfsw,DihedralCharmmfsw); - - */ - #ifdef DIHEDRAL_CLASS // clang-format off DihedralStyle(charmmfsw/kk,DihedralCharmmfswKokkos); @@ -44,17 +19,6 @@ DihedralStyle(charmmfsw/kk/host,DihedralCharmmfswKokkos); // clang-format on #else -/* - - 20,21c20,21 - < #ifndef LMP_DIHEDRAL_CHARMM_H - < #define LMP_DIHEDRAL_CHARMM_H - --- - > #ifndef LMP_DIHEDRAL_CHARMMFSW_H - > #define LMP_DIHEDRAL_CHARMMFSW_H - - */ - // clang-format off #ifndef LMP_DIHEDRAL_CHARMMFSW_KOKKOS_H #define LMP_DIHEDRAL_CHARMMFSW_KOKKOS_H @@ -62,95 +26,17 @@ DihedralStyle(charmmfsw/kk/host,DihedralCharmmfswKokkos); #include "dihedral_charmmfsw.h" #include "kokkos_type.h" #include "dihedral_charmm_kokkos.h" - -/* - s_EVM_FLOAT and TagDihedralCharmmCompute conflict because style_dihedral.h - includes both dihedral_charmm_kokkos.h and dihedral_charmmfsw_kokkos.h - so comment out definitions in here and include dihedral_charmm_kokkos.h - in dihedral_charmmfsw_kokkos.h: - - In file included from /Users/mitch/Dropbox/lammps/lammps/src/force.cpp:18: - In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:65:8: error: redefinition of 's_EVM_FLOAT' - struct s_EVM_FLOAT { - ^ - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here - struct s_EVM_FLOAT { - ^ - In file included from /Users/mitch/Dropbox/lammps/lammps/src/lammps.cpp:23: - In file included from /Users/mitch/Dropbox/lammps/lammps/build/styles/style_dihedral.h:4: - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmmfsw_kokkos.h:65:8: error: redefinition of 's_EVM_FLOAT' - struct s_EVM_FLOAT { - ^ - /Users/mitch/Dropbox/lammps/lammps/src/KOKKOS/dihedral_charmm_kokkos.h:31:8: note: previous definition is here - struct s_EVM_FLOAT { - ^ - - */ - namespace LAMMPS_NS { -/* -struct s_EVM_FLOAT { - E_FLOAT evdwl; - E_FLOAT ecoul; - E_FLOAT emol; - F_FLOAT v[6]; - F_FLOAT vp[6]; - KOKKOS_INLINE_FUNCTION - s_EVM_FLOAT() { - evdwl = 0; - ecoul = 0; - emol = 0; - v[0] = 0; v[1] = 0; v[2] = 0; - v[3] = 0; v[4] = 0; v[5] = 0; - vp[0] = 0; vp[1] = 0; vp[2] = 0; - vp[3] = 0; vp[4] = 0; vp[5] = 0; - } - - KOKKOS_INLINE_FUNCTION - void operator+=(const s_EVM_FLOAT &rhs) { - evdwl += rhs.evdwl; - ecoul += rhs.ecoul; - emol += rhs.emol; - v[0] += rhs.v[0]; - v[1] += rhs.v[1]; - v[2] += rhs.v[2]; - v[3] += rhs.v[3]; - v[4] += rhs.v[4]; - v[5] += rhs.v[5]; - vp[0] += rhs.vp[0]; - vp[1] += rhs.vp[1]; - vp[2] += rhs.vp[2]; - vp[3] += rhs.vp[3]; - vp[4] += rhs.vp[4]; - vp[5] += rhs.vp[5]; - } -}; -typedef struct s_EVM_FLOAT EVM_FLOAT; - - */ +// s_EVM_FLOAT definition in here conflicted because style_dihedral.h +// includes both dihedral_charmm_kokkos.h and dihedral_charmmfsw_kokkos.h +// so remove definition of s_EVM_FLOAT in here and include +// dihedral_charmm_kokkos.h template struct TagDihedralCharmmfswCompute{}; - - -/* - 27c27 - < class DihedralCharmm : public Dihedral { - --- - > class DihedralCharmmfsw : public Dihedral { - 29,30c29,30 - < DihedralCharmm(class LAMMPS *); - < ~DihedralCharmm() override; - --- - > DihedralCharmmfsw(class LAMMPS *); - > ~DihedralCharmmfsw() override; - - */ - template class DihedralCharmmfswKokkos : public DihedralCharmmfsw { public: @@ -235,19 +121,3 @@ class DihedralCharmmfswKokkos : public DihedralCharmmfsw { #endif #endif - - -/* - - 38a39,43 - > int implicit, weightflag, dihedflag; - > double cut_lj_inner14, cut_lj14, cut_coul14; - > double evdwl14_12, evdwl14_6, cut_coulinv14; - > double cut_lj_inner3inv, cut_lj_inner6inv, cut_lj3inv, cut_lj6inv; - > - 42d46 - < int implicit, weightflag; - - */ - -// nothing to do here, inherited from DihedralCharmmfsw diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp index 7c1da2479f..191626fc9f 100644 --- a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp @@ -27,35 +27,6 @@ ------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - - *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** - - (1) first draft version of PairLJCharmmfswCoulLongKokkos almost exactly - same as PairLJCharmmCoulLongKokkos but with new class name - - method: track changes from serial kspace pair_lj_charmm_coul_long to - pair_lj_charmmfsw_coul_long and apply to PairLJCharmmCoulLongKokkos - - ISSUES: - - (A) charmm denom_lj_inv cache , is it to optimize code because division - is slower that multiplication ?? - - - - ------------------------------------------------------------------------- */ - - -/* - 19c23 - < #include "pair_lj_charmm_coul_long.h" - --- - > #include "pair_lj_charmmfsw_coul_long.h" - - */ - #include "pair_lj_charmmfsw_coul_long_kokkos.h" #include "atom_kokkos.h" @@ -86,30 +57,6 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -/* - 47c51 - < PairLJCharmmCoulLong::PairLJCharmmCoulLong(LAMMPS *lmp) : Pair(lmp) - --- - > PairLJCharmmfswCoulLong::PairLJCharmmfswCoulLong(LAMMPS *lmp) : Pair(lmp) - 55a60,72 - > - > // short-range/long-range flag accessed by DihedralCharmmfsw - > - > dihedflag = 1; - > - > // switch qqr2e from LAMMPS value to CHARMM value - > - > if (strcmp(update->unit_style,"real") == 0) { - > if ((comm->me == 0) && (force->qqr2e != force->qqr2e_charmm_real)) - > error->message(FLERR,"Switching to CHARMM coulomb energy" - > " conversion constant"); - > force->qqr2e = force->qqr2e_charmm_real; - > } - - */ - -// added superclass constructor to inherit from PairLJCharmmfswCoulLong - template PairLJCharmmfswCoulLongKokkos::PairLJCharmmfswCoulLongKokkos(LAMMPS *lmp):PairLJCharmmfswCoulLong(lmp) { @@ -125,25 +72,6 @@ PairLJCharmmfswCoulLongKokkos::PairLJCharmmfswCoulLongKokkos(LAMMPS /* ---------------------------------------------------------------------- */ -/* - - 60c77 - < PairLJCharmmCoulLong::~PairLJCharmmCoulLong() - --- - > PairLJCharmmfswCoulLong::~PairLJCharmmfswCoulLong() - 61a79,87 - > // switch qqr2e back from CHARMM value to LAMMPS value - > - > if (update && strcmp(update->unit_style,"real") == 0) { - > if ((comm->me == 0) && (force->qqr2e == force->qqr2e_charmm_real)) - > error->message(FLERR,"Restoring original LAMMPS coulomb energy" - > " conversion constant"); - > force->qqr2e = force->qqr2e_lammps_real; - > } - > - - */ - template PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() { @@ -159,28 +87,6 @@ PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() /* ---------------------------------------------------------------------- */ -/* - 87c112 - < void PairLJCharmmCoulLong::compute(int eflag, int vflag) - --- - > void PairLJCharmmfswCoulLong::compute(int eflag, int vflag) - 90c115 - < double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,ecoul,fpair; - --- - > double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,evdwl12,evdwl6,ecoul,fpair; - 92c117 - < double r,r2inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; - --- - > double r,rinv,r2inv,r3inv,r6inv,rsq,forcecoul,forcelj,factor_coul,factor_lj; - 94c119 - < double philj,switch1,switch2; - --- - > double switch1; - 96d120 - < double rsq; - - */ - template void PairLJCharmmfswCoulLongKokkos::compute(int eflag_in, int vflag_in) { @@ -287,22 +193,6 @@ compute_fpair(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, (STACKPARAMS?m_params[itype][jtype].lj2:params(itype,jtype).lj2)); if (rsq > cut_lj_innersq) { - - /* - 174,179c198,200 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - < } - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - > } - - */ - switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; forcelj = forcelj*switch1; @@ -327,32 +217,6 @@ compute_evdwl(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, const F_FLOAT r3inv = rinv*rinv*rinv; F_FLOAT englj, englj12, englj6; - /* - 205d225 - < evdwl = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); - 207,209c227,240 - < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < evdwl *= switch1; - --- - > r = sqrt(rsq); - > rinv = 1.0/r; - > r3inv = rinv*rinv*rinv; - > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * - > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); - > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * - > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); - > evdwl = evdwl12 + evdwl6; - > } else { - > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - - > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; - > evdwl6 = -lj4[itype][jtype]*r6inv + - > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; - > evdwl = evdwl12 + evdwl6; - - */ - - if (rsq > cut_lj_innersq) { englj12 = (STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*cut_lj6* denom_lj12 * (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); @@ -360,8 +224,8 @@ compute_evdwl(const F_FLOAT& rsq, const int& /*i*/, const int& /*j*/, cut_lj3*denom_lj6 * (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); englj = englj12 + englj6; } else { - englj12 = r6inv*lj3[itype][jtype]*r6inv - - lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; + englj12 = r6inv*(STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*r6inv - + (STACKPARAMS?m_params[itype][jtype].lj3:params(itype,jtype).lj3)*cut_lj_inner6inv*cut_lj6inv; englj6 = -(STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)*r6inv + (STACKPARAMS?m_params[itype][jtype].lj4:params(itype,jtype).lj4)* cut_lj_inner3inv*cut_lj3inv; @@ -573,18 +437,6 @@ void PairLJCharmmfswCoulLongKokkos::init_tables(double cut_coul, dou init specific to this pair style ------------------------------------------------------------------------- */ -/* - 682c733 - < void PairLJCharmmCoulLong::init_style() - --- - > void PairLJCharmmfswCoulLong::init_style() - 688c739 - < // request regular or rRESPA neighbor list - --- - > // request regular or rRESPA neighbor lists - - */ - template void PairLJCharmmfswCoulLongKokkos::init_style() { @@ -651,301 +503,3 @@ template class PairLJCharmmfswCoulLongKokkos; template class PairLJCharmmfswCoulLongKokkos; #endif } - - - - - - -/* - 80d105 - < memory->destroy(offset); - 598c650 - < void PairLJCharmmCoulLong::allocate() - --- - > void PairLJCharmmfswCoulLong::allocate() - 622d673 - < memory->create(offset,n+1,n+1,"pair:offset"); - 631c682 - < void PairLJCharmmCoulLong::settings(int narg, char **arg) - --- - > void PairLJCharmmfswCoulLong::settings(int narg, char **arg) - 645c696 - < void PairLJCharmmCoulLong::coeff(int narg, char **arg) - --- - > void PairLJCharmmfswCoulLong::coeff(int narg, char **arg) - - 686c737 - < "Pair style lj/charmm/coul/long requires atom attribute q"); - --- - > "Pair style lj/charmmfsw/coul/long requires atom attribute q"); - - 705a757,766 - > cut_ljinv = 1.0/cut_lj; - > cut_lj_innerinv = 1.0/cut_lj_inner; - > cut_lj3 = cut_lj * cut_lj * cut_lj; - > cut_lj3inv = cut_ljinv * cut_ljinv * cut_ljinv; - > cut_lj_inner3inv = cut_lj_innerinv * cut_lj_innerinv * cut_lj_innerinv; - > cut_lj_inner3 = cut_lj_inner * cut_lj_inner * cut_lj_inner; - > cut_lj6 = cut_ljsq * cut_ljsq * cut_ljsq; - > cut_lj6inv = cut_lj3inv * cut_lj3inv; - > cut_lj_inner6inv = cut_lj_inner3inv * cut_lj_inner3inv; - > cut_lj_inner6 = cut_lj_innersq * cut_lj_innersq * cut_lj_innersq; - 709,711c770,773 - < denom_lj = ( (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * - < (cut_ljsq-cut_lj_innersq) ); - < denom_lj_inv = 1.0 / denom_lj; - --- - > denom_lj = (cut_ljsq-cut_lj_innersq) * (cut_ljsq-cut_lj_innersq) * - > (cut_ljsq-cut_lj_innersq); - > denom_lj12 = 1.0/(cut_lj6 - cut_lj_inner6); - > denom_lj6 = 1.0/(cut_lj3 - cut_lj_inner3); - 718,730d779 - < cut_in_off = cut_respa[0]; - < cut_in_on = cut_respa[1]; - < cut_out_on = cut_respa[2]; - < cut_out_off = cut_respa[3]; - < - < cut_in_diff = cut_in_on - cut_in_off; - < cut_out_diff = cut_out_off - cut_out_on; - < cut_in_diff_inv = 1.0 / (cut_in_diff); - < cut_out_diff_inv = 1.0 / (cut_out_diff); - < cut_in_off_sq = cut_in_off*cut_in_off; - < cut_in_on_sq = cut_in_on*cut_in_on; - < cut_out_on_sq = cut_out_on*cut_out_on; - < cut_out_off_sq = cut_out_off*cut_out_off; - - - 752c801 - < double PairLJCharmmCoulLong::init_one(int i, int j) - --- - > double PairLJCharmmfswCoulLong::init_one(int i, int j) - 790c839 - < void PairLJCharmmCoulLong::write_restart(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::write_restart(FILE *fp) - 811c860 - < void PairLJCharmmCoulLong::read_restart(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::read_restart(FILE *fp) - 842c891 - < void PairLJCharmmCoulLong::write_restart_settings(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::write_restart_settings(FILE *fp) - 857c906 - < void PairLJCharmmCoulLong::read_restart_settings(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::read_restart_settings(FILE *fp) - 882c931 - < void PairLJCharmmCoulLong::write_data(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::write_data(FILE *fp) - 893c942 - < void PairLJCharmmCoulLong::write_data_all(FILE *fp) - --- - > void PairLJCharmmfswCoulLong::write_data_all(FILE *fp) - 903c952 - < double PairLJCharmmCoulLong::single(int i, int j, int itype, int jtype, - --- - > double PairLJCharmmfswCoulLong::single(int i, int j, int itype, int jtype, - 908,909c957,958 - < double r2inv,r6inv,r,grij,expm2,t,erfc,prefactor; - < double switch1,switch2,fraction,table,forcecoul,forcelj,phicoul,philj; - --- - > double r,rinv,r2inv,r3inv,r6inv,grij,expm2,t,erfc,prefactor; - > double switch1,fraction,table,forcecoul,forcelj,phicoul,philj,philj12,philj6; - 911a961,962 - > r = sqrt(rsq); - > rinv = 1.0/r; - 939c990,991 - < r6inv = r2inv*r2inv*r2inv; - --- - > r3inv = rinv*rinv*rinv; - > r6inv = r3inv*r3inv; - 943,947c995,996 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - 965d1013 - < philj = r6inv*(lj3[itype][jtype]*r6inv-lj4[itype][jtype]); - 967,969c1015,1025 - < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < philj *= switch1; - --- - > philj12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * - > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); - > philj6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * - > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); - > philj = philj12 + philj6; - > } else { - > philj12 = r6inv*lj3[itype][jtype]*r6inv - - > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; - > philj6 = -lj4[itype][jtype]*r6inv + - > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; - > philj = philj12 + philj6; - 979c1035 - < void *PairLJCharmmCoulLong::extract(const char *str, int &dim) - --- - > void *PairLJCharmmfswCoulLong::extract(const char *str, int &dim) - 988a1045,1047 - > - > // info extracted by dihedral_charmmfsw - > - 989a1049,1051 - > if (strcmp(str,"cut_lj_inner") == 0) return (void *) &cut_lj_inner; - > if (strcmp(str,"cut_lj") == 0) return (void *) &cut_lj; - > if (strcmp(str,"dihedflag") == 0) return (void *) &dihedflag; - - - */ - -// nothing to do for all these, inherited from PairLJCharmmfswCoulLong - - - - -/* - - 226c257 - < void PairLJCharmmCoulLong::compute_inner() - --- - > void PairLJCharmmfswCoulLong::compute_inner() - 248a280,286 - > double cut_out_on = cut_respa[0]; - > double cut_out_off = cut_respa[1]; - > - > double cut_out_diff = cut_out_off - cut_out_on; - > double cut_out_on_sq = cut_out_on*cut_out_on; - > double cut_out_off_sq = cut_out_off*cut_out_off; - > - 284c322 - < rsw = (sqrt(rsq) - cut_out_on)*cut_out_diff_inv; - --- - > rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; - 303c341 - < void PairLJCharmmCoulLong::compute_middle() - --- - > void PairLJCharmmfswCoulLong::compute_middle() - 308c346 - < double philj,switch1,switch2; - --- - > double switch1; - 326a365,376 - > double cut_in_off = cut_respa[0]; - > double cut_in_on = cut_respa[1]; - > double cut_out_on = cut_respa[2]; - > double cut_out_off = cut_respa[3]; - > - > double cut_in_diff = cut_in_on - cut_in_off; - > double cut_out_diff = cut_out_off - cut_out_on; - > double cut_in_off_sq = cut_in_off*cut_in_off; - > double cut_in_on_sq = cut_in_on*cut_in_on; - > double cut_out_on_sq = cut_out_on*cut_out_on; - > double cut_out_off_sq = cut_out_off*cut_out_off; - > - 361,365c411,412 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - 370c417 - < rsw = (sqrt(rsq) - cut_in_off)*cut_in_diff_inv; - --- - > rsw = (sqrt(rsq) - cut_in_off)/cut_in_diff; - 374c421 - < rsw = (sqrt(rsq) - cut_out_on)*cut_out_diff_inv; - --- - > rsw = (sqrt(rsq) - cut_out_on)/cut_out_diff; - 393c440 - < void PairLJCharmmCoulLong::compute_outer(int eflag, int vflag) - --- - > void PairLJCharmmfswCoulLong::compute_outer(int eflag, int vflag) - 396c443 - < double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,ecoul,fpair; - --- - > double qtmp,xtmp,ytmp,ztmp,delx,dely,delz,evdwl,evdwl6,evdwl12,ecoul,fpair; - 398c445 - < double r,r2inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; - --- - > double r,rinv,r2inv,r3inv,r6inv,forcecoul,forcelj,factor_coul,factor_lj; - 400c447 - < double philj,switch1,switch2; - --- - > double switch1; - 422a470,476 - > double cut_in_off = cut_respa[2]; - > double cut_in_on = cut_respa[3]; - > - > double cut_in_diff = cut_in_on - cut_in_off; - > double cut_in_off_sq = cut_in_off*cut_in_off; - > double cut_in_on_sq = cut_in_on*cut_in_on; - > - 448a503 - > r6inv = r2inv*r2inv*r2inv; - 489d543 - < r6inv = r2inv*r2inv*r2inv; - 493,497c547,548 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - 533d583 - < r6inv = r2inv*r2inv*r2inv; - 536,538c586,598 - < switch1 = (cut_ljsq-rsq) * (cut_ljsq-rsq) * - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < evdwl *= switch1; - --- - > rinv = sqrt(r2inv); - > r3inv = r2inv*rinv; - > evdwl12 = lj3[itype][jtype]*cut_lj6*denom_lj12 * - > (r6inv - cut_lj6inv)*(r6inv - cut_lj6inv); - > evdwl6 = -lj4[itype][jtype]*cut_lj3*denom_lj6 * - > (r3inv - cut_lj3inv)*(r3inv - cut_lj3inv); - > evdwl = evdwl12 + evdwl6; - > } else { - > evdwl12 = r6inv*lj3[itype][jtype]*r6inv - - > lj3[itype][jtype]*cut_lj_inner6inv*cut_lj6inv; - > evdwl6 = -lj4[itype][jtype]*r6inv + - > lj4[itype][jtype]*cut_lj_inner3inv*cut_lj3inv; - > evdwl = evdwl12 + evdwl6; - 561d620 - < r6inv = r2inv*r2inv*r2inv; - 565,569c624,625 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - 572d627 - < r6inv = r2inv*r2inv*r2inv; - 576,580c631,632 - < (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) * denom_lj_inv; - < switch2 = 12.0*rsq * (cut_ljsq-rsq) * - < (rsq-cut_lj_innersq) * denom_lj_inv; - < philj = r6inv * (lj3[itype][jtype]*r6inv - lj4[itype][jtype]); - < forcelj = forcelj*switch1 + philj*switch2; - --- - > (cut_ljsq + 2.0*rsq - 3.0*cut_lj_innersq) / denom_lj; - > forcelj = forcelj*switch1; - - */ - -// kokkos doesnt support respa, so ignore compute_inner / compute_middle / compute_outer diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h index e9a6b5486f..8fdb8543ed 100644 --- a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.h @@ -11,29 +11,6 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - - *** DRAFT VERSION 1 (lots of comments to be removed just before merge) *** - - (1) first draft version of PairLJCharmmfswCoulLongKokkos exactly - same as PairLJCharmmCoulLongKokkos but with new class name - - method: track changes from serial kspace pair_lj_charmm_coul_long to - pair_lj_charmmfsw_coul_long and apply to PairLJCharmmfswCoulLongKokkos - - % diff pair_lj_charmm_coul_long.h pair_lj_charmmfsw_coul_long.h - - -------------------------------------------------------------------------- */ - -/* - 16c16 - < PairStyle(lj/charmm/coul/long,PairLJCharmmCoulLong); - --- - > PairStyle(lj/charmmfsw/coul/long,PairLJCharmmfswCoulLong); - - */ #ifdef PAIR_CLASS // clang-format off @@ -43,17 +20,6 @@ PairStyle(lj/charmmfsw/coul/long/kk/host,PairLJCharmmfswCoulLongKokkos #ifndef LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_H - > #define LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_H - - */ - // clang-format off #ifndef LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_KOKKOS_H #define LMP_PAIR_LJ_CHARMMFSW_COUL_LONG_KOKKOS_H @@ -64,15 +30,6 @@ PairStyle(lj/charmmfsw/coul/long/kk/host,PairLJCharmmfswCoulLongKokkos class PairLJCharmmfswCoulLong : public Pair { - - */ - template class PairLJCharmmfswCoulLongKokkos : public PairLJCharmmfswCoulLong { public: @@ -80,18 +37,7 @@ class PairLJCharmmfswCoulLongKokkos : public PairLJCharmmfswCoulLong { enum {COUL_FLAG=1}; typedef DeviceType device_type; typedef ArrayTypes AT; - - /* - - 29,30c29,30 - < PairLJCharmmCoulLong(class LAMMPS *); - < ~PairLJCharmmCoulLong() override; - --- - > PairLJCharmmfswCoulLong(class LAMMPS *); - > ~PairLJCharmmfswCoulLong() override; - - */ - + PairLJCharmmfswCoulLongKokkos(class LAMMPS *); ~PairLJCharmmfswCoulLongKokkos() override; @@ -103,34 +49,6 @@ class PairLJCharmmfswCoulLongKokkos : public PairLJCharmmfswCoulLong { protected: - /* - 52c52,54 - < double cut_lj_inner, cut_lj; - --- - > int dihedflag; - > - > double cut_lj_inner, cut_lj, cut_ljinv, cut_lj_innerinv; - 53a56,57 - > double cut_lj3inv, cut_lj_inner3inv, cut_lj3, cut_lj_inner3; - > double cut_lj6inv, cut_lj_inner6inv, cut_lj6, cut_lj_inner6; - 56,60c60 - < double cut_in_off, cut_in_on, cut_out_off, cut_out_on; - < double cut_in_diff, cut_out_diff; - < double cut_in_diff_inv, cut_out_diff_inv; - < double cut_in_off_sq, cut_in_on_sq, cut_out_off_sq, cut_out_on_sq; - < double denom_lj, denom_lj_inv; - --- - > double denom_lj, denom_lj12, denom_lj6; - - */ - - // almost nothing to do here, inherited from PairLJCharmmfswCoulLong - // only temporarily need cut_lj_innersq, denom_coul protected variables - // (removed from pair_lj_charmm_coul_long to pair_lj_charmmfsw_coul_long) - // to compile draft version 1, can be removed by draft version 2 - - - template KOKKOS_INLINE_FUNCTION F_FLOAT compute_fpair(const F_FLOAT& rsq, const int& i, const int&j, From 5020861cbc7cd76fdddd8bafb31373bafccbc51b Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 13:22:52 -0700 Subject: [PATCH 146/305] Fix compiler warnings --- src/ML-PACE/compute_pace.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ML-PACE/compute_pace.cpp b/src/ML-PACE/compute_pace.cpp index cf3be8f47f..a3fd989fcd 100644 --- a/src/ML-PACE/compute_pace.cpp +++ b/src/ML-PACE/compute_pace.cpp @@ -73,10 +73,7 @@ ComputePACE::ComputePACE(LAMMPS *lmp, int narg, char **arg) : auto potential_file_name = utils::get_potential_file_path(arg[3]); delete acecimpl -> basis_set; acecimpl -> basis_set = new ACECTildeBasisSet(potential_file_name); - double cut = acecimpl -> basis_set->cutoffmax; cutmax = acecimpl -> basis_set->cutoffmax; - double cuti; - double radelemall = 0.5; //# of rank 1, rank > 1 functions @@ -184,7 +181,6 @@ void ComputePACE::init_list(int /*id*/, NeighList *ptr) void ComputePACE::compute_array() { int ntotal = atom->nlocal + atom->nghost; - double **f = atom->f; invoked_array = update->ntimestep; // grow pace_peratom array if necessary @@ -214,9 +210,6 @@ void ComputePACE::compute_array() // invoke full neighbor list (will copy or build if necessary) neighbor->build_one(list); - SPECIES_TYPE *mus; - NS_TYPE *ns; - LS_TYPE *ls; const int inum = list->inum; const int* const ilist = list->ilist; @@ -240,7 +233,6 @@ void ComputePACE::compute_array() // compute pace 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; const int ntypes = atom->ntypes; From 377450882ee20f89827f02c47e1adc190d375f32 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 14:10:29 -0700 Subject: [PATCH 147/305] Port changes to pair_pace_extrapolation_kokkos, unify code --- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 897 ++++++++++-------- src/KOKKOS/pair_pace_extrapolation_kokkos.h | 58 +- src/KOKKOS/pair_pace_kokkos.cpp | 270 +++--- src/KOKKOS/pair_pace_kokkos.h | 6 +- 4 files changed, 675 insertions(+), 556 deletions(-) diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 0980ad776d..0597885860 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -106,7 +106,8 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) if ((int)A.extent(0) < natom) { - MemKK::realloc_kokkos(A, "pace:A", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(A_sph, "pace:A_sph", natom, nelements, idx_sph_max, nradmax + 1); + MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_ms_combs_max, basis_set->rankmax); @@ -117,7 +118,7 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion MemKK::realloc_kokkos(dF_drho, "pace:dF_drho", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion - MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, nradmax + 1, (lmax + 1) * (lmax + 1)); + MemKK::realloc_kokkos(weights, "pace:weights", natom, nelements, idx_sph_max, nradmax + 1); MemKK::realloc_kokkos(weights_rank1, "pace:weights_rank1", natom, nelements, nradbase); // hard-core repulsion @@ -130,16 +131,16 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_ms_combs_max, basis_set->rankmax); - //B-projections + // B-projections MemKK::realloc_kokkos(projections, "pace:projections", natom, total_num_functions_max); // per-atom B-projections MemKK::realloc_kokkos(d_gamma, "pace:gamma", natom); // per-atom gamma } - if (((int)ylm.extent(0) < natom) || ((int)ylm.extent(1) < maxneigh)) { + if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { // radial functions - MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, nradmax, lmax + 1); - MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, nradmax, lmax + 1); + MemKK::realloc_kokkos(fr, "pace:fr", natom, maxneigh, lmax + 1, nradmax); + MemKK::realloc_kokkos(dfr, "pace:dfr", natom, maxneigh, lmax + 1, nradmax); MemKK::realloc_kokkos(gr, "pace:gr", natom, maxneigh, nradbase); MemKK::realloc_kokkos(dgr, "pace:dgr", natom, maxneigh, nradbase); const int max_num_functions = MAX(nradbase, nradmax*(lmax + 1)); @@ -150,12 +151,6 @@ void PairPACEExtrapolationKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(cr, "pace:cr", natom, maxneigh); MemKK::realloc_kokkos(dcr, "pace:dcr", natom, maxneigh); - // spherical harmonics - MemKK::realloc_kokkos(plm, "pace:plm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dplm, "pace:dplm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(ylm, "pace:ylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - MemKK::realloc_kokkos(dylm, "pace:dylm", natom, maxneigh, (lmax + 1) * (lmax + 1)); - // short neigh list MemKK::realloc_kokkos(d_ncount, "pace:ncount", natom); MemKK::realloc_kokkos(d_mu, "pace:mu", natom, maxneigh); @@ -224,7 +219,6 @@ void PairPACEExtrapolationKokkos::copy_pertype() Kokkos::deep_copy(d_wpre, h_wpre); Kokkos::deep_copy(d_mexp, h_mexp); - // ZBL core-rep MemKK::realloc_kokkos(d_cut_in, "pace:d_cut_in", nelements, nelements); MemKK::realloc_kokkos(d_dcut_in, "pace:d_dcut_in", nelements, nelements); @@ -266,6 +260,9 @@ void PairPACEExtrapolationKokkos::copy_splines() ACERadialFunctions* radial_functions = dynamic_cast(basis_set->radial_functions); + if (radial_functions == nullptr) + error->all(FLERR,"Chosen radial basis style not supported by pair style pace/kk"); + for (int i = 0; i < nelements; i++) { for (int j = 0; j < nelements; j++) { k_splines_gk.h_view(i, j) = radial_functions->splines_gk(i, j); @@ -297,8 +294,9 @@ void PairPACEExtrapolationKokkos::copy_tilde() total_num_functions_max = 0; MemKK::realloc_kokkos(d_idx_ms_combs_count, "pace:idx_ms_combs_count", nelements); - MemKK::realloc_kokkos(d_total_basis_size, "pace:total_basis_size", nelements); auto h_idx_ms_combs_count = Kokkos::create_mirror_view(d_idx_ms_combs_count); + + MemKK::realloc_kokkos(d_total_basis_size, "pace:total_basis_size", nelements); auto h_total_basis_size = Kokkos::create_mirror_view(d_total_basis_size); for (int mu = 0; mu < nelements; mu++) { @@ -313,8 +311,8 @@ void PairPACEExtrapolationKokkos::copy_tilde() idx_ms_combs++; // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACEBBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACEBBasisFunction *func = &basis[idx_func]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) @@ -331,7 +329,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() MemKK::realloc_kokkos(d_rank, "pace:rank", nelements, total_num_functions_max); MemKK::realloc_kokkos(d_num_ms_combs, "pace:num_ms_combs", nelements, total_num_functions_max); - MemKK::realloc_kokkos(d_func_inds, "pace:func_inds", nelements, idx_ms_combs_max); + MemKK::realloc_kokkos(d_idx_funcs, "pace:idx_funcs", nelements, idx_ms_combs_max); MemKK::realloc_kokkos(d_mus, "pace:mus", nelements, total_num_functions_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ns, "pace:ns", nelements, total_num_functions_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ls, "pace:ls", nelements, total_num_functions_max, basis_set->rankmax); @@ -344,7 +342,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() auto h_rank = Kokkos::create_mirror_view(d_rank); auto h_num_ms_combs = Kokkos::create_mirror_view(d_num_ms_combs); - auto h_func_inds = Kokkos::create_mirror_view(d_func_inds); + auto h_idx_funcs = Kokkos::create_mirror_view(d_idx_funcs); auto h_mus = Kokkos::create_mirror_view(d_mus); auto h_ns = Kokkos::create_mirror_view(d_ns); auto h_ls = Kokkos::create_mirror_view(d_ls); @@ -365,55 +363,52 @@ void PairPACEExtrapolationKokkos::copy_tilde() const int ndensity = basis_set->map_embedding_specifications.at(mu).ndensity; - int idx_ms_comb = 0; + int idx_ms_combs = 0; // rank=1 - for (int func_ind = 0; func_ind < total_basis_size_rank1; ++func_ind) { - ACEBBasisFunction *func = &basis_rank1[func_ind]; - h_rank(mu, func_ind) = 1; - h_mus(mu, func_ind, 0) = func->mus[0]; - h_ns(mu, func_ind, 0) = func->ns[0]; + for (int idx_func = 0; idx_func < total_basis_size_rank1; ++idx_func) { + ACEBBasisFunction *func = &basis_rank1[idx_func]; + h_rank(mu, idx_func) = 1; + h_mus(mu, idx_func, 0) = func->mus[0]; + h_ns(mu, idx_func, 0) = func->ns[0]; for (int p = 0; p < ndensity; ++p) - h_coeffs(mu, func_ind, p) = func->coeff[p]; + h_coeffs(mu, idx_func, p) = func->coeff[p]; - h_gen_cgs(mu, idx_ms_comb) = func->gen_cgs[0]; + h_gen_cgs(mu, idx_ms_combs) = func->gen_cgs[0]; - h_func_inds(mu, idx_ms_comb) = func_ind; - idx_ms_comb++; + h_idx_funcs(mu, idx_ms_combs) = idx_func; + idx_ms_combs++; } // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACEBBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACEBBasisFunction *func = &basis[idx_func]; // TODO: check if func->ctildes are zero, then skip - const int func_ind_through = total_basis_size_rank1 + func_ind; + const int idx_func_through = total_basis_size_rank1 + idx_func; - const int rank = h_rank(mu, func_ind_through) = func->rank; - h_num_ms_combs(mu, func_ind_through) = func->num_ms_combs; + const int rank = h_rank(mu, idx_func_through) = func->rank; + h_num_ms_combs(mu, idx_func_through) = func->num_ms_combs; for (int t = 0; t < rank; t++) { - h_mus(mu, func_ind_through, t) = func->mus[t]; - h_ns(mu, func_ind_through, t) = func->ns[t]; - h_ls(mu, func_ind_through, t) = func->ls[t]; + h_mus(mu, idx_func_through, t) = func->mus[t]; + h_ns(mu, idx_func_through, t) = func->ns[t]; + h_ls(mu, idx_func_through, t) = func->ls[t]; } for (int p = 0; p < ndensity; ++p) - h_coeffs(mu, func_ind_through, p) = func->coeff[p]; - + h_coeffs(mu, idx_func_through, p) = func->coeff[p]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) { auto ms = &func->ms_combs[ms_ind * rank]; // current ms-combination (of length = rank) for (int t = 0; t < rank; t++) - h_ms_combs(mu, idx_ms_comb, t) = ms[t]; + h_ms_combs(mu, idx_ms_combs, t) = ms[t]; + h_gen_cgs(mu, idx_ms_combs) = func->gen_cgs[ms_ind]; - h_gen_cgs(mu, idx_ms_comb) = func->gen_cgs[ms_ind]; - - - h_func_inds(mu, idx_ms_comb) = func_ind_through; - idx_ms_comb++; + h_idx_funcs(mu, idx_ms_combs) = idx_func_through; + idx_ms_combs++; } } @@ -427,7 +422,7 @@ void PairPACEExtrapolationKokkos::copy_tilde() Kokkos::deep_copy(d_rank, h_rank); Kokkos::deep_copy(d_num_ms_combs, h_num_ms_combs); - Kokkos::deep_copy(d_func_inds, h_func_inds); + Kokkos::deep_copy(d_idx_funcs, h_idx_funcs); Kokkos::deep_copy(d_mus, h_mus); Kokkos::deep_copy(d_ns, h_ns); Kokkos::deep_copy(d_ls, h_ls); @@ -477,6 +472,7 @@ void PairPACEExtrapolationKokkos::init_style() // spherical harmonics + MemKK::realloc_kokkos(d_idx_sph, "pace:idx_sph", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(alm, "pace:alm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(blm, "pace:blm", (lmax + 1) * (lmax + 1)); MemKK::realloc_kokkos(cl, "pace:cl", lmax + 1); @@ -575,6 +571,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in atomKK->modified(Host,F_MASK); return; } + eflag = eflag_in; vflag = vflag_in; @@ -602,6 +599,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in //zeroify array memset(extrapolation_grade_gamma, 0, nmax * sizeof(*extrapolation_grade_gamma)); } + if (flag_corerep_factor && atom->nlocal > nmax_corerep) { memory->destroy(corerep_factor); nmax_corerep = atom->nlocal; @@ -647,7 +645,6 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in chunk_size = MIN(chunksize,inum); // "chunksize" variable is set by user chunk_offset = 0; - grow(chunk_size, maxneigh); EV_FLOAT ev; @@ -656,14 +653,12 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in Kokkos::deep_copy(weights, 0.0); Kokkos::deep_copy(weights_rank1, 0.0); - Kokkos::deep_copy(A, 0.0); + Kokkos::deep_copy(A_sph, 0.0); Kokkos::deep_copy(A_rank1, 0.0); Kokkos::deep_copy(rhos, 0.0); - Kokkos::deep_copy(rho_core, 0.0); Kokkos::deep_copy(d_d_min, PairPACEExtrapolation::aceimpl->basis_set->cutoffmax); Kokkos::deep_copy(d_jj_min, -1); - Kokkos::deep_copy(projections, 0.0); Kokkos::deep_copy(d_gamma, 0.0); Kokkos::deep_copy(d_corerep, 0.0); @@ -693,15 +688,6 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in Kokkos::parallel_for("ComputeRadial",policy_radial,*this); } - //ComputeYlm - { - int vector_length = vector_length_default; - int team_size = 16; - check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_ylm(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - Kokkos::parallel_for("ComputeYlm",policy_ylm,*this); - } - //ComputeAi { int vector_length = vector_length_default; @@ -737,7 +723,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in //ComputeWeights { - typename Kokkos::RangePolicy policy_weights(0, chunk_size * idx_ms_combs_max); + typename Kokkos::RangePolicy policy_weights(0,chunk_size * idx_ms_combs_max); Kokkos::parallel_for("ComputeWeights",policy_weights,*this); } @@ -746,7 +732,7 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in int vector_length = vector_length_default; int team_size = team_size_default; check_team_size_for(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); - typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); + typename Kokkos::TeamPolicy policy_derivative(((chunk_size+team_size-1)/team_size)*maxneigh,team_size,vector_length); Kokkos::parallel_for("ComputeDerivative",policy_derivative,*this); } @@ -772,16 +758,18 @@ void PairPACEExtrapolationKokkos::compute(int eflag_in, int vflag_in } ev += ev_tmp; - //if flag_compute_extrapolation_grade - copy current d_gamma to extrapolation_grade_gamma + // if flag_compute_extrapolation_grade - copy current d_gamma to extrapolation_grade_gamma + if (flag_compute_extrapolation_grade){ h_gamma = Kokkos::create_mirror_view(d_gamma); Kokkos::deep_copy(h_gamma, d_gamma); memcpy(extrapolation_grade_gamma+chunk_offset, (void *) h_gamma.data(), sizeof(double)*chunk_size); } + if (flag_corerep_factor) { - h_corerep = Kokkos::create_mirror_view(d_corerep); - Kokkos::deep_copy(h_corerep,d_corerep); - memcpy(corerep_factor+chunk_offset, (void *) h_corerep.data(), sizeof(double)*chunk_size); + h_corerep = Kokkos::create_mirror_view(d_corerep); + Kokkos::deep_copy(h_corerep,d_corerep); + memcpy(corerep_factor+chunk_offset, (void *) h_corerep.data(), sizeof(double)*chunk_size); } chunk_offset += chunk_size; @@ -909,18 +897,18 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeNeig Kokkos::MinLoc reducer_scalar(djjmin); // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), - [&](const int offset, minloc_value_type &min_d_dist) { - int j = d_nearest(ii,offset); - j &= NEIGHMASK; - const int jtype = type(j); - auto r = d_rnorms(ii,offset); - const int mu_j = d_map(type(j)); - const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); - if (d < min_d_dist.val) { - min_d_dist.val = d; - min_d_dist.loc = offset; - } - }, reducer_scalar); + [&](const int offset, minloc_value_type &min_d_dist) { + int j = d_nearest(ii,offset); + j &= NEIGHMASK; + const int jtype = type(j); + auto r = d_rnorms(ii,offset); + const int mu_j = d_map(type(j)); + const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); + if (d < min_d_dist.val) { + min_d_dist.val = d; + min_d_dist.loc = offset; + } + }, reducer_scalar); d_d_min(ii) = djjmin.val; d_jj_min(ii) = djjmin.loc;// d_jj_min should be NOT in 0..jnum range, but in 0..d_ncount(<=jnum) } else { @@ -956,28 +944,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeRadi /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeYlm, const typename Kokkos::TeamPolicy::member_type& team) const -{ - // Extract the atom number - int ii = team.team_rank() + team.team_size() * (team.league_rank() % - ((chunk_size+team.team_size()-1)/team.team_size())); - if (ii >= chunk_size) return; - - // Extract the neighbor number - const int jj = team.league_rank() / ((chunk_size+team.team_size()-1)/team.team_size()); - const int ncount = d_ncount(ii); - if (jj >= ncount) return; - - const double xn = d_rhats(ii, jj, 0); - const double yn = d_rhats(ii, jj, 1); - const double zn = d_rhats(ii, jj, 2); - compute_ylm(ii,jj,xn,yn,zn,lmax); -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, const typename Kokkos::TeamPolicy::member_type& team) const @@ -999,13 +965,127 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, Kokkos::atomic_add(&A_rank1(ii, mu_j, n), gr(ii, jj, n) * Y00); // rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - Kokkos::atomic_add(&A(ii, mu_j, n, idx).re, fr(ii, jj, n, l) * ylm(ii, jj, idx).re); - Kokkos::atomic_add(&A(ii, mu_j, n, idx).im, fr(ii, jj, n, l) * ylm(ii, jj, idx).im); + + // Compute plm and ylm + + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm, phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + } + + ylm = phase * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + for (int n = 0; n < nradmax; n++) { + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).re, fr(ii, jj, l, n) * ylm.re); + Kokkos::atomic_add(&A_sph(ii, mu_j, idx_sph, n).im, fr(ii, jj, l, n) * ylm.im); + } + + plm_idx2 = plm_idx1; + plm_idx1 = plm_idx; + + idx_sph++; } } @@ -1019,17 +1099,35 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEConjugateAi, const int& ii) const { - //complex conjugate A's (for NEGATIVE (-m) terms) - // for rank > 1 for (int mu_j = 0; mu_j < nelements; mu_j++) { - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { + + // transpose + + int idx_sph = 0; + + for (int m = 0; m <= lmax; m++) { + for (int l = m; l <= lmax; l++) { + const int idx = l * (l + 1) + m; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idx, n) = A_sph(ii, mu_j, idx_sph, n); + } + + idx_sph++; + } + } + + // complex conjugate A's (for NEGATIVE (-m) terms) + // for rank > 1 + + for (int l = 0; l <= lmax; l++) { //fill in -m part in the outer loop using the same m <-> -m symmetry as for Ylm - for (int m = 1; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idxm = l * (l + 1) - m; // (l, -m) - const int factor = m % 2 == 0 ? 1 : -1; - A(ii, mu_j, n, idxm) = A(ii, mu_j, n, idx).conj() * (double)factor; + for (int m = 1; m <= l; m++) { + const int idx = l * (l + 1) + m; // (l, m) + const int idxm = l * (l + 1) - m; // (l, -m) + const int idx_sph = d_idx_sph(idx); + const int factor = m % 2 == 0 ? 1 : -1; + for (int n = 0; n < nradmax; n++) { + A(ii, mu_j, idxm, n) = A_sph(ii, mu_j, idx_sph, n).conj() * (double)factor; } } } @@ -1042,73 +1140,72 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeRho, const int& iter) const { - const int idx_ms_comb = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_ms_comb >= d_idx_ms_combs_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int func_ind = d_func_inds(mu_i, idx_ms_comb); - const int rank = d_rank(mu_i, func_ind); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); const int r = rank - 1; // Basis functions B with iterative product and density rho(p) calculation if (rank == 1) { - const int mu = d_mus(mu_i, func_ind, 0); - const int n = d_ns(mu_i, func_ind, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double A_cur = A_rank1(ii, mu, n - 1); for (int p = 0; p < ndensity; ++p) { //for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - Kokkos::atomic_add(&rhos(ii, p), d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb) * A_cur); + Kokkos::atomic_add(&rhos(ii, p), d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs) * A_cur); } - - //gamma_i + // gamma_i if (flag_compute_extrapolation_grade) - Kokkos::atomic_add(&projections(ii, func_ind), d_gen_cgs(mu_i, idx_ms_comb) * A_cur); + Kokkos::atomic_add(&projections(ii, idx_func), d_gen_cgs(mu_i, idx_ms_combs) * A_cur); } else { // rank > 1 // loop over {ms} combinations in sum // loop over m, collect B = product of A with given ms - A_forward_prod(ii, idx_ms_comb, 0) = complex::one(); + A_forward_prod(ii, idx_ms_combs, 0) = complex::one(); // fill forward A-product triangle for (int t = 0; t < rank; t++) { //TODO: optimize ns[t]-1 -> ns[t] during functions construction - const int mu = d_mus(mu_i, func_ind, t); - const int n = d_ns(mu_i, func_ind, t); - const int l = d_ls(mu_i, func_ind, t); - const int m = d_ms_combs(mu_i, idx_ms_comb, t); // current ms-combination (of length = rank) + const int mu = d_mus(mu_i, idx_func, t); + const int n = d_ns(mu_i, idx_func, t); + const int l = d_ls(mu_i, idx_func, t); + const int m = d_ms_combs(mu_i, idx_ms_combs, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_ms_comb, t) = A(ii, mu, n - 1, idx); - A_forward_prod(ii, idx_ms_comb, t + 1) = A_forward_prod(ii, idx_ms_comb, t) * A_list(ii, idx_ms_comb, t); + A_list(ii, idx_ms_combs, t) = A(ii, mu, idx, n - 1); + A_forward_prod(ii, idx_ms_combs, t + 1) = A_forward_prod(ii, idx_ms_combs, t) * A_list(ii, idx_ms_combs, t); } complex A_backward_prod = complex::one(); // fill backward A-product triangle for (int t = r; t >= 1; t--) { - const complex dB = A_forward_prod(ii, idx_ms_comb, t) * A_backward_prod; // dB - product of all A's except t-th - dB_flatten(ii, idx_ms_comb, t) = dB; + const complex dB = A_forward_prod(ii, idx_ms_combs, t) * A_backward_prod; // dB - product of all A's except t-th + dB_flatten(ii, idx_ms_combs, t) = dB; - A_backward_prod = A_backward_prod * A_list(ii, idx_ms_comb, t); + A_backward_prod = A_backward_prod * A_list(ii, idx_ms_combs, t); } - dB_flatten(ii, idx_ms_comb, 0) = A_forward_prod(ii, idx_ms_comb, 0) * A_backward_prod; + dB_flatten(ii, idx_ms_combs, 0) = A_forward_prod(ii, idx_ms_combs, 0) * A_backward_prod; - const complex B = A_forward_prod(ii, idx_ms_comb, rank); + const complex B = A_forward_prod(ii, idx_ms_combs, rank); for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb))); + Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs))); } - //gamma_i + // gamma_i if (flag_compute_extrapolation_grade) - Kokkos::atomic_add(&projections(ii, func_ind), B.real_part_product(d_gen_cgs(mu_i, idx_ms_comb))); + Kokkos::atomic_add(&projections(ii, idx_func), B.real_part_product(d_gen_cgs(mu_i, idx_ms_combs))); } } @@ -1129,7 +1226,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeFS, double evdwl_cut; evdwl = fcut = dfcut = 0.0; - inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); FS_values_and_derivatives(ii, evdwl, mu_i); if (is_zbl) { @@ -1155,7 +1251,6 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeFS, for (int p = 0; p < ndensity; ++p) dF_drho(ii, p) *= fcut; - // tally energy contribution if (eflag) { // E0 shift @@ -1201,52 +1296,58 @@ template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeWeights, const int& iter) const { - const int idx_ms_comb = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_ms_comb >= d_idx_ms_combs_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int func_ind = d_func_inds(mu_i, idx_ms_comb); - const int rank = d_rank(mu_i, func_ind); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); // Weights and theta calculation if (rank == 1) { - const int mu = d_mus(mu_i, func_ind, 0); - const int n = d_ns(mu_i, func_ind, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double theta = 0.0; for (int p = 0; p < ndensity; ++p) { // for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - theta += dF_drho(ii, p) * d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb); + theta += dF_drho(ii, p) * d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs); } Kokkos::atomic_add(&weights_rank1(ii, mu, n - 1), theta); } else { // rank > 1 double theta = 0.0; for (int p = 0; p < ndensity; ++p) - theta += dF_drho(ii, p) * d_coeffs(mu_i, func_ind, p) * d_gen_cgs(mu_i, idx_ms_comb); + theta += dF_drho(ii, p) * d_coeffs(mu_i, idx_func, p) * d_gen_cgs(mu_i, idx_ms_combs); theta *= 0.5; // 0.5 factor due to possible double counting ??? for (int t = 0; t < rank; ++t) { - const int m_t = d_ms_combs(mu_i, idx_ms_comb, t); + const int m_t = d_ms_combs(mu_i, idx_ms_combs, t); const int factor = (m_t % 2 == 0 ? 1 : -1); - const complex dB = dB_flatten(ii, idx_ms_comb, t); - const int mu_t = d_mus(mu_i, func_ind, t); - const int n_t = d_ns(mu_i, func_ind, t); - const int l_t = d_ls(mu_i, func_ind, t); + const complex dB = dB_flatten(ii, idx_ms_combs, t); + const int mu_t = d_mus(mu_i, idx_func, t); + const int n_t = d_ns(mu_i, idx_func, t); + const int l_t = d_ls(mu_i, idx_func, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) - const complex value = theta * dB; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).re), value.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idx).im), value.im); + const int idx_sph = d_idx_sph(idx); + if (idx_sph >= 0) { + const complex value = theta * dB; + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).re), value.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idx_sph, n_t - 1).im), value.im); + } // update -m_t (that could also be positive), because the basis is half_basis const int idxm = l_t * (l_t + 1) - m_t; // (l, -m) - const complex valuem = theta * dB.conj() * (double)factor; - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).re), valuem.re); - Kokkos::atomic_add(&(weights(ii, mu_t, n_t - 1, idxm).im), valuem.im); + const int idxm_sph = d_idx_sph(idxm); + if (idxm_sph >= 0) { + const complex valuem = theta * dB.conj() * (double)factor; + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).re), valuem.re); + Kokkos::atomic_add(&(weights(ii, mu_t, idxm_sph, n_t - 1).im), valuem.im); + } } } } @@ -1293,37 +1394,239 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeDeri } // for rank > 1 - for (int n = 0; n < nradmax; n++) { - for (int l = 0; l <= lmax; l++) { - const double R_over_r = fr(ii, jj, n, l) * rinv; - const double DR = dfr(ii, jj, n, l); - // for m >= 0 - for (int m = 0; m <= l; m++) { - const int idx = l * (l + 1) + m; // (l, m) - complex w = weights(ii, mu_j, n, idx); + // compute plm, dplm, ylm and dylm + // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! + // prefactors include 1/sqrt(2) factor compared to reference + + complex ylm,dylm[3]; + complex phase; + complex phasem, mphasem1; + complex dyx, dyy, dyz; + complex rdy; + + const double rx = d_rhats(ii, jj, 0); + const double ry = d_rhats(ii, jj, 1); + const double rz = d_rhats(ii, jj, 2); + + phase.re = rx; + phase.im = ry; + + double plm_idx,plm_idx1,plm_idx2; + double dplm_idx,dplm_idx1,dplm_idx2; + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + int idx_sph = 0; + + // m = 0 + for (int l = 0; l <= lmax; l++) { + // const int idx = l * (l + 1); + + if (l == 0) { + // l=0, m=0 + // plm[0] = Y00/sq1o4pi; //= sq1o4pi; + plm_idx = Y00; //= 1; + dplm_idx = 0.0; + } else if (l == 1) { + // l=1, m=0 + plm_idx = Y00 * sq3 * rz; + dplm_idx = Y00 * sq3; + } else { + // l>=2, m=0 + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = plm_idx; + ylm.im = 0.0; + + dyz.re = dplm_idx; + rdy.re = dyz.re * rz; + + dylm[0].re = -rdy.re * rx; + dylm[0].im = 0.0; + dylm[1].re = -rdy.re * ry; + dylm[1].im = 0.0; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = 0; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + // m = 1 + for (int l = 1; l <= lmax; l++) { + // const int idx = l * (l + 1) + 1; // (l, 1) + + if (l == 1) { + // l=1, m=1 + plm_idx = -sq3o2 * Y00; + dplm_idx = 0.0; + } else if (l == 2) { + const double t = dl(l) * plm_idx1; + plm_idx = t * rz; + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm = phase * plm_idx; + + dyx.re = plm_idx; + dyx.im = 0.0; + dyy.re = 0.0; + dyy.im = plm_idx; + dyz.re = phase.re * dplm_idx; + dyz.im = phase.im * dplm_idx; + + rdy.re = rx * dyx.re + +rz * dyz.re; + rdy.im = ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = -rdy.im * rx; + dylm[1].re = -rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); + if (w.re == 0.0 && w.im == 0.0) continue; + // counting for -m cases if m > 0 + w.re *= 2.0; + w.im *= 2.0; + + complex grad_phi_nlm[3]; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; + // real-part multiplication only + f_ji[0] += w.real_part_product(grad_phi_nlm[0]); + f_ji[1] += w.real_part_product(grad_phi_nlm[1]); + f_ji[2] += w.real_part_product(grad_phi_nlm[2]); + } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; + } + + plm_idx = plm_idx1 = plm_idx2 = 0.0; + dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; + + double plm_mm1_mm1 = -sq3o2 * Y00; // (1, 1) + + // m > 1 + phasem = phase; + for (int m = 2; m <= lmax; m++) { + + mphasem1.re = phasem.re * double(m); + mphasem1.im = phasem.im * double(m); + phasem = phasem * phase; + + for (int l = m; l <= lmax; l++) { + // const int idx = l * (l + 1) + m; + + if (l == m) { + plm_idx = cl(l) * plm_mm1_mm1; // (m+1, m) + dplm_idx = 0.0; + plm_mm1_mm1 = plm_idx; + } else if (l == (m + 1)) { + const double t = dl(l) * plm_mm1_mm1; // (m - 1, m - 1) + plm_idx = t * rz; // (m, m) + dplm_idx = t; + } else { + plm_idx = alm(idx_sph) * (rz * plm_idx1 + blm(idx_sph) * plm_idx2); + dplm_idx = alm(idx_sph) * (plm_idx1 + rz * dplm_idx1 + blm(idx_sph) * dplm_idx2); + } + + ylm.re = phasem.re * plm_idx; + ylm.im = phasem.im * plm_idx; + + dyx = mphasem1 * plm_idx; + dyy.re = -dyx.im; + dyy.im = dyx.re; + dyz = phasem * dplm_idx; + + rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; + rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; + + dylm[0].re = dyx.re - rdy.re * rx; + dylm[0].im = dyx.im - rdy.im * rx; + dylm[1].re = dyy.re - rdy.re * ry; + dylm[1].im = dyy.im - rdy.im * ry; + dylm[2].re = dyz.re - rdy.re * rz; + dylm[2].im = dyz.im - rdy.im * rz; + + for (int n = 0; n < nradmax; n++) { + + const double R_over_r = fr(ii, jj, l, n) * rinv; + const double DR = dfr(ii, jj, l, n); + const complex Y_DR = ylm * DR; + + complex w = weights(ii, mu_j, idx_sph, n); if (w.re == 0.0 && w.im == 0.0) continue; // counting for -m cases if m > 0 - if (m > 0) { - w.re *= 2.0; - w.im *= 2.0; - } - - complex DY[3]; - DY[0] = dylm(ii, jj, idx, 0); - DY[1] = dylm(ii, jj, idx, 1); - DY[2] = dylm(ii, jj, idx, 2); - const complex Y_DR = ylm(ii, jj, idx) * DR; + w.re *= 2.0; + w.im *= 2.0; complex grad_phi_nlm[3]; - grad_phi_nlm[0] = Y_DR * r_hat[0] + DY[0] * R_over_r; - grad_phi_nlm[1] = Y_DR * r_hat[1] + DY[1] * R_over_r; - grad_phi_nlm[2] = Y_DR * r_hat[2] + DY[2] * R_over_r; + grad_phi_nlm[0] = Y_DR * r_hat[0] + dylm[0] * R_over_r; + grad_phi_nlm[1] = Y_DR * r_hat[1] + dylm[1] * R_over_r; + grad_phi_nlm[2] = Y_DR * r_hat[2] + dylm[2] * R_over_r; // real-part multiplication only f_ji[0] += w.real_part_product(grad_phi_nlm[0]); f_ji[1] += w.real_part_product(grad_phi_nlm[1]); f_ji[2] += w.real_part_product(grad_phi_nlm[2]); } + + plm_idx2 = plm_idx1; + dplm_idx2 = dplm_idx1; + + plm_idx1 = plm_idx; + dplm_idx1 = dplm_idx; + + idx_sph++; } } @@ -1461,31 +1764,46 @@ void PairPACEExtrapolationKokkos::v_tally_xyz(EV_FLOAT &ev, const in template void PairPACEExtrapolationKokkos::pre_compute_harmonics(int lmax) { + auto h_idx_sph = Kokkos::create_mirror_view(d_idx_sph); auto h_alm = Kokkos::create_mirror_view(alm); auto h_blm = Kokkos::create_mirror_view(blm); auto h_cl = Kokkos::create_mirror_view(cl); auto h_dl = Kokkos::create_mirror_view(dl); - for (int l = 1; l <= lmax; l++) { - const double lsq = l * l; - const double ld = 2 * l; - const double l1 = (4 * lsq - 1); - const double l2 = lsq - ld + 1; - for (int m = 0; m < l - 1; m++) { - const double msq = m * m; - const double a = sqrt((double(l1)) / (double(lsq - msq))); - const double b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + Kokkos::deep_copy(h_idx_sph,-1); + + int idx_sph = 0; + for (int m = 0; m <= lmax; m++) { + const double msq = m * m; + for (int l = m; l <= lmax; l++) { const int idx = l * (l + 1) + m; // (l, m) - h_alm(idx) = a; - h_blm(idx) = b; + h_idx_sph(idx) = idx_sph; + + double a = 0.0; + double b = 0.0; + + if (l > 1 && l != m) { + const double lsq = l * l; + const double ld = 2 * l; + const double l1 = (4 * lsq - 1); + const double l2 = lsq - ld + 1; + + a = sqrt((double(l1)) / (double(lsq - msq))); + b = -sqrt((double(l2 - msq)) / (double(4 * l2 - 1))); + } + h_alm(idx_sph) = a; + h_blm(idx_sph) = b; + idx_sph++; } } + idx_sph_max = idx_sph; for (int l = 1; l <= lmax; l++) { h_cl(l) = -sqrt(1.0 + 0.5 / (double(l))); h_dl(l) = sqrt(double(2 * (l - 1) + 3)); } + Kokkos::deep_copy(d_idx_sph, h_idx_sph); Kokkos::deep_copy(alm, h_alm); Kokkos::deep_copy(blm, h_blm); Kokkos::deep_copy(cl, h_cl); @@ -1494,143 +1812,6 @@ void PairPACEExtrapolationKokkos::pre_compute_harmonics(int lmax) /* ---------------------------------------------------------------------- */ -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::compute_barplm(int ii, int jj, double rz, int lmax) const -{ - // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! - // prefactors include 1/sqrt(2) factor compared to reference - - // l=0, m=0 - // plm(ii, jj, 0, 0) = Y00/sq1o4pi; //= sq1o4pi; - plm(ii, jj, 0) = Y00; //= 1; - dplm(ii, jj, 0) = 0.0; - - if (lmax > 0) { - - // l=1, m=0 - plm(ii, jj, 2) = Y00 * sq3 * rz; - dplm(ii, jj, 2) = Y00 * sq3; - - // l=1, m=1 - plm(ii, jj, 3) = -sq3o2 * Y00; - dplm(ii, jj, 3) = 0.0; - - // loop l = 2, lmax - for (int l = 2; l <= lmax; l++) { - for (int m = 0; m < l - 1; m++) { - const int idx = l * (l + 1) + m; // (l, m) - const int idx1 = (l - 1) * l + m; // (l - 1, m) - const int idx2 = (l - 2) * (l - 1) + m; // (l - 2, m) - plm(ii, jj, idx) = alm(idx) * (rz * plm(ii, jj, idx1) + blm(idx) * plm(ii, jj, idx2)); - dplm(ii, jj, idx) = alm(idx) * (plm(ii, jj, idx1) + rz * dplm(ii, jj, idx1) + blm(idx) * dplm(ii, jj, idx2)); - } - const int idx = l * (l + 1) + l; // (l, l) - const int idx1 = l * (l + 1) + l - 1; // (l, l - 1) - const int idx2 = (l - 1) * l + l - 1; // (l - 1, l - 1) - const double t = dl(l) * plm(ii, jj, idx2); - plm(ii, jj, idx1) = t * rz; - dplm(ii, jj, idx1) = t; - plm(ii, jj, idx) = cl(l) * plm(ii, jj, idx2); - dplm(ii, jj, idx) = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- */ - -template -KOKKOS_INLINE_FUNCTION -void PairPACEExtrapolationKokkos::compute_ylm(int ii, int jj, double rx, double ry, double rz, int lmax) const -{ - // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! - - complex phase; - complex phasem, mphasem1; - complex dyx, dyy, dyz; - complex rdy; - - phase.re = rx; - phase.im = ry; - - // compute barplm - compute_barplm(ii, jj, rz, lmax); - - // m = 0 - for (int l = 0; l <= lmax; l++) { - const int idx = l * (l + 1); - - ylm(ii, jj, idx).re = plm(ii, jj, idx); - ylm(ii, jj, idx).im = 0.0; - - dyz.re = dplm(ii, jj, idx); - rdy.re = dyz.re * rz; - - dylm(ii, jj, idx, 0).re = -rdy.re * rx; - dylm(ii, jj, idx, 0).im = 0.0; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = 0.0; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = 0; - } - // m = 1 - for (int l = 1; l <= lmax; l++) { - const int idx = l * (l + 1) + 1; - - ylm(ii, jj, idx) = phase * plm(ii, jj, idx); - - dyx.re = plm(ii, jj, idx); - dyx.im = 0.0; - dyy.re = 0.0; - dyy.im = plm(ii, jj, idx); - dyz.re = phase.re * dplm(ii, jj, idx); - dyz.im = phase.im * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + +rz * dyz.re; - rdy.im = ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = -rdy.im * rx; - dylm(ii, jj, idx, 1).re = -rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - - // m > 1 - phasem = phase; - for (int m = 2; m <= lmax; m++) { - - mphasem1.re = phasem.re * double(m); - mphasem1.im = phasem.im * double(m); - phasem = phasem * phase; - - for (int l = m; l <= lmax; l++) { - const int idx = l * (l + 1) + m; - - ylm(ii, jj, idx).re = phasem.re * plm(ii, jj, idx); - ylm(ii, jj, idx).im = phasem.im * plm(ii, jj, idx); - - dyx = mphasem1 * plm(ii, jj, idx); - dyy.re = -dyx.im; - dyy.im = dyx.re; - dyz = phasem * dplm(ii, jj, idx); - - rdy.re = rx * dyx.re + ry * dyy.re + rz * dyz.re; - rdy.im = rx * dyx.im + ry * dyy.im + rz * dyz.im; - - dylm(ii, jj, idx, 0).re = dyx.re - rdy.re * rx; - dylm(ii, jj, idx, 0).im = dyx.im - rdy.im * rx; - dylm(ii, jj, idx, 1).re = dyy.re - rdy.re * ry; - dylm(ii, jj, idx, 1).im = dyy.im - rdy.im * ry; - dylm(ii, jj, idx, 2).re = dyz.re - rdy.re * rz; - dylm(ii, jj, idx, 2).im = dyz.im - rdy.im * rz; - } - } -} - -/* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEExtrapolationKokkos::cutoff_func_poly(const double r, const double r_in, const double delta_in, double &fc, double &dfc) const @@ -1759,11 +1940,11 @@ void PairPACEExtrapolationKokkos::evaluate_splines(const int ii, con spline_gk.calcSplines(ii, jj, r, gr, dgr); spline_rnl.calcSplines(ii, jj, r, d_values, d_derivatives); - for (int kk = 0; kk < (int)fr.extent(2); kk++) { - for (int ll = 0; ll < (int)fr.extent(3); ll++) { - const int flatten = kk*fr.extent(3) + ll; - fr(ii, jj, kk, ll) = d_values(ii, jj, flatten); - dfr(ii, jj, kk, ll) = d_derivatives(ii, jj, flatten); + for (int ll = 0; ll < (int)fr.extent(2); ll++) { + for (int kk = 0; kk < (int)fr.extent(3); kk++) { + const int flatten = kk*fr.extent(2) + ll; + fr(ii, jj, ll, kk) = d_values(ii, jj, flatten); + dfr(ii, jj, ll, kk) = d_derivatives(ii, jj, flatten); } } @@ -1783,7 +1964,7 @@ void PairPACEExtrapolationKokkos::SplineInterpolatorKokkos::operator rscalelookup = spline.rscalelookup; num_of_functions = spline.num_of_functions; - lookupTable = t_ace_3d4("lookupTable", ntot+1, num_of_functions); + lookupTable = t_ace_3d4_lr("lookupTable", ntot+1, num_of_functions); auto h_lookupTable = Kokkos::create_mirror_view(lookupTable); for (int i = 0; i < ntot+1; i++) for (int j = 0; j < num_of_functions; j++) @@ -1889,10 +2070,6 @@ double PairPACEExtrapolationKokkos::memory_usage() bytes += MemKK::memory_usage(d_derivatives); bytes += MemKK::memory_usage(cr); bytes += MemKK::memory_usage(dcr); - bytes += MemKK::memory_usage(plm); - bytes += MemKK::memory_usage(dplm); - bytes += MemKK::memory_usage(ylm); - bytes += MemKK::memory_usage(dylm); bytes += MemKK::memory_usage(d_ncount); bytes += MemKK::memory_usage(d_mu); bytes += MemKK::memory_usage(d_rhats); @@ -1911,7 +2088,7 @@ double PairPACEExtrapolationKokkos::memory_usage() bytes += MemKK::memory_usage(d_idx_ms_combs_count); bytes += MemKK::memory_usage(d_rank); bytes += MemKK::memory_usage(d_num_ms_combs); - bytes += MemKK::memory_usage(d_func_inds); + bytes += MemKK::memory_usage(d_idx_funcs); bytes += MemKK::memory_usage(d_mus); bytes += MemKK::memory_usage(d_ns); bytes += MemKK::memory_usage(d_ls); @@ -1940,47 +2117,6 @@ double PairPACEExtrapolationKokkos::memory_usage() return bytes; } -/* ---------------------------------------------------------------------- - extract method for extracting value of scale variable - ---------------------------------------------------------------------- */ - -template -void *PairPACEExtrapolationKokkos::extract(const char *str, int &dim) -{ - dim = 0; - //check if str=="flag_compute_extrapolation_grade" then compute extrapolation grades on this iteration - if (strcmp(str, "gamma_flag") == 0) return (void *) &flag_compute_extrapolation_grade; - if (strcmp(str, "corerep_flag") == 0) return (void *) &flag_corerep_factor; - - dim = 2; - if (strcmp(str, "scale") == 0) return (void *) scale; - return nullptr; -} - -/* ---------------------------------------------------------------------- - peratom requests from FixPair - return ptr to requested data - also return ncol = # of quantites per atom - 0 = per-atom vector - 1 or more = # of columns in per-atom array - return NULL if str is not recognized ----------------------------------------------------------------------- */ - -template -void *PairPACEExtrapolationKokkos::extract_peratom(const char *str, int &ncol) -{ - if (strcmp(str, "gamma") == 0) { - ncol = 0; - return (void *) extrapolation_grade_gamma; - } - if (strcmp(str, "corerep") == 0) { - ncol = 0; - return (void *) corerep_factor; - } - - return nullptr; -} - /* ---------------------------------------------------------------------- */ namespace LAMMPS_NS { @@ -1989,4 +2125,3 @@ template class PairPACEExtrapolationKokkos; template class PairPACEExtrapolationKokkos; #endif } - diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.h b/src/KOKKOS/pair_pace_extrapolation_kokkos.h index aa6c49c36d..df8a0c1740 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.h +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.h @@ -36,7 +36,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { public: struct TagPairPACEComputeNeigh{}; struct TagPairPACEComputeRadial{}; - struct TagPairPACEComputeYlm{}; struct TagPairPACEComputeAi{}; struct TagPairPACEConjugateAi{}; struct TagPairPACEComputeRho{}; @@ -67,9 +66,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeRadial,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION - void operator() (TagPairPACEComputeYlm,const typename Kokkos::TeamPolicy::member_type& team) const; - KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeAi,const typename Kokkos::TeamPolicy::member_type& team) const; @@ -99,12 +95,8 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { KOKKOS_INLINE_FUNCTION void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; - - void *extract(const char *str, int &dim) override; - void *extract_peratom(const char *str, int &ncol) override; - protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, total_num_functions_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, total_num_functions_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -165,12 +157,6 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { const F_FLOAT &fx, const F_FLOAT &fy, const F_FLOAT &fz, const F_FLOAT &delx, const F_FLOAT &dely, const F_FLOAT &delz) const; - KOKKOS_INLINE_FUNCTION - void compute_barplm(int, int, double, int) const; - - KOKKOS_INLINE_FUNCTION - void compute_ylm(int, int, double, double, double, int) const; - KOKKOS_INLINE_FUNCTION void cutoff_func_poly(const double, const double, const double, double &, double &) const; @@ -202,15 +188,19 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { typedef Kokkos::View t_ace_1i; typedef Kokkos::View t_ace_2i; + typedef Kokkos::View t_ace_2i_lr; typedef Kokkos::View t_ace_3i; + typedef Kokkos::View t_ace_3i_lr; typedef Kokkos::View t_ace_4i; typedef Kokkos::View t_ace_1d; typedef Kokkos::View t_ace_2d; + typedef Kokkos::View t_ace_2d_lr; typedef Kokkos::View t_ace_2d3; typedef Kokkos::View t_ace_3d; typedef Kokkos::View tc_ace_3d; typedef Kokkos::View t_ace_3d3; typedef Kokkos::View t_ace_3d4; + typedef Kokkos::View t_ace_3d4_lr; typedef Kokkos::View t_ace_4d; typedef Kokkos::View t_ace_1c; typedef Kokkos::View t_ace_2c; @@ -260,25 +250,16 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { th_ace_1d h_gamma; // Spherical Harmonics + void pre_compute_harmonics(int); - KOKKOS_INLINE_FUNCTION - void compute_barplm(double rz, int lmaxi); - - KOKKOS_INLINE_FUNCTION - void compute_ylm(double rx, double ry, double rz, int lmaxi); - + t_ace_4c A_sph; + t_ace_1d d_idx_sph; t_ace_1d alm; t_ace_1d blm; t_ace_1d cl; t_ace_1d dl; - t_ace_3d plm; - t_ace_3d dplm; - - t_ace_3c ylm; - t_ace_4c3 dylm; - // short neigh list t_ace_1i d_ncount; t_ace_2d d_mu; @@ -297,20 +278,19 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { t_ace_1d d_rho_core_cutoff; t_ace_1d d_drho_core_cutoff; t_ace_1d d_E0vals; - t_ace_2d d_wpre; - t_ace_2d d_mexp; + t_ace_2d_lr d_wpre; + t_ace_2d_lr d_mexp; // tilde t_ace_1i d_idx_ms_combs_count; t_ace_1i d_total_basis_size; - t_ace_2i d_rank; - t_ace_2i d_num_ms_combs; - t_ace_2i d_func_inds; - t_ace_3i d_mus; - t_ace_3i d_ns; - t_ace_3i d_ls; - t_ace_3i d_ms_combs; -// t_ace_3d d_ctildes; + t_ace_2i_lr d_rank; + t_ace_2i_lr d_num_ms_combs; + t_ace_2i_lr d_idx_funcs; + t_ace_3i_lr d_mus; + t_ace_3i_lr d_ns; + t_ace_3i_lr d_ls; + t_ace_3i_lr d_ms_combs; t_ace_2d d_gen_cgs; t_ace_3d d_coeffs; @@ -321,12 +301,12 @@ class PairPACEExtrapolationKokkos : public PairPACEExtrapolation { int ntot, nlut, num_of_functions; double cutoff, deltaSplineBins, invrscalelookup, rscalelookup; - t_ace_3d4 lookupTable; + t_ace_3d4_lr lookupTable; void operator=(const SplineInterpolator &spline); void deallocate() { - lookupTable = t_ace_3d4(); + lookupTable = t_ace_3d4_lr(); } double memory_usage() { diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 85fd458298..35b7b5ed82 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -29,11 +29,13 @@ #include "neighbor_kokkos.h" #include "neigh_request.h" +#include "ace-evaluator/ace_version.h" +#include "ace-evaluator/ace_radial.h" + #include "ace-evaluator/ace_c_basis.h" #include "ace-evaluator/ace_evaluator.h" #include "ace-evaluator/ace_recursive.h" -#include "ace-evaluator/ace_version.h" -#include "ace-evaluator/ace_radial.h" + #include namespace LAMMPS_NS { @@ -108,9 +110,9 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(A, "pace:A", natom, nelements, (lmax + 1) * (lmax + 1), nradmax + 1); MemKK::realloc_kokkos(A_rank1, "pace:A_rank1", natom, nelements, nradbase); - MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_rho_max, basis_set->rankmax); + MemKK::realloc_kokkos(A_list, "pace:A_list", natom, idx_ms_combs_max, basis_set->rankmax); //size is +1 of max to avoid out-of-boundary array access in double-triangular scheme - MemKK::realloc_kokkos(A_forward_prod, "pace:A_forward_prod", natom, idx_rho_max, basis_set->rankmax + 1); + MemKK::realloc_kokkos(A_forward_prod, "pace:A_forward_prod", natom, idx_ms_combs_max, basis_set->rankmax + 1); MemKK::realloc_kokkos(e_atom, "pace:e_atom", natom); MemKK::realloc_kokkos(rhos, "pace:rhos", natom, basis_set->ndensitymax + 1); // +1 density for core repulsion @@ -127,7 +129,7 @@ void PairPACEKokkos::grow(int natom, int maxneigh) MemKK::realloc_kokkos(d_jj_min, "pace:j_min_pair", natom); MemKK::realloc_kokkos(d_corerep, "pace:corerep", natom); // per-atom corerep - MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_rho_max, basis_set->rankmax); + MemKK::realloc_kokkos(dB_flatten, "pace:dB_flatten", natom, idx_ms_combs_max, basis_set->rankmax); } if (((int)fr.extent(0) < natom) || ((int)fr.extent(1) < maxneigh)) { @@ -179,7 +181,7 @@ void PairPACEKokkos::copy_pertype() h_rho_core_cutoff[n] = basis_set->map_embedding_specifications.at(n).rho_core_cutoff; h_drho_core_cutoff[n] = basis_set->map_embedding_specifications.at(n).drho_core_cutoff; - h_E0vals(n)= basis_set->E0vals(n); + h_E0vals(n) = basis_set->E0vals(n); h_ndensity(n) = basis_set->map_embedding_specifications.at(n).ndensity; @@ -220,10 +222,10 @@ void PairPACEKokkos::copy_pertype() auto h_dcut_in = Kokkos::create_mirror_view(d_dcut_in); for (int mu_i = 0; mu_i < nelements; ++mu_i) { - for (int mu_j = 0; mu_j < nelements; ++mu_j) { - h_cut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).rcut_in; - h_dcut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).dcut_in; - } + for (int mu_j = 0; mu_j < nelements; ++mu_j) { + h_cut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).rcut_in; + h_dcut_in(mu_i,mu_j) = basis_set->map_bond_specifications.at({mu_i,mu_j}).dcut_in; + } } Kokkos::deep_copy(d_cut_in, h_cut_in); Kokkos::deep_copy(d_dcut_in, h_dcut_in); @@ -283,50 +285,50 @@ void PairPACEKokkos::copy_tilde() // flatten loops, get per-element count and max - idx_rho_max = 0; + idx_ms_combs_max = 0; int total_basis_size_max = 0; - MemKK::realloc_kokkos(d_idx_rho_count, "pace:idx_rho_count", nelements); - auto h_idx_rho_count = Kokkos::create_mirror_view(d_idx_rho_count); + MemKK::realloc_kokkos(d_idx_ms_combs_count, "pace:idx_ms_combs_count", nelements); + auto h_idx_ms_combs_count = Kokkos::create_mirror_view(d_idx_ms_combs_count); - for (int n = 0; n < nelements; n++) { - int idx_rho = 0; - const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[n]; - const int total_basis_size = basis_set->total_basis_size[n]; + for (int mu = 0; mu < nelements; mu++) { + int idx_ms_combs = 0; + const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[mu]; + const int total_basis_size = basis_set->total_basis_size[mu]; - ACECTildeBasisFunction *basis = basis_set->basis[n]; + ACECTildeBasisFunction *basis = basis_set->basis[mu]; // rank=1 for (int func_rank1_ind = 0; func_rank1_ind < total_basis_size_rank1; ++func_rank1_ind) - idx_rho++; + idx_ms_combs++; // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACECTildeBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACECTildeBasisFunction *func = &basis[idx_func]; // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) - idx_rho++; + idx_ms_combs++; } - h_idx_rho_count(n) = idx_rho; - idx_rho_max = MAX(idx_rho_max, idx_rho); + h_idx_ms_combs_count(mu) = idx_ms_combs; + idx_ms_combs_max = MAX(idx_ms_combs_max, idx_ms_combs); total_basis_size_max = MAX(total_basis_size_max, total_basis_size_rank1 + total_basis_size); } - Kokkos::deep_copy(d_idx_rho_count, h_idx_rho_count); + Kokkos::deep_copy(d_idx_ms_combs_count, h_idx_ms_combs_count); MemKK::realloc_kokkos(d_rank, "pace:rank", nelements, total_basis_size_max); MemKK::realloc_kokkos(d_num_ms_combs, "pace:num_ms_combs", nelements, total_basis_size_max); - MemKK::realloc_kokkos(d_offsets, "pace:offsets", nelements, idx_rho_max); + MemKK::realloc_kokkos(d_idx_funcs, "pace:idx_func", nelements, idx_ms_combs_max); MemKK::realloc_kokkos(d_mus, "pace:mus", nelements, total_basis_size_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ns, "pace:ns", nelements, total_basis_size_max, basis_set->rankmax); MemKK::realloc_kokkos(d_ls, "pace:ls", nelements, total_basis_size_max, basis_set->rankmax); - MemKK::realloc_kokkos(d_ms_combs, "pace:ms_combs", nelements, idx_rho_max, basis_set->rankmax); - MemKK::realloc_kokkos(d_ctildes, "pace:ctildes", nelements, idx_rho_max, basis_set->ndensitymax); + MemKK::realloc_kokkos(d_ms_combs, "pace:ms_combs", nelements, idx_ms_combs_max, basis_set->rankmax); + MemKK::realloc_kokkos(d_ctildes, "pace:ctildes", nelements, idx_ms_combs_max, basis_set->ndensitymax); auto h_rank = Kokkos::create_mirror_view(d_rank); auto h_num_ms_combs = Kokkos::create_mirror_view(d_num_ms_combs); - auto h_offsets = Kokkos::create_mirror_view(d_offsets); + auto h_idx_funcs = Kokkos::create_mirror_view(d_idx_funcs); auto h_mus = Kokkos::create_mirror_view(d_mus); auto h_ns = Kokkos::create_mirror_view(d_ns); auto h_ls = Kokkos::create_mirror_view(d_ls); @@ -335,63 +337,66 @@ void PairPACEKokkos::copy_tilde() // copy values on host - for (int n = 0; n < nelements; n++) { - const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[n]; - const int total_basis_size = basis_set->total_basis_size[n]; + for (int mu = 0; mu < nelements; mu++) { + const int total_basis_size_rank1 = basis_set->total_basis_size_rank1[mu]; + const int total_basis_size = basis_set->total_basis_size[mu]; - ACECTildeBasisFunction *basis_rank1 = basis_set->basis_rank1[n]; - ACECTildeBasisFunction *basis = basis_set->basis[n]; + ACECTildeBasisFunction *basis_rank1 = basis_set->basis_rank1[mu]; + ACECTildeBasisFunction *basis = basis_set->basis[mu]; - const int ndensity = basis_set->map_embedding_specifications.at(n).ndensity; + const int ndensity = basis_set->map_embedding_specifications.at(mu).ndensity; - int idx_rho = 0; + int idx_ms_combs = 0; // rank=1 - for (int offset = 0; offset < total_basis_size_rank1; ++offset) { - ACECTildeBasisFunction *func = &basis_rank1[offset]; - h_rank(n, offset) = 1; - h_mus(n, offset, 0) = func->mus[0]; - h_ns(n, offset, 0) = func->ns[0]; - for (int p = 0; p < ndensity; p++) - h_ctildes(n, idx_rho, p) = func->ctildes[p]; - h_offsets(n, idx_rho) = offset; - idx_rho++; + for (int idx_func = 0; idx_func < total_basis_size_rank1; ++idx_func) { + ACECTildeBasisFunction *func = &basis_rank1[idx_func]; + h_rank(mu, idx_func) = 1; + h_mus(mu, idx_func, 0) = func->mus[0]; + h_ns(mu, idx_func, 0) = func->ns[0]; + + for (int p = 0; p < ndensity; ++p) + h_ctildes(mu, idx_ms_combs, p) = func->ctildes[p]; + + h_idx_funcs(mu, idx_ms_combs) = idx_func; + idx_ms_combs++; } // rank > 1 - for (int func_ind = 0; func_ind < total_basis_size; ++func_ind) { - ACECTildeBasisFunction *func = &basis[func_ind]; + for (int idx_func = 0; idx_func < total_basis_size; ++idx_func) { + ACECTildeBasisFunction *func = &basis[idx_func]; // TODO: check if func->ctildes are zero, then skip - const int offset = total_basis_size_rank1 + func_ind; + const int idx_func_through = total_basis_size_rank1 + idx_func; - const int rank = h_rank(n, offset) = func->rank; - h_num_ms_combs(n, offset) = func->num_ms_combs; + const int rank = h_rank(mu, idx_func_through) = func->rank; + h_num_ms_combs(mu, idx_func_through) = func->num_ms_combs; for (int t = 0; t < rank; t++) { - h_mus(n, offset, t) = func->mus[t]; - h_ns(n, offset, t) = func->ns[t]; - h_ls(n, offset, t) = func->ls[t]; + h_mus(mu, idx_func_through, t) = func->mus[t]; + h_ns(mu, idx_func_through, t) = func->ns[t]; + h_ls(mu, idx_func_through, t) = func->ls[t]; } // loop over {ms} combinations in sum for (int ms_ind = 0; ms_ind < func->num_ms_combs; ++ms_ind) { auto ms = &func->ms_combs[ms_ind * rank]; // current ms-combination (of length = rank) for (int t = 0; t < rank; t++) - h_ms_combs(n, idx_rho, t) = ms[t]; + h_ms_combs(mu, idx_ms_combs, t) = ms[t]; for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - h_ctildes(n, idx_rho, p) = func->ctildes[ms_ind * ndensity + p]; + h_ctildes(mu, idx_ms_combs, p) = func->ctildes[ms_ind * ndensity + p]; } - h_offsets(n, idx_rho) = offset; - idx_rho++; + + h_idx_funcs(mu, idx_ms_combs) = idx_func_through; + idx_ms_combs++; } } } Kokkos::deep_copy(d_rank, h_rank); Kokkos::deep_copy(d_num_ms_combs, h_num_ms_combs); - Kokkos::deep_copy(d_offsets, h_offsets); + Kokkos::deep_copy(d_idx_funcs, h_idx_funcs); Kokkos::deep_copy(d_mus, h_mus); Kokkos::deep_copy(d_ns, h_ns); Kokkos::deep_copy(d_ls, h_ls); @@ -659,7 +664,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) //ComputeRho { - typename Kokkos::RangePolicy policy_rho(0,chunk_size*idx_rho_max); + typename Kokkos::RangePolicy policy_rho(0,chunk_size*idx_ms_combs_max); Kokkos::parallel_for("ComputeRho",policy_rho,*this); } @@ -671,7 +676,7 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) //ComputeWeights { - typename Kokkos::RangePolicy policy_weights(0,chunk_size*idx_rho_max); + typename Kokkos::RangePolicy policy_weights(0,chunk_size * idx_ms_combs_max); Kokkos::parallel_for("ComputeWeights",policy_weights,*this); } @@ -713,7 +718,6 @@ void PairPACEKokkos::compute(int eflag_in, int vflag_in) } chunk_offset += chunk_size; - } // end while if (need_dup) @@ -829,15 +833,15 @@ void PairPACEKokkos::operator() (TagPairPACEComputeNeigh,const typen }); if (is_zbl) { - //adapted from https://www.osti.gov/servlets/purl/1429450 - if(ncount>0) { - using minloc_value_type=Kokkos::MinLoc::value_type; - minloc_value_type djjmin; - djjmin.val=1e20; - djjmin.loc=-1; - Kokkos::MinLoc reducer_scalar(djjmin); - // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) - Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), + //adapted from https://www.osti.gov/servlets/purl/1429450 + if (ncount > 0) { + using minloc_value_type=Kokkos::MinLoc::value_type; + minloc_value_type djjmin; + djjmin.val=1e20; + djjmin.loc=-1; + Kokkos::MinLoc reducer_scalar(djjmin); + // loop over ncount (actual neighbours withing cutoff) rather than jnum (total number of neigh in cutoff+skin) + Kokkos::parallel_reduce(Kokkos::TeamThreadRange(team, ncount), [&](const int offset, minloc_value_type &min_d_dist) { int j = d_nearest(ii,offset); j &= NEIGHMASK; @@ -846,8 +850,8 @@ void PairPACEKokkos::operator() (TagPairPACEComputeNeigh,const typen const int mu_j = d_map(type(j)); const F_FLOAT d = r - (d_cut_in(mu_i, mu_j) - d_dcut_in(mu_i, mu_j)); if (d < min_d_dist.val) { - min_d_dist.val = d; - min_d_dist.loc = offset; + min_d_dist.val = d; + min_d_dist.loc = offset; } }, reducer_scalar); d_d_min(ii) = djjmin.val; @@ -1081,70 +1085,69 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeRho, const int& iter) const { - const int idx_rho = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_rho >= d_idx_rho_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int offset = d_offsets(mu_i, idx_rho); - const int rank = d_rank(mu_i, offset); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); const int r = rank - 1; // Basis functions B with iterative product and density rho(p) calculation if (rank == 1) { - const int mu = d_mus(mu_i, offset, 0); - const int n = d_ns(mu_i, offset, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double A_cur = A_rank1(ii, mu, n - 1); for (int p = 0; p < ndensity; ++p) { //for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - Kokkos::atomic_add(&rhos(ii, p), d_ctildes(mu_i, idx_rho, p) * A_cur); + Kokkos::atomic_add(&rhos(ii, p), d_ctildes(mu_i, idx_ms_combs, p) * A_cur); } } else { // rank > 1 // loop over {ms} combinations in sum // loop over m, collect B = product of A with given ms - A_forward_prod(ii, idx_rho, 0) = complex::one(); + A_forward_prod(ii, idx_ms_combs, 0) = complex::one(); // fill forward A-product triangle for (int t = 0; t < rank; t++) { //TODO: optimize ns[t]-1 -> ns[t] during functions construction - const int mu = d_mus(mu_i, offset, t); - const int n = d_ns(mu_i, offset, t); - const int l = d_ls(mu_i, offset, t); - const int m = d_ms_combs(mu_i, idx_rho, t); // current ms-combination (of length = rank) + const int mu = d_mus(mu_i, idx_func, t); + const int n = d_ns(mu_i, idx_func, t); + const int l = d_ls(mu_i, idx_func, t); + const int m = d_ms_combs(mu_i, idx_ms_combs, t); // current ms-combination (of length = rank) const int idx = l * (l + 1) + m; // (l, m) - A_list(ii, idx_rho, t) = A(ii, mu, idx, n - 1); - A_forward_prod(ii, idx_rho, t + 1) = A_forward_prod(ii, idx_rho, t) * A_list(ii, idx_rho, t); + A_list(ii, idx_ms_combs, t) = A(ii, mu, idx, n - 1); + A_forward_prod(ii, idx_ms_combs, t + 1) = A_forward_prod(ii, idx_ms_combs, t) * A_list(ii, idx_ms_combs, t); } complex A_backward_prod = complex::one(); // fill backward A-product triangle for (int t = r; t >= 1; t--) { - const complex dB = A_forward_prod(ii, idx_rho, t) * A_backward_prod; // dB - product of all A's except t-th - dB_flatten(ii, idx_rho, t) = dB; + const complex dB = A_forward_prod(ii, idx_ms_combs, t) * A_backward_prod; // dB - product of all A's except t-th + dB_flatten(ii, idx_ms_combs, t) = dB; - A_backward_prod = A_backward_prod * A_list(ii, idx_rho, t); + A_backward_prod = A_backward_prod * A_list(ii, idx_ms_combs, t); } - dB_flatten(ii, idx_rho, 0) = A_forward_prod(ii, idx_rho, 0) * A_backward_prod; + dB_flatten(ii, idx_ms_combs, 0) = A_forward_prod(ii, idx_ms_combs, 0) * A_backward_prod; - const complex B = A_forward_prod(ii, idx_rho, rank); + const complex B = A_forward_prod(ii, idx_ms_combs, rank); for (int p = 0; p < ndensity; ++p) { // real-part only multiplication - Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_ctildes(mu_i, idx_rho, p))); + Kokkos::atomic_add(&rhos(ii, p), B.real_part_product(d_ctildes(mu_i, idx_ms_combs, p))); } } } /* ---------------------------------------------------------------------- */ - template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeFS, const int& ii) const @@ -1161,34 +1164,35 @@ void PairPACEKokkos::operator() (TagPairPACEComputeFS, const int& ii evdwl = fcut = dfcut = 0.0; FS_values_and_derivatives(ii, evdwl, mu_i); + if (is_zbl) { - if (d_jj_min(ii) != -1) { - const int mu_jmin = d_mu(ii,d_jj_min(ii)); - F_FLOAT dcutin = d_dcut_in(mu_i, mu_jmin); - F_FLOAT transition_coordinate = dcutin - d_d_min(ii); // == cutin - r_min - cutoff_func_poly(transition_coordinate, dcutin, dcutin, fcut, dfcut); - dfcut = -dfcut; // invert, because rho_core = cutin - r_min - } else { - // no neighbours - fcut = 1; - dfcut = 0; - } - evdwl_cut = evdwl * fcut + rho_core(ii) * (1 - fcut); // evdwl * fcut + rho_core_uncut - rho_core_uncut* fcut - dF_drho_core(ii) = 1 - fcut; - dF_dfcut(ii) = evdwl * dfcut - rho_core(ii) * dfcut; + if (d_jj_min(ii) != -1) { + const int mu_jmin = d_mu(ii,d_jj_min(ii)); + F_FLOAT dcutin = d_dcut_in(mu_i, mu_jmin); + F_FLOAT transition_coordinate = dcutin - d_d_min(ii); // == cutin - r_min + cutoff_func_poly(transition_coordinate, dcutin, dcutin, fcut, dfcut); + dfcut = -dfcut; // invert, because rho_core = cutin - r_min + } else { + // no neighbours + fcut = 1; + dfcut = 0; + } + evdwl_cut = evdwl * fcut + rho_core(ii) * (1 - fcut); // evdwl * fcut + rho_core_uncut - rho_core_uncut* fcut + dF_drho_core(ii) = 1 - fcut; + dF_dfcut(ii) = evdwl * dfcut - rho_core(ii) * dfcut; } else { - inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); - dF_drho_core(ii) = evdwl * dfcut + 1; - evdwl_cut = evdwl * fcut + rho_core(ii); + inner_cutoff(rho_core(ii), rho_cut, drho_cut, fcut, dfcut); + dF_drho_core(ii) = evdwl * dfcut + 1; + evdwl_cut = evdwl * fcut + rho_core(ii); } for (int p = 0; p < ndensity; ++p) - dF_drho(ii, p) *= fcut; + dF_drho(ii, p) *= fcut; // tally energy contribution if (eflag) { - // E0 shift - evdwl_cut += d_E0vals(mu_i); - e_atom(ii) = evdwl_cut; + // E0 shift + evdwl_cut += d_E0vals(mu_i); + e_atom(ii) = evdwl_cut; } if (flag_corerep_factor) @@ -1201,43 +1205,43 @@ template KOKKOS_INLINE_FUNCTION void PairPACEKokkos::operator() (TagPairPACEComputeWeights, const int& iter) const { - const int idx_rho = iter / chunk_size; + const int idx_ms_combs = iter / chunk_size; const int ii = iter % chunk_size; const int i = d_ilist[ii + chunk_offset]; const int mu_i = d_map(type(i)); - if (idx_rho >= d_idx_rho_count(mu_i)) return; + if (idx_ms_combs >= d_idx_ms_combs_count(mu_i)) return; const int ndensity = d_ndensity(mu_i); - const int offset = d_offsets(mu_i, idx_rho); - const int rank = d_rank(mu_i, offset); + const int idx_func = d_idx_funcs(mu_i, idx_ms_combs); + const int rank = d_rank(mu_i, idx_func); // Weights and theta calculation if (rank == 1) { - const int mu = d_mus(mu_i, offset, 0); - const int n = d_ns(mu_i, offset, 0); + const int mu = d_mus(mu_i, idx_func, 0); + const int n = d_ns(mu_i, idx_func, 0); double theta = 0.0; for (int p = 0; p < ndensity; ++p) { // for rank=1 (r=0) only 1 ms-combination exists (ms_ind=0), so index of func.ctildes is 0..ndensity-1 - theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_rho, p); + theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_ms_combs, p); } Kokkos::atomic_add(&weights_rank1(ii, mu, n - 1), theta); } else { // rank > 1 double theta = 0.0; for (int p = 0; p < ndensity; ++p) - theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_rho, p); + theta += dF_drho(ii, p) * d_ctildes(mu_i, idx_ms_combs, p); theta *= 0.5; // 0.5 factor due to possible double counting ??? for (int t = 0; t < rank; ++t) { - const int m_t = d_ms_combs(mu_i, idx_rho, t); + const int m_t = d_ms_combs(mu_i, idx_ms_combs, t); const int factor = (m_t % 2 == 0 ? 1 : -1); - const complex dB = dB_flatten(ii, idx_rho, t); - const int mu_t = d_mus(mu_i, offset, t); - const int n_t = d_ns(mu_i, offset, t); - const int l_t = d_ls(mu_i, offset, t); + const complex dB = dB_flatten(ii, idx_ms_combs, t); + const int mu_t = d_mus(mu_i, idx_func, t); + const int n_t = d_ns(mu_i, idx_func, t); + const int l_t = d_ls(mu_i, idx_func, t); const int idx = l_t * (l_t + 1) + m_t; // (l, m) const int idx_sph = d_idx_sph(idx); if (idx_sph >= 0) { @@ -1543,10 +1547,10 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const if (is_zbl) { if (jj==d_jj_min(ii)) { - // DCRU = 1.0 - f_ij(ii, jj, 0) += dF_dfcut(ii) * r_hat[0]; - f_ij(ii, jj, 1) += dF_dfcut(ii) * r_hat[1]; - f_ij(ii, jj, 2) += dF_dfcut(ii) * r_hat[2]; + // DCRU = 1.0 + f_ij(ii, jj, 0) += dF_dfcut(ii) * r_hat[0]; + f_ij(ii, jj, 1) += dF_dfcut(ii) * r_hat[1]; + f_ij(ii, jj, 2) += dF_dfcut(ii) * r_hat[2]; } } } @@ -1990,10 +1994,10 @@ double PairPACEKokkos::memory_usage() bytes += MemKK::memory_usage(d_npoti); bytes += MemKK::memory_usage(d_wpre); bytes += MemKK::memory_usage(d_mexp); - bytes += MemKK::memory_usage(d_idx_rho_count); + bytes += MemKK::memory_usage(d_idx_ms_combs_count); bytes += MemKK::memory_usage(d_rank); bytes += MemKK::memory_usage(d_num_ms_combs); - bytes += MemKK::memory_usage(d_offsets); + bytes += MemKK::memory_usage(d_idx_funcs); bytes += MemKK::memory_usage(d_mus); bytes += MemKK::memory_usage(d_ns); bytes += MemKK::memory_usage(d_ls); diff --git a/src/KOKKOS/pair_pace_kokkos.h b/src/KOKKOS/pair_pace_kokkos.h index bb8c5a1f1a..e22c61f0ea 100644 --- a/src/KOKKOS/pair_pace_kokkos.h +++ b/src/KOKKOS/pair_pace_kokkos.h @@ -92,7 +92,7 @@ class PairPACEKokkos : public PairPACE { void operator() (TagPairPACEComputeForce,const int& ii, EV_FLOAT&) const; protected: - int inum, maxneigh, chunk_size, chunk_offset, idx_rho_max, idx_sph_max; + int inum, maxneigh, chunk_size, chunk_offset, idx_ms_combs_max, idx_sph_max; int host_flag; int eflag, vflag; @@ -271,10 +271,10 @@ class PairPACEKokkos : public PairPACE { t_ace_2d_lr d_mexp; // tilde - t_ace_1i d_idx_rho_count; + t_ace_1i d_idx_ms_combs_count; t_ace_2i_lr d_rank; t_ace_2i_lr d_num_ms_combs; - t_ace_2i_lr d_offsets; + t_ace_2i_lr d_idx_funcs; t_ace_3i_lr d_mus; t_ace_3i_lr d_ns; t_ace_3i_lr d_ls; From 66a5f566828d16eb31f0e9faafbcf4b4a27d4494 Mon Sep 17 00:00:00 2001 From: Stan Moore Date: Wed, 3 Jan 2024 14:13:11 -0700 Subject: [PATCH 148/305] whitespace --- src/KOKKOS/pair_pace_extrapolation_kokkos.cpp | 4 ++-- src/KOKKOS/pair_pace_kokkos.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp index 0597885860..18ecaf6e69 100644 --- a/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp +++ b/src/KOKKOS/pair_pace_extrapolation_kokkos.cpp @@ -985,7 +985,7 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeAi, phase.im = ry; double plm_idx,plm_idx1,plm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; int idx_sph = 0; @@ -1415,7 +1415,7 @@ void PairPACEExtrapolationKokkos::operator() (TagPairPACEComputeDeri double plm_idx,plm_idx1,plm_idx2; double dplm_idx,dplm_idx1,dplm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; diff --git a/src/KOKKOS/pair_pace_kokkos.cpp b/src/KOKKOS/pair_pace_kokkos.cpp index 35b7b5ed82..aaed01510a 100644 --- a/src/KOKKOS/pair_pace_kokkos.cpp +++ b/src/KOKKOS/pair_pace_kokkos.cpp @@ -930,7 +930,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeAi, const typenam phase.im = ry; double plm_idx,plm_idx1,plm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; int idx_sph = 0; @@ -1324,7 +1324,7 @@ void PairPACEKokkos::operator() (TagPairPACEComputeDerivative, const double plm_idx,plm_idx1,plm_idx2; double dplm_idx,dplm_idx1,dplm_idx2; - + plm_idx = plm_idx1 = plm_idx2 = 0.0; dplm_idx = dplm_idx1 = dplm_idx2 = 0.0; From 600eaf837b5d911bea54d224b6a3616accc2935d Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Wed, 3 Jan 2024 16:20:04 -0500 Subject: [PATCH 149/305] update preferred contact info --- src/REACTION/README | 3 ++- src/REACTION/fix_bond_react.cpp | 2 +- src/REACTION/fix_bond_react.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/REACTION/README b/src/REACTION/README index 99a5d604ec..b9199d6d47 100644 --- a/src/REACTION/README +++ b/src/REACTION/README @@ -25,4 +25,5 @@ The REACTER methodology is detailed in: https://doi.org/10.1021/acs.macromol.0c02012 This package was created by Jacob Gissinger -(jacob.r.gissinger@gmail.com) at the NASA Langley Research Center. +(jgissing@stevens.edu) while at the NASA Langley Research Center +and Stevens Institute of Technology. diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index 10a7023e17..e704160e93 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -13,7 +13,7 @@ See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- -Contributing Author: Jacob Gissinger (jacob.r.gissinger@gmail.com) +Contributing Author: Jacob Gissinger (jgissing@stevens.edu) ------------------------------------------------------------------------- */ #include "fix_bond_react.h" diff --git a/src/REACTION/fix_bond_react.h b/src/REACTION/fix_bond_react.h index 534261e11d..3d56c2fc7b 100644 --- a/src/REACTION/fix_bond_react.h +++ b/src/REACTION/fix_bond_react.h @@ -12,7 +12,7 @@ ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- - Contributing Author: Jacob Gissinger (jacob.r.gissinger@gmail.com) + Contributing Author: Jacob Gissinger (jgissing@stevens.edu) ------------------------------------------------------------------------- */ #ifdef FIX_CLASS From 4fbb913425269ac31906e6571823a4e9fd63bd11 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 150/305] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From e00fc992fcbf899cefb3c409a561ed13e03b3346 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Fri, 22 Dec 2023 11:35:35 -0500 Subject: [PATCH 151/305] skip python tests using numpy that fail randomly on macOS --- unittest/python/CMakeLists.txt | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/unittest/python/CMakeLists.txt b/unittest/python/CMakeLists.txt index b4ba281d93..f3b851620c 100644 --- a/unittest/python/CMakeLists.txt +++ b/unittest/python/CMakeLists.txt @@ -84,20 +84,26 @@ if(Python_EXECUTABLE) WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCommands PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonNumpy - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonNumpy + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-numpy.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonNumpy PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonCapabilities COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-capabilities.py -v WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) set_tests_properties(PythonCapabilities PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") - add_test(NAME PythonPyLammps - COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v - WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + # randomly failing on macOS with python 3.12 + if(NOT APPLE) + add_test(NAME PythonPyLammps + COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-pylammps.py -v + WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + set_tests_properties(PythonPyLammps PROPERTIES ENVIRONMENT "${PYTHON_TEST_ENVIRONMENT}") + endif() add_test(NAME PythonFormats COMMAND ${PYTHON_TEST_RUNNER} ${CMAKE_CURRENT_SOURCE_DIR}/python-formats.py -v From 55784019f76c27ce5c3703d379439e123588530b Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 3 Jan 2024 16:46:06 -0700 Subject: [PATCH 152/305] Fix CPU issue --- src/KOKKOS/pair_kokkos.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index eea2cd5316..32f50c3bb5 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -84,8 +84,6 @@ struct PairComputeFunctor { // typename KKDevice::value,Kokkos::MemoryTraits::value> > vatom; KKScatterView dup_vatom; - - NeighListKokkos list; PairComputeFunctor(PairStyle* c_ptr, @@ -109,13 +107,15 @@ struct PairComputeFunctor { } void contribute() { - Kokkos::Experimental::contribute(c.f, dup_f); + if constexpr (std::is_same_v,Kokkos::Experimental::ScatterDuplicated>) { + Kokkos::Experimental::contribute(c.f, dup_f); - if (c.eflag_atom) - Kokkos::Experimental::contribute(c.d_eatom, dup_eatom); + if (c.eflag_atom) + Kokkos::Experimental::contribute(c.d_eatom, dup_eatom); - if (c.vflag_atom) - Kokkos::Experimental::contribute(c.d_vatom, dup_vatom); + if (c.vflag_atom) + Kokkos::Experimental::contribute(c.d_vatom, dup_vatom); + } } // Loop over neighbors of one atom without coulomb interaction @@ -988,11 +988,13 @@ EV_FLOAT pair_compute_neighlist (PairStyle* fpair, std::enable_if_t<(NEIGHFLAG&P Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); + ff.contribute(); } else { PairComputeFunctor ff(fpair,list); Kokkos::TeamPolicy > policy(num_teams,atoms_per_team,vectorsize); if (fpair->eflag || fpair->vflag) Kokkos::parallel_reduce(policy,ff,ev); else Kokkos::parallel_for(policy,ff); + ff.contribute(); } } else { if (fpair->atom->ntypes > MAX_TYPES_STACKPARAMS) { From 9d7582ec1bb86d0b41f9ae6f1de9e62d81e9db01 Mon Sep 17 00:00:00 2001 From: Stan Gerald Moore Date: Wed, 3 Jan 2024 16:59:11 -0700 Subject: [PATCH 153/305] Small tweak for readability --- src/KOKKOS/pair_kokkos.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/KOKKOS/pair_kokkos.h b/src/KOKKOS/pair_kokkos.h index 32f50c3bb5..9521268284 100644 --- a/src/KOKKOS/pair_kokkos.h +++ b/src/KOKKOS/pair_kokkos.h @@ -107,7 +107,9 @@ struct PairComputeFunctor { } void contribute() { - if constexpr (std::is_same_v,Kokkos::Experimental::ScatterDuplicated>) { + int need_dup = std::is_same_v; + + if (need_dup) { Kokkos::Experimental::contribute(c.f, dup_f); if (c.eflag_atom) From a6b00a60b208b17cd25fe6ff2a1d7c0000718bc0 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Wed, 3 Jan 2024 20:31:22 -0500 Subject: [PATCH 154/305] additional check/warning for valid templates --- src/REACTION/fix_bond_react.cpp | 35 +++++++++++++++++++++++++++++---- src/REACTION/fix_bond_react.h | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/REACTION/fix_bond_react.cpp b/src/REACTION/fix_bond_react.cpp index e704160e93..00292438ec 100644 --- a/src/REACTION/fix_bond_react.cpp +++ b/src/REACTION/fix_bond_react.cpp @@ -2681,16 +2681,43 @@ void FixBondReact::find_landlocked_atoms(int myrxn) } // also, if atoms change number of bonds, but aren't landlocked, that could be bad + int warnflag = 0; if (comm->me == 0) for (int i = 0; i < twomol->natoms; i++) { if ((create_atoms[i][myrxn] == 0) && (twomol_nxspecial[i][0] != onemol_nxspecial[equivalences[i][1][myrxn]-1][0]) && - (landlocked_atoms[i][myrxn] == 0)) - error->warning(FLERR, "Fix bond/react: Atom affected by reaction {} is too close " - "to template edge",rxn_name[myrxn]); - break; + (landlocked_atoms[i][myrxn] == 0)) { + warnflag = 1; + break; + } } + // also, if an atom changes any of its bonds, but is not landlocked, that could be bad + int thereflag; + if (comm->me == 0) + for (int i = 0; i < twomol->natoms; i++) { + if (landlocked_atoms[i][myrxn] == 1) continue; + for (int j = 0; j < twomol_nxspecial[i][0]; j++) { + int oneneighID = equivalences[twomol_xspecial[i][j]-1][1][myrxn]; + int ii = equivalences[i][1][myrxn] - 1; + thereflag = 0; + for (int k = 0; k < onemol_nxspecial[ii][0]; k++) { + if (oneneighID == onemol_xspecial[ii][k]) { + thereflag = 1; + break; + } + } + if (thereflag == 0) { + warnflag = 1; + break; + } + } + if (warnflag == 1) break; + } + + if (comm->me == 0 && warnflag == 1) error->warning(FLERR, "Fix bond/react: Atom affected " + "by reaction {} is too close to template edge",rxn_name[myrxn]); + // finally, if a created atom is not landlocked, bad! for (int i = 0; i < twomol->natoms; i++) { if (create_atoms[i][myrxn] == 1 && landlocked_atoms[i][myrxn] == 0) { diff --git a/src/REACTION/fix_bond_react.h b/src/REACTION/fix_bond_react.h index 3d56c2fc7b..8c9fc9dce4 100644 --- a/src/REACTION/fix_bond_react.h +++ b/src/REACTION/fix_bond_react.h @@ -139,7 +139,7 @@ class FixBondReact : public Fix { int avail_guesses; // num of restore points available int *guess_branch; // used when there is more than two choices when guessing int **restore_pt; // contains info about restore points - tagint **restore; // contaings info about restore points + tagint **restore; // contains info about restore points int *pioneer_count; // counts pioneers int **edge; // atoms in molecule templates with incorrect valences From 13b6d40062b01a078dd86bd6fe517e15ac731178 Mon Sep 17 00:00:00 2001 From: Jacob Gissinger Date: Wed, 3 Jan 2024 20:38:10 -0500 Subject: [PATCH 155/305] tiny_epoxy example correction not sure why this issue showed up in recent LAMMPS versions --- examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized b/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized index ea09d06893..7e0350cdb0 100644 --- a/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized +++ b/examples/PACKAGES/reaction/tiny_epoxy/in.tiny_epoxy.stabilized @@ -20,7 +20,8 @@ improper_style class2 special_bonds lj/coul 0 0 1 pair_modify tail yes mix sixthpower -read_data tiny_epoxy.data +read_data tiny_epoxy.data & + extra/special/per/atom 25 velocity all create 300.0 4928459 dist gaussian From de1f6eefd7c167e4980f4173ad1e0b65d48ea4c5 Mon Sep 17 00:00:00 2001 From: Mitch Murphy Date: Wed, 3 Jan 2024 23:00:07 -0500 Subject: [PATCH 156/305] // FIXME: // superclass destructor from KSPACE/pair_lj_charmmfsw_coul_long.cpp:81 // resets force->qqr2e = force->qqr2e_lammps_real at end of timestep 0 // causing ~E-6 errors for steps 1,2,... everywhere in this class when // running kokkos with openmp (and probably with GPUs also). // // WORKAROUND: for now until guidance from lammps devs is to // reset it back force->qqr2e = force->qqr2e_charmm_real here. --- src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp index 191626fc9f..7701f13768 100644 --- a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp @@ -76,6 +76,17 @@ template PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() { + // FIXME: + // superclass destructor from KSPACE/pair_lj_charmmfsw_coul_long.cpp:81 + // resets force->qqr2e = force->qqr2e_lammps_real at end of timestep 0 + // causing ~E-6 errors for steps 1,2,... everywhere in this class when + // running kokkos with openmp (and probably with GPUs also). + // + // WORKAROUND: for now until guidance from lammps devs is to + // reset it back force->qqr2e = force->qqr2e_charmm_real here. + + force->qqr2e = force->qqr2e_charmm_real; + if (copymode) return; if (allocated) { From c065d4bac626bd7fd2912cc1bcd368078a2f587e Mon Sep 17 00:00:00 2001 From: Mitch Murphy Date: Thu, 4 Jan 2024 00:50:06 -0500 Subject: [PATCH 157/305] // FIXME: destructor from this class resets // // force->qqr2e = force->qqr2e_lammps_real // // at end of timestep 0 causing ~E-6 errors for steps 1,2,... // everywhere in pair_lj_charmmfsw_coul_long_kokkos when // running kokkos with openmp (and probably with GPUs also). // // WORKAROUND: for now until guidance from lammps devs is to // comment out this line here (commit to be reversed later). //force->qqr2e = force->qqr2e_lammps_real; --- src/KSPACE/pair_lj_charmmfsw_coul_long.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/KSPACE/pair_lj_charmmfsw_coul_long.cpp b/src/KSPACE/pair_lj_charmmfsw_coul_long.cpp index b7635c49c7..83b7293178 100644 --- a/src/KSPACE/pair_lj_charmmfsw_coul_long.cpp +++ b/src/KSPACE/pair_lj_charmmfsw_coul_long.cpp @@ -82,7 +82,19 @@ PairLJCharmmfswCoulLong::~PairLJCharmmfswCoulLong() if ((comm->me == 0) && (force->qqr2e == force->qqr2e_charmm_real)) error->message(FLERR,"Restoring original LAMMPS coulomb energy" " conversion constant"); - force->qqr2e = force->qqr2e_lammps_real; + + // FIXME: destructor from this class resets + // + // force->qqr2e = force->qqr2e_lammps_real + // + // at end of timestep 0 causing ~E-6 errors for steps 1,2,... + // everywhere in pair_lj_charmmfsw_coul_long_kokkos when + // running kokkos with openmp (and probably with GPUs also). + // + // WORKAROUND: for now until guidance from lammps devs is to + // comment out this line here (commit to be reversed later). + + //force->qqr2e = force->qqr2e_lammps_real; } if (copymode) return; From e26a762f880964497f500434229163d5e82e674c Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 11:21:38 -0500 Subject: [PATCH 158/305] improve compatibility of oneapi.cmake preset --- cmake/presets/oneapi.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/presets/oneapi.cmake b/cmake/presets/oneapi.cmake index 2aacf1a1f5..393d1d9b68 100644 --- a/cmake/presets/oneapi.cmake +++ b/cmake/presets/oneapi.cmake @@ -18,11 +18,11 @@ set(MPI_CXX_COMPILER "mpicxx" CACHE STRING "" FORCE) unset(HAVE_OMP_H_INCLUDE CACHE) set(OpenMP_C "icx" CACHE STRING "" FORCE) -set(OpenMP_C_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_C_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_C_LIB_NAMES "omp" CACHE STRING "" FORCE) set(OpenMP_CXX "icpx" CACHE STRING "" FORCE) -set(OpenMP_CXX_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_CXX_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_CXX_LIB_NAMES "omp" CACHE STRING "" FORCE) -set(OpenMP_Fortran_FLAGS "-qopenmp -qopenmp-simd" CACHE STRING "" FORCE) +set(OpenMP_Fortran_FLAGS "-qopenmp;-qopenmp-simd" CACHE STRING "" FORCE) set(OpenMP_omp_LIBRARY "libiomp5.so" CACHE PATH "" FORCE) From 4b4e796c190734090c80b2badd2023a66e08bb6c Mon Sep 17 00:00:00 2001 From: alphataubio Date: Thu, 4 Jan 2024 16:06:41 -0500 Subject: [PATCH 159/305] Revert " // FIXME:" This reverts commit de1f6eefd7c167e4980f4173ad1e0b65d48ea4c5. my first idea to reset back force->qqr2e = force->qqr2e_charmm_real didnt work because class destructor gets called first THEN superclass destructor gets called --- src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp index 7701f13768..191626fc9f 100644 --- a/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp +++ b/src/KOKKOS/pair_lj_charmmfsw_coul_long_kokkos.cpp @@ -76,17 +76,6 @@ template PairLJCharmmfswCoulLongKokkos::~PairLJCharmmfswCoulLongKokkos() { - // FIXME: - // superclass destructor from KSPACE/pair_lj_charmmfsw_coul_long.cpp:81 - // resets force->qqr2e = force->qqr2e_lammps_real at end of timestep 0 - // causing ~E-6 errors for steps 1,2,... everywhere in this class when - // running kokkos with openmp (and probably with GPUs also). - // - // WORKAROUND: for now until guidance from lammps devs is to - // reset it back force->qqr2e = force->qqr2e_charmm_real here. - - force->qqr2e = force->qqr2e_charmm_real; - if (copymode) return; if (allocated) { From f9aafff9928c43f27728647ebcb5b5f245f0ad19 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 22:45:53 -0500 Subject: [PATCH 160/305] must include fmt/ranges.h for fmt::join() --- src/KIM/kim_interactions.cpp | 2 ++ src/KIM/kim_param.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/KIM/kim_interactions.cpp b/src/KIM/kim_interactions.cpp index 1f4f84e648..ce550bf5da 100644 --- a/src/KIM/kim_interactions.cpp +++ b/src/KIM/kim_interactions.cpp @@ -70,6 +70,8 @@ #include "modify.h" #include "update.h" +#include "fmt/ranges.h" + #include #include diff --git a/src/KIM/kim_param.cpp b/src/KIM/kim_param.cpp index f72df81989..c50474fe67 100644 --- a/src/KIM/kim_param.cpp +++ b/src/KIM/kim_param.cpp @@ -68,6 +68,8 @@ #include "pair_kim.h" #include "variable.h" +#include "fmt/ranges.h" + #include #include #include From 01482e7a2e0324eabc3dc0b81809573f93ce89b4 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 4 Jan 2024 22:46:40 -0500 Subject: [PATCH 161/305] update fmtlib to version 10.2.1 --- src/fmt/args.h | 13 +- src/fmt/chrono.h | 272 ++++++++-------- src/fmt/color.h | 77 +++-- src/fmt/compile.h | 17 +- src/fmt/core.h | 647 +++++++++++++++++++------------------- src/fmt/format-inl.h | 161 +++++----- src/fmt/format.h | 718 ++++++++++++++++++++++--------------------- src/fmt/os.h | 64 ++-- src/fmt/ostream.h | 100 ++++-- src/fmt/printf.h | 34 +- src/fmt/ranges.h | 117 +++++-- src/fmt/std.h | 154 +++++++--- src/fmt/xchar.h | 26 +- src/fmtlib_os.cpp | 67 ++-- 14 files changed, 1384 insertions(+), 1083 deletions(-) diff --git a/src/fmt/args.h b/src/fmt/args.h index 2d684e7cc1..b77a2d0661 100644 --- a/src/fmt/args.h +++ b/src/fmt/args.h @@ -12,7 +12,7 @@ #include // std::unique_ptr #include -#include "core.h" +#include "format.h" // std_string_view FMT_BEGIN_NAMESPACE @@ -22,8 +22,9 @@ template struct is_reference_wrapper : std::false_type {}; template struct is_reference_wrapper> : std::true_type {}; -template const T& unwrap(const T& v) { return v; } -template const T& unwrap(const std::reference_wrapper& v) { +template auto unwrap(const T& v) -> const T& { return v; } +template +auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(v); } @@ -50,7 +51,7 @@ class dynamic_arg_list { std::unique_ptr> head_; public: - template const T& push(const Arg& arg) { + template auto push(const Arg& arg) -> const T& { auto new_node = std::unique_ptr>(new typed_node(arg)); auto& value = new_node->value; new_node->next = std::move(head_); @@ -110,14 +111,14 @@ class dynamic_format_arg_store friend class basic_format_args; - unsigned long long get_types() const { + auto get_types() const -> unsigned long long { return detail::is_unpacked_bit | data_.size() | (named_info_.empty() ? 0ULL : static_cast(detail::has_named_args_bit)); } - const basic_format_arg* data() const { + auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } diff --git a/src/fmt/chrono.h b/src/fmt/chrono.h index ff3e1445b9..9d54574e16 100644 --- a/src/fmt/chrono.h +++ b/src/fmt/chrono.h @@ -18,7 +18,7 @@ #include #include -#include "format.h" +#include "ostream.h" // formatbuf FMT_BEGIN_NAMESPACE @@ -72,7 +72,8 @@ template ::value && std::numeric_limits::is_signed == std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -101,7 +102,8 @@ template ::value && std::numeric_limits::is_signed != std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -133,7 +135,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; return from; } // function @@ -154,7 +157,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { // clang-format on template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; using T = std::numeric_limits; static_assert(std::is_floating_point::value, "From must be floating"); @@ -176,7 +179,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; static_assert(std::is_floating_point::value, "From must be floating"); return from; @@ -188,8 +191,8 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { template ::value), FMT_ENABLE_IF(std::is_integral::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { using From = std::chrono::duration; ec = 0; // the basic idea is that we need to convert from count() in the from type @@ -240,8 +243,8 @@ To safe_duration_cast(std::chrono::duration from, template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { using From = std::chrono::duration; ec = 0; if (std::isnan(from.count())) { @@ -321,12 +324,12 @@ To safe_duration_cast(std::chrono::duration from, namespace detail { template struct null {}; -inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } -inline null<> localtime_s(...) { return null<>(); } -inline null<> gmtime_r(...) { return null<>(); } -inline null<> gmtime_s(...) { return null<>(); } +inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } +inline auto localtime_s(...) -> null<> { return null<>(); } +inline auto gmtime_r(...) -> null<> { return null<>(); } +inline auto gmtime_s(...) -> null<> { return null<>(); } -inline const std::locale& get_classic_locale() { +inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; } @@ -336,8 +339,6 @@ template struct codecvt_result { CodeUnit buf[max_size]; CodeUnit* end; }; -template -constexpr const size_t codecvt_result::max_size; template void write_codecvt(codecvt_result& out, string_view in_buf, @@ -408,8 +409,7 @@ inline void do_write(buffer& buf, const std::tm& time, auto&& format_buf = formatbuf>(buf); auto&& os = std::basic_ostream(&format_buf); os.imbue(loc); - using iterator = std::ostreambuf_iterator; - const auto& facet = std::use_facet>(loc); + const auto& facet = std::use_facet>(loc); auto end = facet.put(os, os, Char(' '), &time, format, modifier); if (end.failed()) FMT_THROW(format_error("failed to format time")); } @@ -432,6 +432,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } +template +struct is_same_arithmetic_type + : public std::integral_constant::value && + std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)> { +}; + +template < + typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(is_same_arithmetic_type::value)> +auto fmt_duration_cast(std::chrono::duration from) -> To { +#if FMT_SAFE_DURATION_CAST + // Throwing version of safe_duration_cast is only available for + // integer to integer or float to float casts. + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +#else + // Standard duration cast, may overflow. + return std::chrono::duration_cast(from); +#endif +} + +template < + typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(!is_same_arithmetic_type::value)> +auto fmt_duration_cast(std::chrono::duration from) -> To { + // Mixed integer <-> float cast is not supported by safe_duration_cast. + return std::chrono::duration_cast(from); +} + +template +auto to_time_t( + std::chrono::time_point time_point) + -> std::time_t { + // Cannot use std::chrono::system_clock::to_time_t since this would first + // require a cast to std::chrono::system_clock::time_point, which could + // overflow. + return fmt_duration_cast>( + time_point.time_since_epoch()) + .count(); +} } // namespace detail FMT_BEGIN_EXPORT @@ -441,29 +486,29 @@ FMT_BEGIN_EXPORT expressed in local time. Unlike ``std::localtime``, this function is thread-safe on most platforms. */ -inline std::tm localtime(std::time_t time) { +inline auto localtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; dispatcher(std::time_t t) : time_(t) {} - bool run() { + auto run() -> bool { using namespace fmt::detail; return handle(localtime_r(&time_, &tm_)); } - bool handle(std::tm* tm) { return tm != nullptr; } + auto handle(std::tm* tm) -> bool { return tm != nullptr; } - bool handle(detail::null<>) { + auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(localtime_s(&tm_, &time_)); } - bool fallback(int res) { return res == 0; } + auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - bool fallback(detail::null<>) { + auto fallback(detail::null<>) -> bool { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); if (tm) tm_ = *tm; @@ -480,8 +525,8 @@ inline std::tm localtime(std::time_t time) { #if FMT_USE_LOCAL_TIME template inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime(std::chrono::system_clock::to_time_t( - std::chrono::current_zone()->to_sys(time))); + return localtime( + detail::to_time_t(std::chrono::current_zone()->to_sys(time))); } #endif @@ -490,29 +535,29 @@ inline auto localtime(std::chrono::local_time time) -> std::tm { expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this function is thread-safe on most platforms. */ -inline std::tm gmtime(std::time_t time) { +inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; dispatcher(std::time_t t) : time_(t) {} - bool run() { + auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } - bool handle(std::tm* tm) { return tm != nullptr; } + auto handle(std::tm* tm) -> bool { return tm != nullptr; } - bool handle(detail::null<>) { + auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } - bool fallback(int res) { return res == 0; } + auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - bool fallback(detail::null<>) { + auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; @@ -525,9 +570,11 @@ inline std::tm gmtime(std::time_t time) { return gt.tm_; } -inline std::tm gmtime( - std::chrono::time_point time_point) { - return gmtime(std::chrono::system_clock::to_time_t(time_point)); +template +inline auto gmtime( + std::chrono::time_point time_point) + -> std::tm { + return gmtime(detail::to_time_t(time_point)); } namespace detail { @@ -566,7 +613,8 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, } } -template FMT_CONSTEXPR inline const char* get_units() { +template +FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "as"; if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; @@ -584,8 +632,9 @@ template FMT_CONSTEXPR inline const char* get_units() { if (std::is_same::value) return "Ts"; if (std::is_same::value) return "Ps"; if (std::is_same::value) return "Es"; - if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "min"; if (std::is_same>::value) return "h"; + if (std::is_same>::value) return "d"; return nullptr; } @@ -621,9 +670,8 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt { // Parses a put_time-like format string and invokes handler actions. template -FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - const Char* end, - Handler&& handler) { +FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; @@ -954,25 +1002,25 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() {} }; -inline const char* tm_wday_full_name(int wday) { +inline auto tm_wday_full_name(int wday) -> const char* { static constexpr const char* full_name_list[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; } -inline const char* tm_wday_short_name(int wday) { +inline auto tm_wday_short_name(int wday) -> const char* { static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; } -inline const char* tm_mon_full_name(int mon) { +inline auto tm_mon_full_name(int mon) -> const char* { static constexpr const char* full_name_list[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; } -inline const char* tm_mon_short_name(int mon) { +inline auto tm_mon_short_name(int mon) -> const char* { static constexpr const char* short_name_list[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", @@ -1004,21 +1052,21 @@ inline void tzset_once() { // Converts value to Int and checks that it's in the range [0, upper). template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { - FMT_ASSERT(std::is_unsigned::value || - (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), - "invalid value"); - (void)upper; +inline auto to_nonnegative_int(T value, Int upper) -> Int { + if (!std::is_unsigned::value && + (value < 0 || to_unsigned(value) > to_unsigned(upper))) { + FMT_THROW(fmt::format_error("chrono value is out of range")); + } return static_cast(value); } template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { +inline auto to_nonnegative_int(T value, Int upper) -> Int { if (value < 0 || value > static_cast(upper)) FMT_THROW(format_error("invalid value")); return static_cast(value); } -constexpr long long pow10(std::uint32_t n) { +constexpr auto pow10(std::uint32_t n) -> long long { return n == 0 ? 1 : 10 * pow10(n - 1); } @@ -1052,13 +1100,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { std::chrono::seconds::rep>::type, std::ratio<1, detail::pow10(num_fractional_digits)>>; - const auto fractional = - d - std::chrono::duration_cast(d); + const auto fractional = d - fmt_duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() - : std::chrono::duration_cast(fractional).count(); + : fmt_duration_cast(fractional).count(); auto n = static_cast>(subseconds); const int num_digits = detail::count_digits(n); @@ -1109,11 +1156,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration, num_fractional_digits = 6; } - format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), - std::fmod(val * static_cast(Duration::period::num) / - static_cast(Duration::period::den), - static_cast(60)), - num_fractional_digits); + fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); } template (l); } - // Algorithm: - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { const auto prev_year = curr_year - 1; const auto curr_p = @@ -1315,7 +1361,7 @@ class tm_writer { subsecs_(subsecs), tm_(tm) {} - OutputIt out() const { return out_; } + auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { out_ = copy_str(begin, end, out_); @@ -1579,6 +1625,7 @@ struct chrono_format_checker : null_chrono_spec_handler { template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_day_of_year() {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -1597,16 +1644,16 @@ struct chrono_format_checker : null_chrono_spec_handler { template ::value&& has_isfinite::value)> -inline bool isfinite(T) { +inline auto isfinite(T) -> bool { return true; } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return x % static_cast(y); } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return std::fmod(x, static_cast(y)); } @@ -1621,49 +1668,38 @@ template struct make_unsigned_or_unchanged { using type = typename std::make_unsigned::type; }; -#if FMT_SAFE_DURATION_CAST -// throwing version of safe_duration_cast -template -To fmt_safe_duration_cast(std::chrono::duration from) { - int ec; - To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); - return to; -} -#endif - template ::value)> -inline std::chrono::duration get_milliseconds( - std::chrono::duration d) { +inline auto get_milliseconds(std::chrono::duration d) + -> std::chrono::duration { // this may overflow and/or the result may not fit in the // target type. #if FMT_SAFE_DURATION_CAST using CommonSecondsType = typename std::common_type::type; - const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_common = fmt_duration_cast(d); const auto d_as_whole_seconds = - fmt_safe_duration_cast(d_as_common); + fmt_duration_cast(d_as_common); // this conversion should be nonproblematic const auto diff = d_as_common - d_as_whole_seconds; const auto ms = - fmt_safe_duration_cast>(diff); + fmt_duration_cast>(diff); return ms; #else - auto s = std::chrono::duration_cast(d); - return std::chrono::duration_cast(d - s); + auto s = fmt_duration_cast(d); + return fmt_duration_cast(d - s); #endif } template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int) { +auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { return write(out, val); } template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int precision) { +auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { auto specs = format_specs(); specs.precision = precision; specs.type = precision >= 0 ? presentation_type::fixed_lower @@ -1672,12 +1708,12 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { } template -OutputIt copy_unit(string_view unit, OutputIt out, Char) { +auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { return std::copy(unit.begin(), unit.end(), out); } template -OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { +auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { // This works when wchar_t is UTF-32 because units only contain characters // that have the same representation in UTF-16 and UTF-32. utf8_to_utf16 u(unit); @@ -1685,7 +1721,7 @@ OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { } template -OutputIt format_duration_unit(OutputIt out) { +auto format_duration_unit(OutputIt out) -> OutputIt { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); *out++ = '['; @@ -1752,18 +1788,12 @@ struct chrono_formatter { // this may overflow and/or the result may not fit in the // target type. -#if FMT_SAFE_DURATION_CAST // might need checked conversion (rep!=Rep) - auto tmpval = std::chrono::duration(val); - s = fmt_safe_duration_cast(tmpval); -#else - s = std::chrono::duration_cast( - std::chrono::duration(val)); -#endif + s = fmt_duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. - bool handle_nan_inf() { + auto handle_nan_inf() -> bool { if (isfinite(val)) { return false; } @@ -1780,17 +1810,22 @@ struct chrono_formatter { return true; } - Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + auto days() const -> Rep { return static_cast(s.count() / 86400); } + auto hour() const -> Rep { + return static_cast(mod((s.count() / 3600), 24)); + } - Rep hour12() const { + auto hour12() const -> Rep { Rep hour = static_cast(mod((s.count() / 3600), 12)); return hour <= 0 ? 12 : hour; } - Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } - Rep second() const { return static_cast(mod(s.count(), 60)); } + auto minute() const -> Rep { + return static_cast(mod((s.count() / 60), 60)); + } + auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } - std::tm time() const { + auto time() const -> std::tm { auto time = std::tm(); time.tm_hour = to_nonnegative_int(hour(), 24); time.tm_min = to_nonnegative_int(minute(), 60); @@ -1858,10 +1893,14 @@ struct chrono_formatter { void on_dec0_week_of_year(numeric_system) {} void on_dec1_week_of_year(numeric_system) {} void on_iso_week_of_year(numeric_system) {} - void on_day_of_year() {} void on_day_of_month(numeric_system) {} void on_day_of_month_space(numeric_system) {} + void on_day_of_year() { + if (handle_nan_inf()) return; + write(days(), 0); + } + void on_24_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; @@ -1968,7 +2007,7 @@ class weekday { weekday() = default; explicit constexpr weekday(unsigned wd) noexcept : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr unsigned c_encoding() const noexcept { return value; } + constexpr auto c_encoding() const noexcept -> unsigned { return value; } }; class year_month_day {}; @@ -2083,25 +2122,22 @@ struct formatter, period::num != 1 || period::den != 1 || std::is_floating_point::value)) { const auto epoch = val.time_since_epoch(); - auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); + auto subsecs = detail::fmt_duration_cast( + epoch - detail::fmt_duration_cast(epoch)); if (subsecs.count() < 0) { auto second = - std::chrono::duration_cast(std::chrono::seconds(1)); + detail::fmt_duration_cast(std::chrono::seconds(1)); if (epoch.count() < ((Duration::min)() + second).count()) FMT_THROW(format_error("duration is too small")); subsecs += second; val -= second; } - return formatter::do_format( - gmtime(std::chrono::time_point_cast(val)), ctx, - &subsecs); + return formatter::do_format(gmtime(val), ctx, &subsecs); } - return formatter::format( - gmtime(std::chrono::time_point_cast(val)), ctx); + return formatter::format(gmtime(val), ctx); } }; @@ -2120,17 +2156,13 @@ struct formatter, Char> if (period::num != 1 || period::den != 1 || std::is_floating_point::value) { const auto epoch = val.time_since_epoch(); - const auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); + const auto subsecs = detail::fmt_duration_cast( + epoch - detail::fmt_duration_cast(epoch)); - return formatter::do_format( - localtime(std::chrono::time_point_cast(val)), - ctx, &subsecs); + return formatter::do_format(localtime(val), ctx, &subsecs); } - return formatter::format( - localtime(std::chrono::time_point_cast(val)), - ctx); + return formatter::format(localtime(val), ctx); } }; #endif diff --git a/src/fmt/color.h b/src/fmt/color.h index 8697e1ca0b..464519e582 100644 --- a/src/fmt/color.h +++ b/src/fmt/color.h @@ -233,7 +233,7 @@ class text_style { FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept : set_foreground_color(), set_background_color(), ems(em) {} - FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& { if (!set_foreground_color) { set_foreground_color = rhs.set_foreground_color; foreground_color = rhs.foreground_color; @@ -257,29 +257,29 @@ class text_style { return *this; } - friend FMT_CONSTEXPR text_style operator|(text_style lhs, - const text_style& rhs) { + friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs) + -> text_style { return lhs |= rhs; } - FMT_CONSTEXPR bool has_foreground() const noexcept { + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { return set_foreground_color; } - FMT_CONSTEXPR bool has_background() const noexcept { + FMT_CONSTEXPR auto has_background() const noexcept -> bool { return set_background_color; } - FMT_CONSTEXPR bool has_emphasis() const noexcept { + FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { return static_cast(ems) != 0; } - FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); return foreground_color; } - FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); return background_color; } - FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); return ems; } @@ -297,9 +297,11 @@ class text_style { } } - friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; + friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept + -> text_style; - friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept + -> text_style; detail::color_type foreground_color; detail::color_type background_color; @@ -309,16 +311,19 @@ class text_style { }; /** Creates a text style from the foreground (text) color. */ -FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { +FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept + -> text_style { return text_style(true, foreground); } /** Creates a text style from the background color. */ -FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { +FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept + -> text_style { return text_style(false, background); } -FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { +FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept + -> text_style { return text_style(lhs) | rhs; } @@ -384,8 +389,8 @@ template struct ansi_color_escape { } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } - FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { + FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } + FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { return buffer + std::char_traits::length(buffer); } @@ -400,25 +405,27 @@ template struct ansi_color_escape { out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } - static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept + -> bool { return static_cast(em) & static_cast(mask); } }; template -FMT_CONSTEXPR ansi_color_escape make_foreground_color( - detail::color_type foreground) noexcept { +FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept + -> ansi_color_escape { return ansi_color_escape(foreground, "\x1b[38;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_background_color( - detail::color_type background) noexcept { +FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept + -> ansi_color_escape { return ansi_color_escape(background, "\x1b[48;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { +FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept + -> ansi_color_escape { return ansi_color_escape(em); } @@ -427,9 +434,10 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg { +template struct styled_arg : detail::view { const T& value; text_style style; + styled_arg(const T& v, text_style s) : value(v), style(s) {} }; template @@ -510,9 +518,10 @@ void print(const text_style& ts, const S& format_str, const Args&... args) { } template > -inline std::basic_string vformat( +inline auto vformat( const text_style& ts, const S& format_str, - basic_format_args>> args) { + basic_format_args>> args) + -> std::basic_string { basic_memory_buffer buf; detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); return fmt::to_string(buf); @@ -531,8 +540,8 @@ inline std::basic_string vformat( \endrst */ template > -inline std::basic_string format(const text_style& ts, const S& format_str, - const Args&... args) { +inline auto format(const text_style& ts, const S& format_str, + const Args&... args) -> std::basic_string { return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -542,9 +551,10 @@ inline std::basic_string format(const text_style& ts, const S& format_str, */ template ::value)> -OutputIt vformat_to( - OutputIt out, const text_style& ts, basic_string_view format_str, - basic_format_args>> args) { +auto vformat_to(OutputIt out, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) + -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, format_str, args); return detail::get_iterator(buf, out); @@ -562,9 +572,10 @@ OutputIt vformat_to( fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); \endrst */ -template >::value&& - detail::is_string::value> +template < + typename OutputIt, typename S, typename... Args, + bool enable = detail::is_output_iterator>::value && + detail::is_string::value> inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, Args&&... args) -> typename std::enable_if::type { diff --git a/src/fmt/compile.h b/src/fmt/compile.h index af76507f07..71fa69c67e 100644 --- a/src/fmt/compile.h +++ b/src/fmt/compile.h @@ -14,8 +14,8 @@ FMT_BEGIN_NAMESPACE namespace detail { template -FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { +FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end, + counting_iterator it) -> counting_iterator { return it + (end - begin); } @@ -57,7 +57,7 @@ struct udl_compiled_string : compiled_string { #endif template -const T& first(const T& value, const Tail&...) { +auto first(const T& value, const Tail&...) -> const T& { return value; } @@ -489,18 +489,19 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { template ::value)> -format_to_n_result format_to_n(OutputIt out, size_t n, - const S& format_str, Args&&... args) { +auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) + -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - format_to(std::back_inserter(buf), format_str, std::forward(args)...); + fmt::format_to(std::back_inserter(buf), format_str, + std::forward(args)...); return {buf.out(), buf.count()}; } template ::value)> -FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, - const Args&... args) { +FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) + -> size_t { return fmt::format_to(detail::counting_iterator(), format_str, args...) .count(); } diff --git a/src/fmt/core.h b/src/fmt/core.h index 9f7de781bb..6a53b8c52c 100644 --- a/src/fmt/core.h +++ b/src/fmt/core.h @@ -8,17 +8,15 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ -#include // std::byte -#include // std::FILE -#include // std::strlen -#include -#include -#include // std::addressof -#include -#include +#include // std::byte +#include // std::FILE +#include // std::strlen +#include // CHAR_BIT +#include // std::string +#include // std::enable_if // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100100 +#define FMT_VERSION 100200 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -58,6 +56,12 @@ # define FMT_MSC_WARNING(...) #endif +#ifdef _GLIBCXX_RELEASE +# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE +#else +# define FMT_GLIBCXX_RELEASE 0 +#endif + #ifdef _MSVC_LANG # define FMT_CPLUSPLUS _MSVC_LANG #else @@ -88,6 +92,20 @@ #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR @@ -105,30 +123,17 @@ # define FMT_CONSTEXPR #endif -#if ((FMT_CPLUSPLUS >= 202002L) && \ - (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +#if (FMT_CPLUSPLUS >= 202002L || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \ + ((!FMT_GLIBCXX_RELEASE || FMT_GLIBCXX_RELEASE >= 10) && \ + (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \ + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1928)) && \ + defined(__cpp_lib_is_constant_evaluated) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 #endif -// Check if constexpr std::char_traits<>::{compare,length} are supported. -#if defined(__GLIBCXX__) -# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ - _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#endif -#ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS -#endif - // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ @@ -191,33 +196,25 @@ # define FMT_END_EXPORT #endif +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +#else +# define FMT_VISIBILITY(value) +#endif + #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_LIB_EXPORT +# if defined(FMT_LIB_EXPORT) # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) # endif -#else -# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -# if defined(__GNUC__) || defined(__clang__) -# define FMT_API __attribute__((visibility("default"))) -# endif -# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") #endif #ifndef FMT_API # define FMT_API #endif -// libc++ supports string_view in pre-c++17. -#if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -# include -# define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW -#endif - #ifndef FMT_UNICODE # define FMT_UNICODE !FMT_MSC_VERSION #endif @@ -228,8 +225,9 @@ __apple_build_version__ >= 14000029L) && \ FMT_CPLUSPLUS >= 202002L) || \ (defined(__cpp_consteval) && \ - (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) -// consteval is broken in MSVC before VS2022 and Apple clang before 14. + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1929)) +// consteval is broken in MSVC before VS2019 version 16.10 and Apple clang +// before 14. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL # else @@ -248,6 +246,15 @@ # endif #endif +// GCC < 5 requires this-> in decltype. +#ifndef FMT_DECLTYPE_THIS +# if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +# define FMT_DECLTYPE_THIS this-> +# else +# define FMT_DECLTYPE_THIS +# endif +#endif + // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") #if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ @@ -269,20 +276,57 @@ template using remove_const_t = typename std::remove_const::type; template using remove_cvref_t = typename std::remove_cv>::type; -template struct type_identity { using type = T; }; +template struct type_identity { + using type = T; +}; template using type_identity_t = typename type_identity::type; template using underlying_t = typename std::underlying_type::type; -// Checks whether T is a container with contiguous storage. -template struct is_contiguous : std::false_type {}; -template -struct is_contiguous> : std::true_type {}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { + using type = void; +}; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif struct monostate { constexpr monostate() {} }; +// An implementation of back_insert_iterator to avoid dependency on . +template class back_insert_iterator { + private: + Container* container_; + + friend auto get_container(back_insert_iterator it) -> Container& { + return *it.container_; + } + + public: + using difference_type = ptrdiff_t; + FMT_UNCHECKED_ITERATOR(back_insert_iterator); + + explicit back_insert_iterator(Container& c) : container_(&c) {} + + auto operator=(const typename Container::value_type& value) + -> back_insert_iterator& { + container_->push_back(value); + return *this; + } + auto operator*() -> back_insert_iterator& { return *this; } + auto operator++() -> back_insert_iterator& { return *this; } + auto operator++(int) -> back_insert_iterator { return *this; } +}; + +template +auto back_inserter(Container& c) -> back_insert_iterator { + return {c}; +} + // An enable_if helper to be used in template parameters which results in much // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // to workaround a bug in MSVC 2019 (see #1140 and #1186). @@ -310,10 +354,9 @@ template FMT_CONSTEXPR void ignore_unused(const T&...) {} constexpr FMT_INLINE auto is_constant_evaluated( bool default_value = false) noexcept -> bool { // Workaround for incompatibility between libstdc++ consteval-based -// std::is_constant_evaluated() implementation and clang-14. -// https://github.com/fmtlib/fmt/issues/3247 -#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 12 && \ +// std::is_constant_evaluated() implementation and clang-14: +// https://github.com/fmtlib/fmt/issues/3247. +#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) ignore_unused(default_value); return __builtin_is_constant_evaluated(); @@ -346,15 +389,6 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # endif #endif -#if defined(FMT_USE_STRING_VIEW) -template using std_string_view = std::basic_string_view; -#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) -template -using std_string_view = std::experimental::basic_string_view; -#else -template struct std_string_view {}; -#endif - #ifdef FMT_USE_INT128 // Do nothing. #elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ @@ -386,6 +420,15 @@ FMT_CONSTEXPR auto to_unsigned(Int value) -> return static_cast::type>(value); } +template +struct is_string_like : std::false_type {}; + +// A heuristic to detect std::string and std::string_view. +template +struct is_string_like().find_first_of( + typename T::value_type(), 0))>> : std::true_type { +}; + FMT_CONSTEXPR inline auto is_utf8() -> bool { FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; @@ -394,8 +437,33 @@ FMT_CONSTEXPR inline auto is_utf8() -> bool { return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && uchar(section[1]) == 0xA7); } + +template FMT_CONSTEXPR auto length(const Char* s) -> size_t { + size_t len = 0; + while (*s++) ++len; + return len; +} + +template +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) + -> int { + for (; n != 0; ++s1, ++s2, --n) { + if (*s1 < *s2) return -1; + if (*s1 > *s2) return 1; + } + return 0; +} } // namespace detail +template +using basic_string = + std::basic_string, std::allocator>; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; + /** An implementation of ``std::basic_string_view`` for pre-C++17. It provides a subset of the API. ``fmt::basic_string_view`` is used for format strings even @@ -420,29 +488,25 @@ template class basic_string_view { : data_(s), size_(count) {} /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst + Constructs a string reference object from a C string. */ - FMT_CONSTEXPR_CHAR_TRAITS + FMT_CONSTEXPR20 FMT_INLINE basic_string_view(const Char* s) : data_(s), size_(detail::const_check(std::is_same::value && - !detail::is_constant_evaluated(true)) + !detail::is_constant_evaluated(false)) ? std::strlen(reinterpret_cast(s)) - : std::char_traits::length(s)) {} + : detail::length(s)) {} - /** Constructs a string reference from a ``std::basic_string`` object. */ - template - FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) noexcept - : data_(s.data()), size_(s.size()) {} - - template >::value)> - FMT_CONSTEXPR basic_string_view(S s) noexcept + /** + Constructs a string reference from a ``std::basic_string`` or a + ``std::basic_string_view`` object. + */ + template ::value&& std::is_same< + typename S::value_type, Char>::value)> + FMT_CONSTEXPR basic_string_view(const S& s) noexcept : data_(s.data()), size_(s.size()) {} /** Returns a pointer to the string data. */ @@ -463,30 +527,28 @@ template class basic_string_view { size_ -= n; } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( - basic_string_view sv) const noexcept { - return size_ >= sv.size_ && - std::char_traits::compare(data_, sv.data_, sv.size_) == 0; + FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept + -> bool { + return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { - return size_ >= 1 && std::char_traits::eq(*data_, c); + FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { + return size_ >= 1 && *data_ == c; } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { + FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { return starts_with(basic_string_view(s)); } // Lexicographically compare this string reference to other. - FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { + FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { size_t str_size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, str_size); + int result = detail::compare(data_, other.data_, str_size); if (result == 0) result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); return result; } - FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, - basic_string_view rhs) - -> bool { + FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, + basic_string_view rhs) -> bool { return lhs.compare(rhs) == 0; } friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { @@ -526,21 +588,16 @@ template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { return s; } -template -inline auto to_string_view(const std::basic_string& s) - -> basic_string_view { - return s; +template ::value)> +inline auto to_string_view(const S& s) + -> basic_string_view { + return s; // std::basic_string[_view] } template constexpr auto to_string_view(basic_string_view s) -> basic_string_view { return s; } -template >::value)> -inline auto to_string_view(std_string_view s) -> basic_string_view { - return s; -} template ::value)> constexpr auto to_string_view(const S& s) -> basic_string_view { @@ -609,10 +666,10 @@ FMT_TYPE_CONSTANT(const Char*, cstring_type); FMT_TYPE_CONSTANT(basic_string_view, string_type); FMT_TYPE_CONSTANT(const void*, pointer_type); -constexpr bool is_integral_type(type t) { +constexpr auto is_integral_type(type t) -> bool { return t > type::none_type && t <= type::last_integer_type; } -constexpr bool is_arithmetic_type(type t) { +constexpr auto is_arithmetic_type(type t) -> bool { return t > type::none_type && t <= type::last_numeric_type; } @@ -635,21 +692,10 @@ enum { cstring_set = set(type::cstring_type), pointer_set = set(type::pointer_type) }; - -FMT_NORETURN FMT_API void throw_format_error(const char* message); - -struct error_handler { - constexpr error_handler() = default; - - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN void on_error(const char* message) { - throw_format_error(message); - } -}; } // namespace detail /** Throws ``format_error`` with a given message. */ -using detail::throw_format_error; +FMT_NORETURN FMT_API void throw_format_error(const char* message); /** String's character type. */ template using char_t = typename detail::char_t_impl::type; @@ -701,7 +747,7 @@ template class basic_format_parse_context { */ FMT_CONSTEXPR auto next_arg_id() -> int { if (next_arg_id_ < 0) { - detail::throw_format_error( + throw_format_error( "cannot switch from manual to automatic argument indexing"); return 0; } @@ -716,7 +762,7 @@ template class basic_format_parse_context { */ FMT_CONSTEXPR void check_arg_id(int id) { if (next_arg_id_ > 0) { - detail::throw_format_error( + throw_format_error( "cannot switch from automatic to manual argument indexing"); return; } @@ -769,35 +815,6 @@ class compile_parse_context : public basic_format_parse_context { } }; -// Extracts a reference to the container from back_insert_iterator. -template -inline auto get_container(std::back_insert_iterator it) - -> Container& { - using base = std::back_insert_iterator; - struct accessor : base { - accessor(base b) : base(b) {} - using base::container; - }; - return *accessor(it).container; -} - -template -FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) - -> OutputIt { - while (begin != end) *out++ = static_cast(*begin++); - return out; -} - -template , U>::value&& is_char::value)> -FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { - if (is_constant_evaluated()) return copy_str(begin, end, out); - auto size = to_unsigned(end - begin); - if (size > 0) memcpy(out, begin, size * sizeof(U)); - return out + size; -} - /** \rst A contiguous memory buffer with an optional growing ability. It is an internal @@ -810,13 +827,18 @@ template class buffer { size_t size_; size_t capacity_; + using grow_fun = void (*)(buffer& buf, size_t capacity); + grow_fun grow_; + protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} + FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept + : size_(sz), capacity_(sz), grow_(grow) {} - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept - : ptr_(p), size_(sz), capacity_(cap) {} + FMT_CONSTEXPR20 buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, + size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; @@ -827,9 +849,6 @@ template class buffer { capacity_ = buf_capacity; } - /** Increases the buffer capacity to hold at least *capacity* elements. */ - virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; - public: using value_type = T; using const_reference = const T&; @@ -868,7 +887,7 @@ template class buffer { // for at least one additional element either by increasing the capacity or by // flushing the buffer if it is full. FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); + if (new_capacity > capacity_) grow_(*this, new_capacity); } FMT_CONSTEXPR20 void push_back(const T& value) { @@ -917,22 +936,25 @@ class iterator_buffer final : public Traits, public buffer { enum { buffer_size = 256 }; T data_[buffer_size]; - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == buffer_size) flush(); + static FMT_CONSTEXPR20 void grow(buffer& buf, size_t) { + if (buf.size() == buffer_size) static_cast(buf).flush(); } void flush() { auto size = this->size(); this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); + const T* begin = data_; + const T* end = begin + this->limit(size); + while (begin != end) *out_++ = *begin++; } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} + : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} iterator_buffer(iterator_buffer&& other) - : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} + : Traits(other), + buffer(grow, data_, 0, buffer_size), + out_(other.out_) {} ~iterator_buffer() { flush(); } auto out() -> OutputIt { @@ -951,9 +973,9 @@ class iterator_buffer final enum { buffer_size = 256 }; T data_[buffer_size]; - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == this->capacity()) flush(); + static FMT_CONSTEXPR20 void grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) + static_cast(buf).flush(); } void flush() { @@ -967,10 +989,10 @@ class iterator_buffer final public: explicit iterator_buffer(T* out, size_t n = buffer_size) - : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} + : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} iterator_buffer(iterator_buffer&& other) : fixed_buffer_traits(other), - buffer(std::move(other)), + buffer(static_cast(other)), out_(other.out_) { if (this->data() != out_) { this->set(data_, buffer_size); @@ -989,38 +1011,37 @@ class iterator_buffer final }; template class iterator_buffer final : public buffer { - protected: - FMT_CONSTEXPR20 void grow(size_t) override {} - public: - explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} + explicit iterator_buffer(T* out, size_t = 0) + : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} auto out() -> T* { return &*this->end(); } }; // A buffer that writes to a container with the contiguous storage. template -class iterator_buffer, +class iterator_buffer, enable_if_t::value, typename Container::value_type>> final : public buffer { private: + using value_type = typename Container::value_type; Container& container_; - protected: - FMT_CONSTEXPR20 void grow(size_t capacity) override { - container_.resize(capacity); - this->set(&container_[0], capacity); + static FMT_CONSTEXPR20 void grow(buffer& buf, size_t capacity) { + auto& self = static_cast(buf); + self.container_.resize(capacity); + self.set(&self.container_[0], capacity); } public: explicit iterator_buffer(Container& c) - : buffer(c.size()), container_(c) {} - explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) + : buffer(grow, c.size()), container_(c) {} + explicit iterator_buffer(back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} - auto out() -> std::back_insert_iterator { - return std::back_inserter(container_); + auto out() -> back_insert_iterator { + return fmt::back_inserter(container_); } }; @@ -1031,15 +1052,14 @@ template class counting_buffer final : public buffer { T data_[buffer_size]; size_t count_ = 0; - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() != buffer_size) return; - count_ += this->size(); - this->clear(); + static FMT_CONSTEXPR20 void grow(buffer& buf, size_t) { + if (buf.size() != buffer_size) return; + static_cast(buf).count_ += buf.size(); + buf.clear(); } public: - counting_buffer() : buffer(data_, 0, buffer_size) {} + counting_buffer() : buffer(grow, data_, 0, buffer_size) {} auto count() -> size_t { return count_ + this->size(); } }; @@ -1053,7 +1073,7 @@ FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { using context = detail::compile_parse_context; if (id >= static_cast(this)->num_args()) - detail::throw_format_error("argument not found"); + throw_format_error("argument not found"); } } @@ -1085,18 +1105,29 @@ template using has_formatter = std::is_constructible>; -// An output iterator that appends to a buffer. -// It is used to reduce symbol sizes for the common case. -class appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; +// An output iterator that appends to a buffer. It is used instead of +// back_insert_iterator to reduce symbol sizes for the common case. +class appender { + private: + detail::buffer* buffer_; + + friend auto get_container(appender app) -> detail::buffer& { + return *app.buffer_; + } public: - using std::back_insert_iterator>::back_insert_iterator; - appender(base it) noexcept : base(it) {} + using difference_type = ptrdiff_t; FMT_UNCHECKED_ITERATOR(appender); - auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } + appender(detail::buffer& buf) : buffer_(&buf) {} + + auto operator=(char c) -> appender& { + buffer_->push_back(c); + return *this; + } + auto operator*() -> appender& { return *this; } + auto operator++() -> appender& { return *this; } + auto operator++(int) -> appender { return *this; } }; namespace detail { @@ -1119,7 +1150,7 @@ constexpr auto has_const_formatter() -> bool { template using buffer_appender = conditional_t::value, appender, - std::back_insert_iterator>>; + back_insert_iterator>>; // Maps an output iterator to a buffer. template @@ -1128,7 +1159,7 @@ auto get_buffer(OutputIt out) -> iterator_buffer { } template , Buf>::value)> -auto get_buffer(std::back_insert_iterator out) -> buffer& { +auto get_buffer(back_insert_iterator out) -> buffer& { return get_container(out); } @@ -1293,7 +1324,13 @@ template class value { template FMT_CONSTEXPR20 FMT_INLINE value(T& val) { using value_type = remove_const_t; - custom.value = const_cast(std::addressof(val)); + // T may overload operator& e.g. std::vector::reference in libc++. +#ifdef __cpp_if_constexpr + if constexpr (std::is_same::value) + custom.value = const_cast(&val); +#endif + if (!is_constant_evaluated()) + custom.value = const_cast(&reinterpret_cast(val)); // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. @@ -1314,6 +1351,7 @@ template class value { parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t(), const T, T>; + // Calling format through a mutable reference is deprecated. ctx.advance_to(f.format(*static_cast(arg), ctx)); } }; @@ -1327,7 +1365,7 @@ using ulong_type = conditional_t; template struct format_as_result { template ::value || std::is_class::value)> - static auto map(U*) -> decltype(format_as(std::declval())); + static auto map(U*) -> remove_cvref_t()))>; static auto map(...) -> void; using type = decltype(map(static_cast(nullptr))); @@ -1444,7 +1482,8 @@ template struct arg_mapper { // Only map owning types because mapping views can be unsafe. template , FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(FMT_DECLTYPE_THIS map(U())) { return map(format_as(val)); } @@ -1468,13 +1507,14 @@ template struct arg_mapper { !is_string::value && !is_char::value && !is_named_arg::value && !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) { + FMT_CONSTEXPR FMT_INLINE auto map(T& val) + -> decltype(FMT_DECLTYPE_THIS do_map(val)) { return do_map(val); } template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(this->map(named_arg.value)) { + -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) { return map(named_arg.value); } @@ -1493,45 +1533,19 @@ enum { max_packed_args = 62 / packed_arg_bits }; enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; -template -auto copy_str(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} -template -auto copy_str(InputIt begin, InputIt end, - std::back_insert_iterator out) - -> std::back_insert_iterator { - get_container(out).append(begin, end); - return out; -} - -template -FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { - return detail::copy_str(rng.begin(), rng.end(), out); -} - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; -template using void_t = typename void_t_impl::type; -#else -template using void_t = void; -#endif - template struct is_output_iterator : std::false_type {}; +template <> struct is_output_iterator : std::true_type {}; + template struct is_output_iterator< - It, T, - void_t::iterator_category, - decltype(*std::declval() = std::declval())>> + It, T, void_t()++ = std::declval())>> : std::true_type {}; template struct is_back_insert_iterator : std::false_type {}; template -struct is_back_insert_iterator> +struct is_back_insert_iterator> : std::true_type {}; // A type-erased reference to an std::locale to avoid a heavy include. @@ -1607,8 +1621,8 @@ FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { } // namespace detail FMT_BEGIN_EXPORT -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in basic_memory_buffer. +// A formatting argument. Context is a template parameter for the compiled API +// where output can be unbuffered. template class basic_format_arg { private: detail::value value_; @@ -1618,11 +1632,6 @@ template class basic_format_arg { friend FMT_CONSTEXPR auto detail::make_arg(T& value) -> basic_format_arg; - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)); - friend class basic_format_args; friend class dynamic_format_arg_store; @@ -1660,55 +1669,68 @@ template class basic_format_arg { auto is_arithmetic() const -> bool { return detail::is_arithmetic_type(type_); } + + /** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ + template + FMT_CONSTEXPR auto visit(Visitor&& vis) -> decltype(vis(0)) { + switch (type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(value_.int_value); + case detail::type::uint_type: + return vis(value_.uint_value); + case detail::type::long_long_type: + return vis(value_.long_long_value); + case detail::type::ulong_long_type: + return vis(value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(value_.uint128_value)); + case detail::type::bool_type: + return vis(value_.bool_value); + case detail::type::char_type: + return vis(value_.char_value); + case detail::type::float_type: + return vis(value_.float_value); + case detail::type::double_type: + return vis(value_.double_value); + case detail::type::long_double_type: + return vis(value_.long_double_value); + case detail::type::cstring_type: + return vis(value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(value_.string.data, value_.string.size)); + case detail::type::pointer_type: + return vis(value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(value_.custom)); + } + return vis(monostate()); + } + + FMT_INLINE auto format_custom(const char_type* parse_begin, + typename Context::parse_context_type& parse_ctx, + Context& ctx) -> bool { + if (type_ != detail::type::custom_type) return false; + parse_ctx.advance_to(parse_begin); + value_.custom.format(value_.custom.value, parse_ctx, ctx); + return true; + } }; -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -// DEPRECATED! template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( +FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); + return arg.visit(std::forward(vis)); } // Formatting context. @@ -1748,8 +1770,8 @@ template class basic_format_context { } auto args() const -> const format_args& { return args_; } - FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } + // This function is intentionally not constexpr to give a compile-time error. + void on_error(const char* message) { throw_format_error(message); } // Returns an iterator to the beginning of the output range. FMT_CONSTEXPR auto out() -> iterator { return out_; } @@ -1831,7 +1853,7 @@ class format_arg_store // Arguments are taken by lvalue references to avoid some lifetime issues. template constexpr auto make_format_args(T&... args) - -> format_arg_store...> { + -> format_arg_store...> { return {args...}; } @@ -2107,11 +2129,8 @@ struct dynamic_format_specs : format_specs { }; // Converts a character to ASCII. Returns '\0' on conversion failure. -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} -template ::value)> +template ::value || + std::is_enum::value)> constexpr auto to_ascii(Char c) -> char { return c <= 0xff ? static_cast(c) : '\0'; } @@ -2156,11 +2175,11 @@ FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, } while (p != end && '0' <= *p && *p <= '9'); auto num_digits = p - begin; begin = p; - if (num_digits <= std::numeric_limits::digits10) - return static_cast(value); + int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) return static_cast(value); // Check for overflow. - const unsigned max = to_unsigned((std::numeric_limits::max)()); - return num_digits == std::numeric_limits::digits10 + 1 && + unsigned max = INT_MAX; + return num_digits == digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max ? static_cast(value) : error_value; @@ -2188,9 +2207,8 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, Char c = *begin; if (c >= '0' && c <= '9') { int index = 0; - constexpr int max = (std::numeric_limits::max)(); if (c != '0') - index = parse_nonnegative_int(begin, end, max); + index = parse_nonnegative_int(begin, end, INT_MAX); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) @@ -2309,9 +2327,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( dynamic_format_specs& specs; type arg_type; - FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { - if (!in(arg_type, set)) throw_format_error("invalid format specifier"); - specs.type = type; + FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { + if (!in(arg_type, set)) { + if (arg_type == type::none_type) return begin; + throw_format_error("invalid format specifier"); + } + specs.type = pres_type; return begin + 1; } } parse_presentation_type{begin, specs, arg_type}; @@ -2328,6 +2349,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( case '+': case '-': case ' ': + if (arg_type == type::none_type) return begin; enter_state(state::sign, in(arg_type, sint_set | float_set)); switch (c) { case '+': @@ -2343,14 +2365,17 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( ++begin; break; case '#': + if (arg_type == type::none_type) return begin; enter_state(state::hash, is_arithmetic_type(arg_type)); specs.alt = true; ++begin; break; case '0': enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) + if (!is_arithmetic_type(arg_type)) { + if (arg_type == type::none_type) return begin; throw_format_error("format specifier requires numeric argument"); + } if (specs.align == align::none) { // Ignore 0 if align is specified for compatibility with std::format. specs.align = align::numeric; @@ -2372,12 +2397,14 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); break; case '.': + if (arg_type == type::none_type) return begin; enter_state(state::precision, in(arg_type, float_set | string_set | cstring_set)); begin = parse_precision(begin, end, specs.precision, specs.precision_ref, ctx); break; case 'L': + if (arg_type == type::none_type) return begin; enter_state(state::locale, is_arithmetic_type(arg_type)); specs.localized = true; ++begin; @@ -2411,6 +2438,8 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( case 'G': return parse_presentation_type(pres::general_upper, float_set); case 'c': + if (arg_type == type::bool_type) + throw_format_error("invalid format specifier"); return parse_presentation_type(pres::chr, integral_set); case 's': return parse_presentation_type(pres::string, @@ -2550,9 +2579,9 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) decltype(arg_mapper().map(std::declval())), typename strip_named_arg::type>; // LAMMPS customization. Fails to compile with (some) Intel compilers -#if defined(__cpp_if_constexpr) && 0 - if constexpr (std::is_default_constructible_v< - formatter>) { +#if defined(__cpp_if_constexpr) && 1 + if constexpr (std::is_default_constructible< + formatter>::value) { return formatter().parse(ctx); } else { type_is_unformattable_for _; @@ -2675,9 +2704,11 @@ void check_format_string(S format_str) { template struct vformat_args { using type = basic_format_args< - basic_format_context>, Char>>; + basic_format_context>, Char>>; +}; +template <> struct vformat_args { + using type = format_args; }; -template <> struct vformat_args { using type = format_args; }; // Use vformat_args and avoid type_identity to keep symbols short. template @@ -2779,7 +2810,7 @@ using format_string = basic_format_string...>; inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } #endif -FMT_API auto vformat(string_view fmt, format_args args) -> std::string; +FMT_API auto vformat(string_view fmt, format_args args) -> basic_string; /** \rst @@ -2794,7 +2825,7 @@ FMT_API auto vformat(string_view fmt, format_args args) -> std::string; */ template FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) - -> std::string { + -> basic_string { return vformat(fmt, fmt::make_format_args(args...)); } @@ -2816,7 +2847,7 @@ auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { **Example**:: auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); + fmt::format_to(fmt::back_inserter(out), "{}", 42); \endrst */ template #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) # include // _isatty #endif @@ -36,10 +36,6 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { std::terminate(); } -FMT_FUNC void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); -} - FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { // Report error code making sure that the output fits into @@ -58,8 +54,8 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); auto it = buffer_appender(out); if (message.size() <= inline_buffer_size - error_code_size) - format_to(it, FMT_STRING("{}{}"), message, SEP); - format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); + fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); FMT_ASSERT(out.size() <= inline_buffer_size, ""); } @@ -73,9 +69,8 @@ FMT_FUNC void report_error(format_func func, int error_code, } // A wrapper around fwrite that throws on error. -inline void fwrite_fully(const void* ptr, size_t size, size_t count, - FILE* stream) { - size_t written = std::fwrite(ptr, size, count, stream); +inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) { + size_t written = std::fwrite(ptr, 1, count, stream); if (written < count) FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } @@ -86,7 +81,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { static_assert(std::is_same::value, ""); } -template Locale locale_ref::get() const { +template auto locale_ref::get() const -> Locale { static_assert(std::is_same::value, ""); return locale_ ? *static_cast(locale_) : std::locale(); } @@ -98,7 +93,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } -template FMT_FUNC Char decimal_point_impl(locale_ref loc) { +template +FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { return std::use_facet>(loc.get()) .decimal_point(); } @@ -127,6 +123,10 @@ FMT_FUNC auto write_loc(appender out, loc_value value, } } // namespace detail +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); +} + template typename Locale::id format_facet::id; #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -144,24 +144,25 @@ FMT_API FMT_FUNC auto format_facet::do_put( } #endif -FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, - format_args args) { +FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error { auto ec = std::error_code(error_code, std::generic_category()); return std::system_error(ec, vformat(fmt, args)); } namespace detail { -template inline bool operator==(basic_fp x, basic_fp y) { +template +inline auto operator==(basic_fp x, basic_fp y) -> bool { return x.f == y.f && x.e == y.e; } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { +FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { +FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -170,14 +171,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { namespace dragonbox { // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { return umul128_upper64(static_cast(x) << 32, y); } // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_lower128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint64_t high = x * y.high(); uint128_fallback high_low = umul128(x, y.low()); return {high + high_low.high(), high_low.low()}; @@ -185,12 +186,12 @@ inline uint128_fallback umul192_lower128(uint64_t x, // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { return x * y; } // Various fast log computations. -inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { +inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); return (e * 631305 - 261663) >> 21; } @@ -204,7 +205,7 @@ FMT_INLINE_VARIABLE constexpr struct { // divisible by pow(10, N). // Precondition: n <= pow(10, N + 1). template -bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { +auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { // The numbers below are chosen such that: // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, // 2. nm mod 2^k < m if and only if n is divisible by d, @@ -229,7 +230,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) noexcept { +template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { constexpr auto info = div_small_pow10_infos[N - 1]; FMT_ASSERT(n <= info.divisor * 10, "n is too large"); constexpr uint32_t magic_number = @@ -238,12 +239,12 @@ template uint32_t small_division_by_pow10(uint32_t n) noexcept { } // Computes floor(n / 10^(kappa + 1)) (float) -inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { // 1374389535 = ceil(2^37/100) return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { // 2361183241434822607 = ceil(2^(64+7)/1000) return umul128_upper64(n, 2361183241434822607ull) >> 7; } @@ -255,7 +256,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr const uint64_t pow10_significands[] = { @@ -297,20 +298,23 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul96_upper64(u, cache); return {static_cast(r >> 32), static_cast(r) == 0}; } - static uint32_t compute_delta(const cache_entry_type& cache, - int beta) noexcept { + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { return static_cast(cache >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -319,22 +323,22 @@ template <> struct cache_accessor { static_cast(r >> (32 - beta)) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache - (cache >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache + (cache >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (static_cast( cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / @@ -346,7 +350,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128_fallback; - static uint128_fallback get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint128_fallback { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); @@ -985,8 +989,7 @@ template <> struct cache_accessor { {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, - { 0xdb68c2ca82ed2a05, - 0xa67398db9f6820e2 } + {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, @@ -1071,19 +1074,22 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul192_upper128(u, cache); return {r.high(), r.low() == 0}; } - static uint32_t compute_delta(cache_entry_type const& cache, - int beta) noexcept { + static auto compute_delta(cache_entry_type const& cache, int beta) noexcept + -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -1092,35 +1098,35 @@ template <> struct cache_accessor { ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() - (cache.high() >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() + (cache.high() >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; -FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { +FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback { return cache_accessor::get_cached_power(k); } // Various integer checks template -bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { +auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_upper_threshold = 3; return exponent >= case_shorter_interval_left_endpoint_lower_threshold && @@ -1132,7 +1138,7 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { FMT_ASSERT(n != 0, ""); // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. constexpr uint32_t mod_inv_5 = 0xcccccccd; - constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 + constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5 while (true) { auto q = rotr(n * mod_inv_25, 2); @@ -1168,7 +1174,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { // If n is not divisible by 10^8, work with n itself. constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd; - constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5 + constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5 int s = 0; while (true) { @@ -1234,7 +1240,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { return ret_value; } -template decimal_fp to_decimal(T x) noexcept { +template auto to_decimal(T x) noexcept -> decimal_fp { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -1373,15 +1379,15 @@ template <> struct formatter { for (auto i = n.bigits_.size(); i > 0; --i) { auto value = n.bigits_[i - 1u]; if (first) { - out = format_to(out, FMT_STRING("{:x}"), value); + out = fmt::format_to(out, FMT_STRING("{:x}"), value); first = false; continue; } - out = format_to(out, FMT_STRING("{:08x}"), value); + out = fmt::format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) - out = format_to(out, FMT_STRING("p{}"), - n.exp_ * detail::bigint::bigit_bits); + out = fmt::format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); return out; } }; @@ -1417,7 +1423,7 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -FMT_FUNC std::string vformat(string_view fmt, format_args args) { +FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. auto buffer = memory_buffer(); @@ -1426,33 +1432,38 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { } namespace detail { -#ifndef _WIN32 -FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } +#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) +FMT_FUNC auto write_console(int, string_view) -> bool { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -FMT_FUNC bool write_console(std::FILE* f, string_view text) { - auto fd = _fileno(f); - if (!_isatty(fd)) return false; +FMT_FUNC bool write_console(int fd, string_view text) { auto u16 = utf8_to_utf16(text); - auto written = dword(); return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), - static_cast(u16.size()), &written, nullptr) != 0; + static_cast(u16.size()), nullptr, nullptr) != 0; } +#endif +#ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); - detail::vformat_to(buffer, fmt, - basic_format_args>(args)); - fwrite_fully(buffer.data(), 1, buffer.size(), f); + detail::vformat_to(buffer, fmt, args); + fwrite_fully(buffer.data(), buffer.size(), f); } #endif FMT_FUNC void print(std::FILE* f, string_view text) { - if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); +#ifdef _WIN32 + int fd = _fileno(f); + if (_isatty(fd)) { + std::fflush(f); + if (write_console(fd, text)) return; + } +#endif + fwrite_fully(text.data(), text.size(), f); } } // namespace detail diff --git a/src/fmt/format.h b/src/fmt/format.h index 87a34b972c..8cdf95b7bd 100644 --- a/src/fmt/format.h +++ b/src/fmt/format.h @@ -37,17 +37,28 @@ #include // uint32_t #include // std::memcpy #include // std::initializer_list -#include // std::numeric_limits -#include // std::uninitialized_copy -#include // std::runtime_error -#include // std::system_error +#include +#include // std::numeric_limits +#include // std::uninitialized_copy +#include // std::runtime_error +#include // std::system_error #ifdef __cpp_lib_bit_cast -# include // std::bitcast +# include // std::bit_cast #endif #include "core.h" +// libc++ supports string_view in pre-c++17. +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline #else @@ -65,25 +76,11 @@ # define FMT_FALLTHROUGH #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VERSION -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - #ifndef FMT_NO_UNIQUE_ADDRESS # if FMT_CPLUSPLUS >= 202002L # if FMT_HAS_CPP_ATTRIBUTE(no_unique_address) # define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485) +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). # elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION # define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] # endif @@ -93,10 +90,11 @@ # define FMT_NO_UNIQUE_ADDRESS #endif -#if FMT_GCC_VERSION || defined(__clang__) -# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +// Visibility when compiled as a shared library/object. +#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value) #else -# define FMT_VISIBILITY(value) +# define FMT_SO_VISIBILITY(value) #endif #ifdef __has_builtin @@ -152,7 +150,10 @@ FMT_END_NAMESPACE #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ +// +// GCC before 4.9 requires a space in `operator"" _a` which is invalid in later +// compiler versions. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 409 || \ FMT_MSC_VERSION >= 1900) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) # define FMT_USE_USER_DEFINED_LITERALS 1 @@ -272,20 +273,19 @@ inline auto ctzll(uint64_t x) -> int { FMT_END_NAMESPACE #endif +namespace std { +template <> struct iterator_traits { + using value_type = void; + using iterator_category = std::output_iterator_tag; +}; +template +struct iterator_traits> { + using value_type = void; + using iterator_category = std::output_iterator_tag; +}; +} // namespace std + FMT_BEGIN_NAMESPACE - -template struct disjunction : std::false_type {}; -template struct disjunction

    : P {}; -template -struct disjunction - : conditional_t> {}; - -template struct conjunction : std::true_type {}; -template struct conjunction

    : P {}; -template -struct conjunction - : conditional_t, P1> {}; - namespace detail { FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { @@ -295,6 +295,15 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { #endif } +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + template struct string_literal { static constexpr CharT value[sizeof...(C)] = {C...}; constexpr operator basic_string_view() const { @@ -307,37 +316,6 @@ template constexpr CharT string_literal::value[sizeof...(C)]; #endif -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const char_type* s, streamsize count) -> streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - // Implementation of std::bit_cast for pre-C++20. template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { @@ -373,8 +351,8 @@ class uint128_fallback { constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} - constexpr uint64_t high() const noexcept { return hi_; } - constexpr uint64_t low() const noexcept { return lo_; } + constexpr auto high() const noexcept -> uint64_t { return hi_; } + constexpr auto low() const noexcept -> uint64_t { return lo_; } template ::value)> constexpr explicit operator T() const { @@ -450,7 +428,7 @@ class uint128_fallback { hi_ &= n.hi_; } - FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& { if (is_constant_evaluated()) { lo_ += n; hi_ += (lo_ < n ? 1 : 0); @@ -546,6 +524,52 @@ FMT_INLINE void assume(bool condition) { #endif } +// Extracts a reference to the container from back_insert_iterator. +template +inline auto get_container(std::back_insert_iterator it) + -> Container& { + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; + }; + return *accessor(it).container; +} + +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template , U>::value&& is_char::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + if (size > 0) memcpy(out, begin, size * sizeof(U)); + return out + size; +} + +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} +template +auto copy_str(InputIt begin, InputIt end, back_insert_iterator out) + -> back_insert_iterator { + get_container(out).append(begin, end); + return out; +} + +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), out); +} + // An approximation of iterator_t for pre-C++20 systems. template using iterator_t = decltype(std::begin(std::declval())); @@ -740,7 +764,7 @@ inline auto compute_width(basic_string_view s) -> size_t { } // Computes approximate display width of a UTF-8 string. -FMT_CONSTEXPR inline size_t compute_width(string_view s) { +FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { size_t num_code_points = 0; // It is not a lambda for compatibility with C++14. struct count_code_points { @@ -787,12 +811,17 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { // Calculates the index of the nth code point in a UTF-8 string. inline auto code_point_index(string_view s, size_t n) -> size_t { - const char* data = s.data(); - size_t num_code_points = 0; - for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; - } - return s.size(); + size_t result = s.size(); + const char* begin = s.begin(); + for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { + if (n != 0) { + --n; + return true; + } + result = to_unsigned(sv.begin() - begin); + return false; + }); + return result; } inline auto code_point_index(basic_string_view s, size_t n) @@ -902,7 +931,7 @@ enum { inline_buffer_size = 500 }; **Example**:: auto out = fmt::memory_buffer(); - format_to(std::back_inserter(out), "The answer is {}.", 42); + fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); This will append the following output to the ``out`` object: @@ -929,27 +958,29 @@ class basic_memory_buffer final : public detail::buffer { } protected: - FMT_CONSTEXPR20 void grow(size_t size) override { + static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { detail::abort_fuzzing_if(size > 5000); - const size_t max_size = std::allocator_traits::max_size(alloc_); - size_t old_capacity = this->capacity(); + auto& self = static_cast(buf); + const size_t max_size = + std::allocator_traits::max_size(self.alloc_); + size_t old_capacity = buf.capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; else if (new_capacity > max_size) new_capacity = size > max_size ? size : max_size; - T* old_data = this->data(); + T* old_data = buf.data(); T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); + std::allocator_traits::allocate(self.alloc_, new_capacity); // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). - detail::assume(this->size() <= new_capacity); + detail::assume(buf.size() <= new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy_n(old_data, this->size(), new_data); - this->set(new_data, new_capacity); + std::uninitialized_copy_n(old_data, buf.size(), new_data); + self.set(new_data, new_capacity); // deallocate must not throw according to the standard, but even if it does, // the buffer already uses the new storage and will deallocate it in // destructor. - if (old_data != store_) alloc_.deallocate(old_data, old_capacity); + if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); } public: @@ -958,7 +989,7 @@ class basic_memory_buffer final : public detail::buffer { FMT_CONSTEXPR20 explicit basic_memory_buffer( const Allocator& alloc = Allocator()) - : alloc_(alloc) { + : detail::buffer(grow), alloc_(alloc) { this->set(store_, SIZE); if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } @@ -990,7 +1021,8 @@ class basic_memory_buffer final : public detail::buffer { of the other object to it. \endrst */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept + : detail::buffer(grow) { move(other); } @@ -1018,7 +1050,6 @@ class basic_memory_buffer final : public detail::buffer { /** Increases the buffer capacity to *new_capacity*. */ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } - // Directly append data into the buffer using detail::buffer::append; template void append(const ContiguousRange& range) { @@ -1034,7 +1065,7 @@ struct is_contiguous> : std::true_type { FMT_END_EXPORT namespace detail { -FMT_API bool write_console(std::FILE* f, string_view text); +FMT_API auto write_console(int fd, string_view text) -> bool; FMT_API void print(std::FILE*, string_view); } // namespace detail @@ -1046,7 +1077,7 @@ FMT_BEGIN_EXPORT #endif /** An error reported from a formatting function. */ -class FMT_VISIBILITY("default") format_error : public std::runtime_error { +class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; @@ -1089,7 +1120,7 @@ class loc_value { loc_value(T) {} template auto visit(Visitor&& vis) -> decltype(vis(0)) { - return visit_format_arg(vis, value_); + return value_.visit(vis); } }; @@ -1153,13 +1184,13 @@ using uint32_or_64_or_128_t = template using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; -#define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ + (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ + (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr const char* digits2(size_t value) { +constexpr auto digits2(size_t value) -> const char* { // GCC generates slightly better code when value is pointer-size. return &"0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" @@ -1169,7 +1200,7 @@ constexpr const char* digits2(size_t value) { } // Sign is a template parameter to workaround a bug in gcc 4.8. -template constexpr Char sign(Sign s) { +template constexpr auto sign(Sign s) -> Char { #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 static_assert(std::is_same::value, ""); #endif @@ -1394,7 +1425,7 @@ FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits, return out; } // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1]; + char buffer[num_bits() / BASE_BITS + 1] = {}; format_uint(buffer, value, num_digits, upper); return detail::copy_str_noinline(buffer, buffer + num_digits, out); } @@ -1430,22 +1461,23 @@ template class to_utf8 { : "invalid utf32")); } operator string_view() const { return string_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char* c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const char* { return &buffer_[0]; } + auto str() const -> std::string { return std::string(&buffer_[0], size()); } // Performs conversion returning a bool instead of throwing exception on // conversion error. This method may still throw in case of memory allocation // error. - bool convert(basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + auto convert(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { if (!convert(buffer_, s, policy)) return false; buffer_.push_back(0); return true; } - static bool convert( - Buffer& buf, basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + static auto convert(Buffer& buf, basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { for (auto p = s.begin(); p != s.end(); ++p) { uint32_t c = static_cast(*p); if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { @@ -1481,7 +1513,7 @@ template class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return {static_cast(p >> 64), static_cast(p)}; @@ -1512,19 +1544,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { namespace dragonbox { // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. -inline int floor_log10_pow2(int e) noexcept { +inline auto floor_log10_pow2(int e) noexcept -> int { FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); return (e * 315653) >> 20; } -inline int floor_log2_pow10(int e) noexcept { +inline auto floor_log2_pow10(int e) noexcept -> int { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); return (e * 1741647) >> 19; } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); @@ -1537,14 +1569,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_upper128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint128_fallback r = umul128(x, y.high()); r += umul128_upper64(x, y.low()); return r; } -FMT_API uint128_fallback get_cached_power(int k) noexcept; +FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1598,14 +1630,14 @@ template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. -template constexpr bool has_implicit_bit() { +template constexpr auto has_implicit_bit() -> bool { // An 80-bit FP number has a 64-bit significand an no implicit bit. return std::numeric_limits::digits != 64; } // Returns the number of significand bits stored in Float. The implicit bit is // not counted since it is not stored. -template constexpr int num_significand_bits() { +template constexpr auto num_significand_bits() -> int { // std::numeric_limits may not support __float128. return is_float128() ? 112 : (std::numeric_limits::digits - @@ -1698,7 +1730,7 @@ using fp = basic_fp; // Normalizes the value converted from double and multiplied by (1 << SHIFT). template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { +FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { // Handle subnormals. const auto implicit_bit = F(1) << num_significand_bits(); const auto shifted_implicit_bit = implicit_bit << SHIFT; @@ -1715,7 +1747,7 @@ FMT_CONSTEXPR basic_fp normalize(basic_fp value) { } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { #if FMT_USE_INT128 auto product = static_cast<__uint128_t>(lhs) * rhs; auto f = static_cast(product >> 64); @@ -1732,33 +1764,10 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { #endif } -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { +FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { return {multiply(x.f, y.f), x.e + y.e + 64}; } -template struct basic_data { - // For checking rounding thresholds. - // The kth entry is chosen to be the smallest integer such that the - // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. - static constexpr uint32_t fractional_part_rounding_thresholds[8] = { - 2576980378U, // ceil(2^31 + 2^32/10^1) - 2190433321U, // ceil(2^31 + 2^32/10^2) - 2151778616U, // ceil(2^31 + 2^32/10^3) - 2147913145U, // ceil(2^31 + 2^32/10^4) - 2147526598U, // ceil(2^31 + 2^32/10^5) - 2147487943U, // ceil(2^31 + 2^32/10^6) - 2147484078U, // ceil(2^31 + 2^32/10^7) - 2147483691U // ceil(2^31 + 2^32/10^8) - }; -}; -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct data : basic_data<> {}; - -#if FMT_CPLUSPLUS < 201703L -template -constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; -#endif - template () == num_bits()> using convert_float_result = conditional_t::value || doublish, double, T>; @@ -1939,15 +1948,11 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) *out++ = static_cast('\\'); break; default: - if (escape.cp < 0x100) { - return write_codepoint<2, Char>(out, 'x', escape.cp); - } - if (escape.cp < 0x10000) { + if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); + if (escape.cp < 0x10000) return write_codepoint<4, Char>(out, 'u', escape.cp); - } - if (escape.cp < 0x110000) { + if (escape.cp < 0x110000) return write_codepoint<8, Char>(out, 'U', escape.cp); - } for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { out = write_codepoint<2, Char>(out, 'x', @@ -1977,11 +1982,13 @@ auto write_escaped_string(OutputIt out, basic_string_view str) template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + Char v_array[1] = {v}; *out++ = static_cast('\''); if ((needs_escape(static_cast(v)) && v != static_cast('"')) || v == static_cast('\'')) { - out = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); + out = write_escaped_cp(out, + find_escape_result{v_array, v_array + 1, + static_cast(v)}); } else { *out++ = v; } @@ -2070,10 +2077,10 @@ template class digit_grouping { std::string::const_iterator group; int pos; }; - next_state initial_state() const { return {grouping_.begin(), 0}; } + auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } // Returns the next digit group separator position. - int next(next_state& state) const { + auto next(next_state& state) const -> int { if (thousands_sep_.empty()) return max_value(); if (state.group == grouping_.end()) return state.pos += grouping_.back(); if (*state.group <= 0 || *state.group == max_value()) @@ -2092,9 +2099,9 @@ template class digit_grouping { digit_grouping(std::string grouping, std::basic_string sep) : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} - bool has_separator() const { return !thousands_sep_.empty(); } + auto has_separator() const -> bool { return !thousands_sep_.empty(); } - int count_separators(int num_digits) const { + auto count_separators(int num_digits) const -> int { int count = 0; auto state = initial_state(); while (num_digits > next(state)) ++count; @@ -2103,7 +2110,7 @@ template class digit_grouping { // Applies grouping to digits and write the output to out. template - Out apply(Out out, basic_string_view digits) const { + auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); auto separators = basic_memory_buffer(); separators.push_back(0); @@ -2126,24 +2133,66 @@ template class digit_grouping { } }; +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, const format_specs& specs, const digit_grouping& grouping) -> OutputIt { static_assert(std::is_same, UInt>::value, ""); - int num_digits = count_digits(value); - char digits[40]; - format_decimal(digits, value, num_digits); - unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + - grouping.count_separators(num_digits)); + int num_digits = 0; + auto buffer = memory_buffer(); + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + num_digits = count_digits(value); + format_decimal(appender(buffer), value, num_digits); + break; + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + num_digits = count_digits<4>(value); + format_uint<4, char>(appender(buffer), value, num_digits, upper); + break; + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_uint<1, char>(appender(buffer), value, num_digits); + break; + } + case presentation_type::oct: { + num_digits = count_digits<3>(value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && value != 0) + prefix_append(prefix, '0'); + format_uint<3, char>(appender(buffer), value, num_digits); + break; + } + case presentation_type::chr: + return write_char(out, static_cast(value), specs); + default: + throw_format_error("invalid format specifier"); + } + + unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + + to_unsigned(grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) { - char sign = static_cast(prefix); - *it++ = static_cast(sign); - } - return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return grouping.apply(it, string_view(buffer.data(), buffer.size())); }); } @@ -2156,11 +2205,6 @@ inline auto write_loc(OutputIt, loc_value, const format_specs&, return false; } -FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { - prefix |= prefix != 0 ? value << 8 : value; - prefix += (1u + (value > 0xff ? 1 : 0)) << 24; -} - template struct write_int_arg { UInt abs_value; unsigned prefix; @@ -2307,25 +2351,25 @@ class counting_iterator { FMT_CONSTEXPR counting_iterator() : count_(0) {} - FMT_CONSTEXPR size_t count() const { return count_; } + FMT_CONSTEXPR auto count() const -> size_t { return count_; } - FMT_CONSTEXPR counting_iterator& operator++() { + FMT_CONSTEXPR auto operator++() -> counting_iterator& { ++count_; return *this; } - FMT_CONSTEXPR counting_iterator operator++(int) { + FMT_CONSTEXPR auto operator++(int) -> counting_iterator { auto it = *this; ++*this; return it; } - FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, - difference_type n) { + FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n) + -> counting_iterator { it.count_ += static_cast(n); return it; } - FMT_CONSTEXPR value_type operator*() const { return {}; } + FMT_CONSTEXPR auto operator*() const -> value_type { return {}; } }; template @@ -2360,9 +2404,10 @@ template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { - return specs.type != presentation_type::pointer - ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, bit_cast(s), &specs); + if (specs.type == presentation_type::pointer) + return write_ptr(out, bit_cast(s), &specs); + if (!s) throw_format_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); } template -FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, - ErrorHandler&& eh = {}) +template +FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs) -> float_specs { auto result = float_specs(); result.showpoint = specs.alt; @@ -2486,7 +2530,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, result.format = float_format::hex; break; default: - eh.on_error("invalid format specifier"); + throw_format_error("invalid format specifier"); break; } return result; @@ -2725,12 +2769,12 @@ template class fallback_digit_grouping { public: constexpr fallback_digit_grouping(locale_ref, bool) {} - constexpr bool has_separator() const { return false; } + constexpr auto has_separator() const -> bool { return false; } - constexpr int count_separators(int) const { return 0; } + constexpr auto count_separators(int) const -> int { return 0; } template - constexpr Out apply(Out out, basic_string_view) const { + constexpr auto apply(Out out, basic_string_view) const -> Out { return out; } }; @@ -2749,7 +2793,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, } } -template constexpr bool isnan(T value) { +template constexpr auto isnan(T value) -> bool { return !(value >= value); // std::isnan doesn't support __float128. } @@ -2762,14 +2806,14 @@ struct has_isfinite> template ::value&& has_isfinite::value)> -FMT_CONSTEXPR20 bool isfinite(T value) { +FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) return !detail::isnan(value) && value < inf && value > -inf; return std::isfinite(value); } template ::value)> -FMT_CONSTEXPR bool isfinite(T value) { +FMT_CONSTEXPR auto isfinite(T value) -> bool { T inf = T(std::numeric_limits::infinity()); // std::isfinite doesn't support __float128. return !detail::isnan(value) && value < inf && value > -inf; @@ -2806,10 +2850,10 @@ class bigint { basic_memory_buffer bigits_; int exp_; - FMT_CONSTEXPR20 bigit operator[](int index) const { + FMT_CONSTEXPR20 auto operator[](int index) const -> bigit { return bigits_[to_unsigned(index)]; } - FMT_CONSTEXPR20 bigit& operator[](int index) { + FMT_CONSTEXPR20 auto operator[](int index) -> bigit& { return bigits_[to_unsigned(index)]; } @@ -2905,11 +2949,11 @@ class bigint { assign(uint64_or_128_t(n)); } - FMT_CONSTEXPR20 int num_bigits() const { + FMT_CONSTEXPR20 auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } - FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -2924,13 +2968,15 @@ class bigint { return *this; } - template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + template + FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs) + -> int { int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); if (num_lhs_bigits != num_rhs_bigits) return num_lhs_bigits > num_rhs_bigits ? 1 : -1; @@ -2947,8 +2993,9 @@ class bigint { } // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { + friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1, + const bigint& lhs2, const bigint& rhs) + -> int { auto minimum = [](int a, int b) { return a < b ? a : b; }; auto maximum = [](int a, int b) { return a > b ? a : b; }; int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); @@ -3029,13 +3076,13 @@ class bigint { bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0u); exp_ -= exp_difference; } // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -3178,8 +3225,10 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, } if (buf[0] == overflow) { buf[0] = '1'; - if ((flags & dragon::fixed) != 0) buf.push_back('0'); - else ++exp10; + if ((flags & dragon::fixed) != 0) + buf.push_back('0'); + else + ++exp10; } return; } @@ -3276,6 +3325,17 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, format_hexfloat(static_cast(value), precision, specs, buf); } +constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + // It is equal to ceil(2^31 + 2^32/10^(k + 1)). + // These are stored in a string literal because we cannot have static arrays + // in constexpr functions and non-static ones are poorly optimized. + return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7" + U"\x800001ae\x8000002b"[index]; +} + template FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, buffer& buf) -> int { @@ -3480,12 +3540,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // fractional part is strictly larger than 1/2. if (precision < 9) { uint32_t fractional_part = static_cast(prod); - should_round_up = fractional_part >= - data::fractional_part_rounding_thresholds - [8 - number_of_digits_to_print] || - ((fractional_part >> 31) & - ((digits & 1) | (second_third_subsegments != 0) | - has_more_segments)) != 0; + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (second_third_subsegments != 0) | + has_more_segments)) != 0; } // Rounding at the subsegment boundary. // In this case, the fractional part is at least 1/2 if and only if @@ -3520,12 +3580,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // of 19 digits, so in this case the third segment should be // consisting of a genuine digit from the input. uint32_t fractional_part = static_cast(prod); - should_round_up = fractional_part >= - data::fractional_part_rounding_thresholds - [8 - number_of_digits_to_print] || - ((fractional_part >> 31) & - ((digits & 1) | (third_subsegment != 0) | - has_more_segments)) != 0; + should_round_up = + fractional_part >= fractional_part_rounding_thresholds( + 8 - number_of_digits_to_print) || + ((fractional_part >> 31) & + ((digits & 1) | (third_subsegment != 0) | + has_more_segments)) != 0; } // Rounding at the subsegment boundary. else { @@ -3726,8 +3786,7 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { } template -FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) - -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); throw_format_error("string pointer is null"); return out; @@ -3757,8 +3816,11 @@ template enable_if_t::value == type::custom_type, OutputIt> { + auto formatter = typename Context::template formatter_type(); + auto parse_ctx = typename Context::parse_context_type({}); + formatter.parse(parse_ctx); auto ctx = Context(out, {}, {}); - return typename Context::template formatter_type().format(value, ctx); + return formatter.format(value, ctx); } // An argument visitor that formats the argument and writes it via the output @@ -3801,62 +3863,39 @@ template struct arg_formatter { } }; -template struct custom_formatter { - basic_format_parse_context& parse_ctx; - buffer_context& ctx; - - void operator()( - typename basic_format_arg>::handle h) const { - h.format(parse_ctx, ctx); - } - template void operator()(T) const {} -}; - -template class width_checker { - public: - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - +struct width_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative width"); + if (is_negative(value)) throw_format_error("negative width"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("width is not integer"); + throw_format_error("width is not integer"); return 0; } - - private: - ErrorHandler& handler_; }; -template class precision_checker { - public: - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - +struct precision_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative precision"); + if (is_negative(value)) throw_format_error("negative precision"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("precision is not integer"); + throw_format_error("precision is not integer"); return 0; } - - private: - ErrorHandler& handler_; }; -template