diff --git a/src/QMCHamiltonians/CMakeLists.txt b/src/QMCHamiltonians/CMakeLists.txt index 1ce0d9a2f0..4b9d39b1eb 100644 --- a/src/QMCHamiltonians/CMakeLists.txt +++ b/src/QMCHamiltonians/CMakeLists.txt @@ -25,6 +25,7 @@ set(HAMSRCS BareKineticEnergy.cpp CoulombPBCAA.cpp CoulombPBCAB.cpp + EwaldRef.cpp NonLocalTOperator.cpp ForwardWalking.cpp PairCorrEstimator.cpp diff --git a/src/QMCHamiltonians/EwaldRef.cpp b/src/QMCHamiltonians/EwaldRef.cpp new file mode 100644 index 0000000000..c6f7eefc19 --- /dev/null +++ b/src/QMCHamiltonians/EwaldRef.cpp @@ -0,0 +1,401 @@ +////////////////////////////////////////////////////////////////////////////////////// +// This file is distributed under the University of Illinois/NCSA Open Source License. +// See LICENSE file in top directory for details. +// +// Copyright (c) 2019 QMCPACK developers. +// +// File developed by: Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory +// +// File created by: Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory +////////////////////////////////////////////////////////////////////////////////////// + + +#include "EwaldRef.h" +#include +#include "Configuration.h" +#include "Utilities/TimerManager.h" + +namespace qmcplusplus +{ +namespace ewaldref +{ + +/// Functor for term within the real-space sum in Drummond 2008 formula 7 +class RspaceMadelungTerm +{ +private: + /// The real-space cell axes + const RealMat a; + /// The constant \kappa in Drummond 2008 formula 7 + const real_t rconst; + +public: + RspaceMadelungTerm(const RealMat& a_in, real_t rconst_in) : a(a_in), rconst(rconst_in) {} + + real_t operator()(const IntVec& i) const + { + RealVec Rv = dot(i, a); + real_t R = std::sqrt(dot(Rv, Rv)); + real_t rm = std::erfc(rconst * R) / R; + return rm; + } +}; + + +/// Functor for term within the k-space sum in Drummond 2008 formula 7 +class KspaceMadelungTerm +{ +private: + /// The k-space cell axes + const RealMat b; + /// The constant 1/(4\kappa^2) in Drummond 2008 formula 7 + const real_t kconst; + /// The constant 4\pi/\Omega in Drummond 2008 formula 7 + const real_t kfactor; + +public: + KspaceMadelungTerm(const RealMat& b_in, real_t kconst_in, real_t kfactor_in) + : b(b_in), kconst(kconst_in), kfactor(kfactor_in) + {} + + real_t operator()(const IntVec& i) const + { + RealVec Kv = dot(i, b); + real_t K2 = dot(Kv, Kv); + real_t km = kfactor * std::exp(kconst * K2) / K2; + return km; + } +}; + + +/// Functor for term within the real-space sum in Drummond 2008 formula 6 +class RspaceEwaldTerm +{ +private: + /// The inter-particle separation vector + const RealVec r; + /// The real-space cell axes + const RealMat a; + /// The constant 1/(\sqrt{2}\kappa) in Drummond 2008 formula 6 + const real_t rconst; + +public: + RspaceEwaldTerm(const RealVec& r_in, const RealMat& a_in, real_t rconst_in) : r(r_in), a(a_in), rconst(rconst_in) {} + + real_t operator()(const IntVec& i) const + { + RealVec Rv = dot(i, a); + for (int_t d : {0, 1, 2}) + Rv[d] -= r[d]; + real_t R = std::sqrt(dot(Rv, Rv)); + real_t rm = std::erfc(rconst * R) / R; + return rm; + } +}; + + +/// Functor for term within the k-space sum in Drummond 2008 formula 6 +class KspaceEwaldTerm +{ +private: + /// The inter-particle separation vector + const RealVec r; + /// The k-space cell axes + const RealMat b; + /// The constant -\kappa^2/2 in Drummond 2008 formula 6 + const real_t kconst; + /// The constant 4\pi/\Omega in Drummond 2008 formula 6 + const real_t kfactor; + +public: + KspaceEwaldTerm(const RealVec& r_in, const RealMat& b_in, real_t kconst_in, real_t kfactor_in) + : r(r_in), b(b_in), kconst(kconst_in), kfactor(kfactor_in) + {} + + real_t operator()(const IntVec& i) const + { + RealVec Kv = dot(i, b); + real_t K2 = dot(Kv, Kv); + real_t Kr = dot(Kv, r); + real_t km = kfactor * std::exp(kconst * K2) * std::cos(Kr) / K2; + return km; + } +}; + + +/** Perform a sum over successively larger cubic integer grids + * in DIM dimensional space for arbitrary functors. + * + * @param function: A functor accepting a point in the grid and + * returning the real-valued contribution to the sum from that + * point. + * + * @param zero: Include the origin in the sum (or not). + * + * @param tol: Tolerance for the sum. Summation ceases when the + * contribution to the sum from the outermost cubic shell of + * points is less than tol. + */ +template +real_t gridSum(T& function, bool zero = true, real_t tol = 1e-11) +{ + real_t dv = std::numeric_limits::max(); + real_t dva = std::numeric_limits::max(); + real_t v = 0.0; + int_t im = 0; + int_t jm = 0; + int_t km = 0; + IntVec iv; + // Add the value at the origin, if requested. + if (zero) + { + iv = 0; + v += function(iv); + } + // Sum over cubic surface shells until the tolerance is reached. + while (std::abs(dva) > tol) + { + dva = 0.0; + dv = 0.0; // Surface shell contribution. + // Sum over new surface planes perpendicular to the x direction. + im += 1; + for (int_t i : {-im, im}) + for (int_t j = -jm; j < jm + 1; ++j) + for (int_t k = -km; k < km + 1; ++k) + { + iv[0] = i; + iv[1] = j; + iv[2] = k; + real_t f = function(iv); + dv += f; + dva += std::abs(f); + } + // Sum over new surface planes perpendicular to the y direction. + jm += 1; + for (int_t j : {-jm, jm}) + for (int_t k = -km; k < km + 1; ++k) + for (int_t i = -im; i < im + 1; ++i) + { + iv[0] = i; + iv[1] = j; + iv[2] = k; + real_t f = function(iv); + dv += f; + dva += std::abs(f); + } + // Sum over new surface planes perpendicular to the z direction. + km += 1; + for (int_t k : {-km, km}) + for (int_t i = -im; i < im + 1; ++i) + for (int_t j = -jm; j < jm + 1; ++j) + { + iv[0] = i; + iv[1] = j; + iv[2] = k; + real_t f = function(iv); + dv += f; + dva += std::abs(f); + } + v += dv; + } + + return v; +} + + +/** Find the optimal kappa for Madelung sums + * + * The optimal kappa balances the number of points within a given + * isosurface of the Gaussians (or limiting Gaussians from erfc) + * in the real-space and k-space Madelung terms. The balancing + * condition is made under isotropic assumptions, as reflected + * by the use of a sphere equal in volume to the simulation cell + * to determine the radius. + * + * @param volume: Volume of the real space cell. + */ +real_t getKappaMadelung(real_t volume) +{ + real_t radius = std::pow(3. * volume / (4 * M_PI), 1. / 3); + return std::sqrt(M_PI) / radius; +} + + +/** Compute the Madelung constant to a given tolerance + * + * Corresponds to the entirety of Drummond 2008 formula 7. + * + * @param a: Real-space cell axes. + * + * @param tol: Tolerance for the Madelung constant in Ha. + */ +real_t madelungSum(const RealMat& a, real_t tol = 1e-10) +{ + // Real-space cell volume + real_t volume = std::abs(det(a)); + // k-space cell axes + RealMat b = 2 * M_PI * transpose(inverse(a)); + // k-space cutoff (kappa) + real_t kconv = getKappaMadelung(volume); + + // Set constants for real-/k-space Madelung functors + real_t rconst = kconv; + real_t kconst = -1. / (4 * std::pow(kconv, 2)); + real_t kfactor = 4 * M_PI / volume; + + // Create real-/k-space fuctors for terms within the sums in formula 7 + RspaceMadelungTerm rfunc(a, rconst); + KspaceMadelungTerm kfunc(b, kconst, kfactor); + + // Compute the constant term + real_t cval = -M_PI / (std::pow(kconv, 2) * volume) - 2 * kconv / std::sqrt(M_PI); + // Compute the real-space sum (excludes zero) + real_t rval = gridSum(rfunc, false, tol); + // Compute the k-space sum (excludes zero) + real_t kval = gridSum(kfunc, false, tol); + + // Sum all contributions to get the Madelung constant + real_t ms = cval + rval + kval; + + return ms; +} + + +/** Find the optimal kappa for Ewald pair sums + * + * The optimal kappa balances the number of points within a given + * isosurface of the Gaussians (or limiting Gaussians from erfc) + * in the real-space and k-space Ewald pair terms. The balancing + * condition is made under isotropic assumptions, as reflected + * by the use of a sphere equal in volume to the simulation cell + * to determine the radius. + * + * @param volume: Volume of the real space cell. + */ +real_t getKappaEwald(real_t volume) +{ + real_t radius = std::pow(3. * volume / (4 * M_PI), 1. / 3); + return radius / std::sqrt(2 * M_PI); +} + + +/** Compute the Ewald interaction of a particle pair to a given tolerance + * + * Corresponds to the entirety of Drummond 2008 formula 6. + * + * @param r: Inter-particle separation vector. + * + * @param a: Real-space cell axes. + * + * @param tol: Tolerance for the Ewald pair interaction in Ha. + */ +real_t ewaldSum(const RealVec& r, const RealMat& a, real_t tol = 1e-10) +{ + // Real-space cell volume + real_t volume = std::abs(det(a)); + // k-space cell axes + RealMat b = 2 * M_PI * transpose(inverse(a)); + // k-space cutoff (kappa) + real_t kconv = getKappaEwald(volume); + + // Set constants for real-/k-space Ewald pair functors + real_t rconst = 1. / (std::sqrt(2.) * kconv); + real_t kconst = -std::pow(kconv, 2) / 2; + real_t kfactor = 4 * M_PI / volume; + + // Create real-/k-space fuctors for terms within the sums in formula 6 + RspaceEwaldTerm rfunc(r, a, rconst); + KspaceEwaldTerm kfunc(r, b, kconst, kfactor); + + // Compute the constant term + real_t cval = -2 * M_PI * std::pow(kconv, 2) / volume; + // Compute the real-space sum (includes zero) + real_t rval = gridSum(rfunc, true, tol); + // Compute the k-space sum (excludes zero) + real_t kval = gridSum(kfunc, false, tol); + + // Sum all contributions to get the Ewald pair interaction + real_t es = cval + rval + kval; + + return es; +} + + +/** Compute the total Ewald potential energy for a collection of charges + * + * Corresponds to the entirety of Drummond 2008 formula 5, but for + * arbitrary charges. + * + * @param a: Real-space cell axes. + * + * @param R: List of particle coordinates. + * + * @param R: List of particle charges. + * + * @param tol: Tolerance for the total potential energy in Ha. + */ +real_t ewaldEnergy(const RealMat& a, const PosArray& R, const ChargeArray& Q, real_t tol) +{ + // Timer for EwaldRef + ScopedTimer totalEwaldTimer(*timer_manager.createTimer("EwaldRef")); + + // Number of particles + const size_t N = R.size(); + + // Total Ewald potential energy + real_t ve = 0.0; + + { + // Sum Madelung contributions + ScopedTimer totalMadelungTimer(*timer_manager.createTimer("MadelungSum")); + // Maximum self-interaction charge product + real_t qqmax = 0.0; + for (size_t i = 0; i < N; ++i) + qqmax = std::max(std::abs(Q[i] * Q[i]), qqmax); + + // Compute the Madelung term (Drummond 2008 formula 7) + real_t vm = madelungSum(a, tol * 2. / qqmax); + + // Sum the Madelung self interaction for each particle + for (size_t i = 0; i < N; ++i) + ve += Q[i] * Q[i] * vm / 2; + } + + { + // Sum the interaction terms for all particle pairs + ScopedTimer EwaldSumTimer(*timer_manager.createTimer("EwaldSum")); + +#pragma omp parallel for reduction(+ : ve) + for (size_t i = 1; i < N / 2 + 1; ++i) + { + for (size_t j = 0; j < i; ++j) + { + real_t qq = Q[i] * Q[j]; + RealVec reduced = dot(R[i] - R[j], inverse(a)); + for (size_t dim = 0; dim < DIM; dim++) + reduced[dim] -= std::floor(reduced[dim]); + RealVec rr = dot(reduced, a); + ve += qq * ewaldSum(rr, a, tol / qq); + } + + const size_t i_reverse = N - i; + if (i == i_reverse) + continue; + + for (size_t j = 0; j < i_reverse; ++j) + { + real_t qq = Q[i_reverse] * Q[j]; + RealVec reduced = dot(R[i_reverse] - R[j], inverse(a)); + for (size_t dim = 0; dim < DIM; dim++) + reduced[dim] -= std::floor(reduced[dim]); + RealVec rr = dot(reduced, a); + ve += qq * ewaldSum(rr, a, tol / qq); + } + } + } + + return ve; +} + +} // namespace ewaldref +} // namespace qmcplusplus diff --git a/src/QMCHamiltonians/EwaldRef.h b/src/QMCHamiltonians/EwaldRef.h index fb52608d1c..5ed5303bd5 100644 --- a/src/QMCHamiltonians/EwaldRef.h +++ b/src/QMCHamiltonians/EwaldRef.h @@ -11,7 +11,7 @@ /**@file EwaldRef.h * - * @brief Computes Ewald sums of the potential energy to a given + * @brief Computes Ewald sums of the potential energy to a given * tolerance for arbitrary collections of charges. * * The implementation follows formulas 6 and 7 from: @@ -21,14 +21,12 @@ * DOI: https://doi.org/10.1103/PhysRevB.78.125106 */ -#ifndef EWALD_REF_H -#define EWALD_REF_H +#ifndef QMCPLUSPLUS_EWALD_REF_H +#define QMCPLUSPLUS_EWALD_REF_H -#include -#include "Configuration.h" +#include #include "OhmmsPETE/TinyVector.h" #include "OhmmsPETE/Tensor.h" -#include "Utilities/TimerManager.h" namespace qmcplusplus { @@ -55,311 +53,9 @@ using PosArray = std::vector; /// Type for lists of particle charges using ChargeArray = std::vector; - -/// Functor for term within the real-space sum in Drummond 2008 formula 7 -class RspaceMadelungTerm -{ -private: - /// The real-space cell axes - const RealMat a; - /// The constant \kappa in Drummond 2008 formula 7 - const real_t rconst; - -public: - RspaceMadelungTerm(const RealMat& a_in, real_t rconst_in) : a(a_in), rconst(rconst_in) {} - - real_t operator()(const IntVec& i) const - { - RealVec Rv = dot(i, a); - real_t R = std::sqrt(dot(Rv, Rv)); - real_t rm = std::erfc(rconst * R) / R; - return rm; - } -}; - - -/// Functor for term within the k-space sum in Drummond 2008 formula 7 -class KspaceMadelungTerm -{ -private: - /// The k-space cell axes - const RealMat b; - /// The constant 1/(4\kappa^2) in Drummond 2008 formula 7 - const real_t kconst; - /// The constant 4\pi/\Omega in Drummond 2008 formula 7 - const real_t kfactor; - -public: - KspaceMadelungTerm(const RealMat& b_in, real_t kconst_in, real_t kfactor_in) - : b(b_in), kconst(kconst_in), kfactor(kfactor_in) - {} - - real_t operator()(const IntVec& i) const - { - RealVec Kv = dot(i, b); - real_t K2 = dot(Kv, Kv); - real_t km = kfactor * std::exp(kconst * K2) / K2; - return km; - } -}; - - -/// Functor for term within the real-space sum in Drummond 2008 formula 6 -class RspaceEwaldTerm -{ -private: - /// The inter-particle separation vector - const RealVec r; - /// The real-space cell axes - const RealMat a; - /// The constant 1/(\sqrt{2}\kappa) in Drummond 2008 formula 6 - const real_t rconst; - -public: - RspaceEwaldTerm(const RealVec& r_in, const RealMat& a_in, real_t rconst_in) : r(r_in), a(a_in), rconst(rconst_in) {} - - real_t operator()(const IntVec& i) const - { - RealVec Rv = dot(i, a); - for (int_t d : {0, 1, 2}) - Rv[d] -= r[d]; - real_t R = std::sqrt(dot(Rv, Rv)); - real_t rm = std::erfc(rconst * R) / R; - return rm; - } -}; - - -/// Functor for term within the k-space sum in Drummond 2008 formula 6 -class KspaceEwaldTerm -{ -private: - /// The inter-particle separation vector - const RealVec r; - /// The k-space cell axes - const RealMat b; - /// The constant -\kappa^2/2 in Drummond 2008 formula 6 - const real_t kconst; - /// The constant 4\pi/\Omega in Drummond 2008 formula 6 - const real_t kfactor; - -public: - KspaceEwaldTerm(const RealVec& r_in, const RealMat& b_in, real_t kconst_in, real_t kfactor_in) - : r(r_in), b(b_in), kconst(kconst_in), kfactor(kfactor_in) - {} - - real_t operator()(const IntVec& i) const - { - RealVec Kv = dot(i, b); - real_t K2 = dot(Kv, Kv); - real_t Kr = dot(Kv, r); - real_t km = kfactor * std::exp(kconst * K2) * std::cos(Kr) / K2; - return km; - } -}; - - -/** Perform a sum over successively larger cubic integer grids - * in DIM dimensional space for arbitrary functors. - * - * @param function: A functor accepting a point in the grid and - * returning the real-valued contribution to the sum from that - * point. - * - * @param zero: Include the origin in the sum (or not). - * - * @param tol: Tolerance for the sum. Summation ceases when the - * contribution to the sum from the outermost cubic shell of - * points is less than tol. - */ -template -real_t gridSum(T& function, bool zero = true, real_t tol = 1e-11) -{ - real_t dv = std::numeric_limits::max(); - real_t dva = std::numeric_limits::max(); - real_t v = 0.0; - int_t im = 0; - int_t jm = 0; - int_t km = 0; - IntVec iv; - // Add the value at the origin, if requested. - if (zero) - { - iv = 0; - v += function(iv); - } - // Sum over cubic surface shells until the tolerance is reached. - while (std::abs(dva) > tol) - { - dva = 0.0; - dv = 0.0; // Surface shell contribution. - // Sum over new surface planes perpendicular to the x direction. - im += 1; - for (int_t i : {-im, im}) - for (int_t j = -jm; j < jm + 1; ++j) - for (int_t k = -km; k < km + 1; ++k) - { - iv[0] = i; - iv[1] = j; - iv[2] = k; - real_t f = function(iv); - dv += f; - dva += std::abs(f); - } - // Sum over new surface planes perpendicular to the y direction. - jm += 1; - for (int_t j : {-jm, jm}) - for (int_t k = -km; k < km + 1; ++k) - for (int_t i = -im; i < im + 1; ++i) - { - iv[0] = i; - iv[1] = j; - iv[2] = k; - real_t f = function(iv); - dv += f; - dva += std::abs(f); - } - // Sum over new surface planes perpendicular to the z direction. - km += 1; - for (int_t k : {-km, km}) - for (int_t i = -im; i < im + 1; ++i) - for (int_t j = -jm; j < jm + 1; ++j) - { - iv[0] = i; - iv[1] = j; - iv[2] = k; - real_t f = function(iv); - dv += f; - dva += std::abs(f); - } - v += dv; - } - - return v; -} - - -/** Find the optimal kappa for Madelung sums - * - * The optimal kappa balances the number of points within a given - * isosurface of the Gaussians (or limiting Gaussians from erfc) - * in the real-space and k-space Madelung terms. The balancing - * condition is made under isotropic assumptions, as reflected - * by the use of a sphere equal in volume to the simulation cell - * to determine the radius. - * - * @param volume: Volume of the real space cell. - */ -real_t getKappaMadelung(real_t volume) -{ - real_t radius = std::pow(3. * volume / (4 * M_PI), 1. / 3); - return std::sqrt(M_PI) / radius; -} - - -/** Compute the Madelung constant to a given tolerance - * - * Corresponds to the entirety of Drummond 2008 formula 7. - * - * @param a: Real-space cell axes. - * - * @param tol: Tolerance for the Madelung constant in Ha. - */ -real_t madelungSum(const RealMat& a, real_t tol = 1e-10) -{ - // Real-space cell volume - real_t volume = std::abs(det(a)); - // k-space cell axes - RealMat b = 2 * M_PI * transpose(inverse(a)); - // k-space cutoff (kappa) - real_t kconv = getKappaMadelung(volume); - - // Set constants for real-/k-space Madelung functors - real_t rconst = kconv; - real_t kconst = -1. / (4 * std::pow(kconv, 2)); - real_t kfactor = 4 * M_PI / volume; - - // Create real-/k-space fuctors for terms within the sums in formula 7 - RspaceMadelungTerm rfunc(a, rconst); - KspaceMadelungTerm kfunc(b, kconst, kfactor); - - // Compute the constant term - real_t cval = -M_PI / (std::pow(kconv, 2) * volume) - 2 * kconv / std::sqrt(M_PI); - // Compute the real-space sum (excludes zero) - real_t rval = gridSum(rfunc, false, tol); - // Compute the k-space sum (excludes zero) - real_t kval = gridSum(kfunc, false, tol); - - // Sum all contributions to get the Madelung constant - real_t ms = cval + rval + kval; - - return ms; -} - - -/** Find the optimal kappa for Ewald pair sums - * - * The optimal kappa balances the number of points within a given - * isosurface of the Gaussians (or limiting Gaussians from erfc) - * in the real-space and k-space Ewald pair terms. The balancing - * condition is made under isotropic assumptions, as reflected - * by the use of a sphere equal in volume to the simulation cell - * to determine the radius. - * - * @param volume: Volume of the real space cell. - */ -real_t getKappaEwald(real_t volume) -{ - real_t radius = std::pow(3. * volume / (4 * M_PI), 1. / 3); - return radius / std::sqrt(2 * M_PI); -} - - -/** Compute the Ewald interaction of a particle pair to a given tolerance - * - * Corresponds to the entirety of Drummond 2008 formula 6. - * - * @param r: Inter-particle separation vector. - * - * @param a: Real-space cell axes. - * - * @param tol: Tolerance for the Ewald pair interaction in Ha. - */ -real_t ewaldSum(const RealVec& r, const RealMat& a, real_t tol = 1e-10) -{ - // Real-space cell volume - real_t volume = std::abs(det(a)); - // k-space cell axes - RealMat b = 2 * M_PI * transpose(inverse(a)); - // k-space cutoff (kappa) - real_t kconv = getKappaEwald(volume); - - // Set constants for real-/k-space Ewald pair functors - real_t rconst = 1. / (std::sqrt(2.) * kconv); - real_t kconst = -std::pow(kconv, 2) / 2; - real_t kfactor = 4 * M_PI / volume; - - // Create real-/k-space fuctors for terms within the sums in formula 6 - RspaceEwaldTerm rfunc(r, a, rconst); - KspaceEwaldTerm kfunc(r, b, kconst, kfactor); - - // Compute the constant term - real_t cval = -2 * M_PI * std::pow(kconv, 2) / volume; - // Compute the real-space sum (includes zero) - real_t rval = gridSum(rfunc, true, tol); - // Compute the k-space sum (excludes zero) - real_t kval = gridSum(kfunc, false, tol); - - // Sum all contributions to get the Ewald pair interaction - real_t es = cval + rval + kval; - - return es; -} - - /** Compute the total Ewald potential energy for a collection of charges * - * Corresponds to the entirety of Drummond 2008 formula 5, but for + * Corresponds to the entirety of Drummond 2008 formula 5, but for * arbitrary charges. * * @param a: Real-space cell axes. @@ -370,56 +66,7 @@ real_t ewaldSum(const RealVec& r, const RealMat& a, real_t tol = 1e-10) * * @param tol: Tolerance for the total potential energy in Ha. */ -real_t ewaldEnergy(const RealMat& a, const PosArray& R, const ChargeArray& Q, real_t tol = 1e-10) -{ - // Timer for EwaldRef - ScopedTimer totalEwaldTimer(*timer_manager.createTimer("EwaldRef")); - - // Number of particles - const size_t N = R.size(); - - // Total Ewald potential energy - real_t ve = 0.0; - - { - // Sum Madelung contributions - ScopedTimer totalMadelungTimer(*timer_manager.createTimer("MadelungSum")); - // Maximum self-interaction charge product - real_t qqmax = 0.0; - for (size_t i = 0; i < N; ++i) - qqmax = std::max(std::abs(Q[i] * Q[i]), qqmax); - - // Compute the Madelung term (Drummond 2008 formula 7) - real_t vm = madelungSum(a, tol * 2. / qqmax); - - // Sum the Madelung self interaction for each particle - for (size_t i = 0; i < N; ++i) - ve += Q[i] * Q[i] * vm / 2; - } - - { - // Sum the interaction terms for all particle pairs - ScopedTimer EwaldSumTimer(*timer_manager.createTimer("EwaldSum")); - - int_t Npairs = (N * (N - 1)) / 2; - - std::vector qq(Npairs); - for (size_t i = 0, n = 0; i < N; ++i) - for (size_t j = 0; j < i; ++j, ++n) - qq[n] = Q[i] * Q[j]; - - std::vector rr(Npairs); - for (size_t i = 0, n = 0; i < N; ++i) - for (size_t j = 0; j < i; ++j, ++n) - rr[n] = R[i] - R[j]; - -#pragma omp parallel for reduction(+ : ve) - for (size_t n = 0; n < Npairs; ++n) - ve += qq[n] * ewaldSum(rr[n], a, tol / qq[n]); - } - - return ve; -} +real_t ewaldEnergy(const RealMat& a, const PosArray& R, const ChargeArray& Q, real_t tol = 1e-10); } // namespace ewaldref } // namespace qmcplusplus diff --git a/src/QMCHamiltonians/tests/CMakeLists.txt b/src/QMCHamiltonians/tests/CMakeLists.txt index d8dcc159ea..7461973f74 100644 --- a/src/QMCHamiltonians/tests/CMakeLists.txt +++ b/src/QMCHamiltonians/tests/CMakeLists.txt @@ -15,7 +15,7 @@ set(SRC_DIR hamiltonian) set(UTEST_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(COULOMB_SRCS test_coulomb_pbcAB.cpp test_coulomb_pbcAB_ewald.cpp test_coulomb_pbcAA.cpp - test_coulomb_pbcAA_ewald.cpp) + test_coulomb_pbcAA_ewald.cpp test_EwaldRef.cpp) set(FORCE_SRCS test_force.cpp test_force_ewald.cpp test_stress.cpp test_spacewarp.cpp) set(HAM_SRCS test_bare_kinetic.cpp diff --git a/src/QMCHamiltonians/tests/test_EwaldRef.cpp b/src/QMCHamiltonians/tests/test_EwaldRef.cpp new file mode 100644 index 0000000000..9a94a7e612 --- /dev/null +++ b/src/QMCHamiltonians/tests/test_EwaldRef.cpp @@ -0,0 +1,182 @@ +////////////////////////////////////////////////////////////////////////////////////// +// This file is distributed under the University of Illinois/NCSA Open Source License. +// See LICENSE file in top directory for details. +// +// Copyright (c) 2017 Jeongnim Kim and QMCPACK developers. +// +// File developed by: Mark Dewing, markdewing@gmail.com, University of Illinois at Urbana-Champaign +// Ye Luo, yeluo@anl.gov, Argonne National Laboratory +// +// File created by: Mark Dewing, markdewing@gmail.com, University of Illinois at Urbana-Champaign +////////////////////////////////////////////////////////////////////////////////////// + + +#include "catch.hpp" + +#include "EwaldRef.h" + +namespace qmcplusplus +{ +TEST_CASE("EwaldRef", "[hamiltonian]") +{ + SECTION("diamondC_2x1x1") + { + ewaldref::RealMat A; + ewaldref::PosArray R; + ewaldref::ChargeArray Q; + + A = {6.7463223, 6.7463223, 0.0, 0.0, 3.37316115, 3.37316115, 3.37316115, 0.0, 3.37316115}; + + const int num_centers = 4; + R.resize(num_centers); + Q.resize(num_centers, 4); + + R[0] = {0.0, 0.0, 0.0}; + R[1] = {1.68658058, 1.68658058, 1.68658058}; + R[2] = {3.37316115, 3.37316115, 0.0}; + R[3] = {5.05974173, 5.05974173, 1.68658058}; + + auto Vii_ref = ewaldref::ewaldEnergy(A, R, Q); + CHECK(Approx(Vii_ref) == -25.551326969); + } + + SECTION("diamondC_2x1x1 fake 5 ions") + { + ewaldref::RealMat A; + ewaldref::PosArray R; + ewaldref::ChargeArray Q; + + A = {6.7463223, 6.7463223, 0.0, 0.0, 3.37316115, 3.37316115, 3.37316115, 0.0, 3.37316115}; + + const int num_centers = 5; + R.resize(num_centers); + Q.resize(num_centers, 4); + + R[0] = {0.0, 0.0, 0.0}; + R[1] = {1.68658058, 1.68658058, 1.68658058}; + R[2] = {3.37316115, 3.37316115, 0.0}; + R[3] = {5.05974173, 5.05974173, 1.68658058}; + R[4] = {5.05974173, 3.37316115, 0.0}; + + auto Vii_ref = ewaldref::ewaldEnergy(A, R, Q); + CHECK(Approx(Vii_ref) == -30.9149928426); + } + + SECTION("32 H2O particle outside the box") + { + ewaldref::RealMat A; + ewaldref::PosArray R; + ewaldref::ChargeArray Q; + + A = {19.09677861, 0.0, 0.0, 0.0, 19.09677861, 0.0, 0.0, 0.0, 19.09677861}; + + + const int num_centers = 96; + R.resize(num_centers); + Q.resize(num_centers, 1); + for (int i = 0; i < 32; i++) + Q[i] = 6; + + R[0] = {24.26616224, 34.23786910, -19.88728885}; + R[1] = {32.99764184, 4.89042226, 1.78485767}; + R[2] = {-15.27579016, 6.65758075, 38.82499031}; + R[3] = {22.42481310, 11.92337821, -7.60669570}; + R[4] = {10.09818623, -36.83076232, 19.09171414}; + R[5] = {-11.23287228, 13.50815924, 31.73549375}; + R[6] = {10.92768151, 24.86633926, 5.76944727}; + R[7] = {-9.78708061, -29.22574847, -17.44821933}; + R[8] = {-6.13987138, -55.38050301, 14.01883883}; + R[9] = {29.88923132, -31.53574970, 35.33598895}; + R[10] = {1.56789254, 17.86637793, -32.73459196}; + R[11] = {34.80913331, 3.65144222, 9.38999246}; + R[12] = {31.77272136, 32.05504644, 0.27278764}; + R[13] = {-30.27341264, -39.32009856, 23.29068561}; + R[14] = {35.83412076, 27.63459907, -1.26540786}; + R[15] = {-10.87943680, 19.57945246, 13.17998940}; + R[16] = {-49.64178269, -16.61639968, -11.19560688}; + R[17] = {-18.60134911, 26.58693490, 31.83659410}; + R[18] = {-7.11236224, -4.03040790, -14.07090078}; + R[19] = {20.17679489, -3.46141136, -4.44342644}; + R[20] = {4.56091071, 11.36220514, -16.19497185}; + R[21] = {1.01714887, 38.91116182, -8.61870074}; + R[22] = {6.32924084, 24.01974195, 15.37836450}; + R[23] = {16.24028749, 10.66064431, 42.29377160}; + R[24] = {-4.79620051, 8.82201637, 27.72832949}; + R[25] = {14.76737825, 16.42141774, -3.33612251}; + R[26] = {12.32262065, 48.53912749, 13.07764183}; + R[27] = {32.35702468, 53.07692686, 10.72873114}; + R[28] = {1.07544125, 32.48798270, 26.25604386}; + R[29] = {23.46321761, 24.58760466, 10.48820680}; + R[30] = {-0.36077140, 1.66652869, 38.94706662}; + R[31] = {-23.26460739, -0.01549386, 5.84212613}; + R[32] = {23.12061026, 34.20139738, -21.54098818}; + R[33] = {25.71709396, 35.41932587, -20.30737496}; + R[34] = {32.34058406, 4.98169603, 3.41910039}; + R[35] = {34.23881396, 3.52021963, 1.85762346}; + R[36] = {-16.89611694, 7.01972787, 37.90563855}; + R[37] = {-15.44556316, 5.09009072, 39.54044062}; + R[38] = {21.69575675, 12.50539496, -9.29422114}; + R[39] = {23.01100614, 10.18061608, -8.16520426}; + R[40] = {9.64824243, -36.16879126, 17.52365719}; + R[41] = {11.76927104, -35.90555241, 19.85176199}; + R[42] = {-12.91138372, 13.13055416, 31.33014750}; + R[43] = {-10.51838573, 15.20947967, 31.45713709}; + R[44] = {11.74992025, 26.05252035, 6.83177571}; + R[45] = {10.06245150, 23.90673633, 7.06776471}; + R[46] = {-11.38983293, -28.87482633, -16.90048221}; + R[47] = {-9.53954539, -30.85828288, -16.37814301}; + R[48] = {-5.28280499, -56.90059871, 14.29589158}; + R[49] = {-6.34785464, -54.53220495, 15.61287951}; + R[50] = {30.43498423, -30.47429053, 34.19138183}; + R[51] = {28.79111146, -30.62452376, 36.34283503}; + R[52] = {3.13093605, 17.57864822, -33.46648289}; + R[53] = {1.80647236, 18.35957755, -30.90609295}; + R[54] = {33.81438147, 3.51511737, 10.72884453}; + R[55] = {36.42957346, 2.91758597, 10.24448882}; + R[56] = {30.42780327, 30.93406090, 0.87568775}; + R[57] = {32.79128374, 30.78533945, -1.03389184}; + R[58] = {-29.69458953, -38.26714316, 21.99376657}; + R[59] = {-28.86462181, -40.40102190, 23.51121665}; + R[60] = {36.71076471, 27.30559775, -2.81622106}; + R[61] = {34.26640396, 26.73301073, -1.26900023}; + R[62] = {-11.52912465, 20.45741922, 14.45016882}; + R[63] = {-9.10093995, 20.61124293, 13.03026640}; + R[64] = {-49.34339494, -18.22820379, -12.45131100}; + R[65] = {-49.56260317, -17.21553735, -9.34201231}; + R[66] = {-17.19834084, 26.24035913, 31.22659050}; + R[67] = {-18.92504030, 28.31660123, 31.41764182}; + R[68] = {-7.14036799, -3.82826389, -15.73108188}; + R[69] = {-5.62450527, -2.61343455, -13.47412527}; + R[70] = {21.17457029, -4.96871361, -5.39550826}; + R[71] = {18.32229325, -3.88765798, -4.78556136}; + R[72] = {4.79809024, 12.59595064, -17.41898526}; + R[73] = {4.16523985, 10.11729136, -17.19852981}; + R[74] = {2.05683461, 40.37683341, -8.69740783}; + R[75] = {0.68774693, 38.51526420, -6.82102317}; + R[76] = {5.61021894, 24.83648159, 16.74378612}; + R[77] = {8.04554680, 24.11120470, 14.94801717}; + R[78] = {16.46450349, 9.58389726, 40.56712883}; + R[79] = {15.21996765, 11.84690099, 41.50821245}; + R[80] = {-4.28433040, 9.40845508, 26.07368529}; + R[81] = {-3.68044951, 7.39886362, 27.87667299}; + R[82] = {13.85105004, 15.06287472, -2.04540177}; + R[83] = {14.57808438, 15.47487281, -5.04968838}; + R[84] = {10.70881343, 49.55504426, 12.80342367}; + R[85] = {12.59744352, 47.50941572, 11.51291080}; + R[86] = {32.33170235, 51.27639580, 11.02301820}; + R[87] = {32.17825658, 53.42766003, 8.99734516}; + R[88] = {0.52424026, 31.64270820, 24.77015220}; + R[89] = {0.82794193, 34.25468766, 25.37769915}; + R[90] = {24.21797422, 23.99064017, 8.99006972}; + R[91] = {24.04997757, 24.10648038, 11.99720981}; + R[92] = {-0.60767545, 0.82025264, 37.32228009}; + R[93] = {0.85953248, 0.69664754, 40.03347017}; + R[94] = {-22.87267819, 0.93691110, 7.30758985}; + R[95] = {-21.48731996, 0.09036670, 5.18794074}; + + auto Vii_ref = ewaldref::ewaldEnergy(A, R, Q); + CHECK(Approx(Vii_ref) == -210.2521866119); + } +} + +} // namespace qmcplusplus