24#if defined(ESPRESSO_ELECTROSTATICS) and defined(ESPRESSO_GSL)
40#include <gsl/gsl_sf_bessel.h>
79 auto const c = 0.5 * (rmin +
rmax);
81 if (
errc > maxPWerror) {
87 return 0.5 * (rmin +
rmax);
93 auto const value = std::accumulate(
95 [x](
auto const &acc,
auto const &
coeff) { return acc * x + coeff; });
109void CoulombMMM1D::determine_bessel_radii() {
111 auto const &
box_l = box_geo.length();
112 auto const &
box_l_inv = box_geo.length_inv();
113 for (
int i = 0; i < MAXIMAL_B_CUT; ++i) {
118void CoulombMMM1D::prepare_polygamma_series() {
121 auto const rhomax2 = uz2 * far_switch_radius_sq;
126 create_mod_psi_up_to(n + 1);
139 : maxPWerror{maxPWerror}, far_switch_radius{
switch_rad},
140 tune_timings{tune_timings}, tune_verbose{tune_verbose}, m_is_tuned{
false},
141 far_switch_radius_sq{-1.}, uz2{0.}, prefuz2{0.}, prefL3_i{0.} {
144 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
147 throw std::domain_error(
"Parameter 'far_switch_radius' must be > 0");
153 throw std::domain_error(
"Parameter 'timings' must be > 0");
157void CoulombMMM1D::sanity_checks_periodicity()
const {
159 if (box_geo.periodic(0) || box_geo.periodic(1) || !box_geo.periodic(2)) {
160 throw std::runtime_error(
"MMM1D requires periodicity (False, False, True)");
164void CoulombMMM1D::sanity_checks_cell_structure()
const {
165 auto const &local_geo = *
get_system().local_geo;
167 throw std::runtime_error(
"MMM1D requires the N-square cellsystem");
171void CoulombMMM1D::recalc_boxl_parameters() {
174 if (far_switch_radius_sq >=
Utils::sqr(box_geo.length()[2]))
175 far_switch_radius_sq = 0.8 *
Utils::sqr(box_geo.length()[2]);
179 prefL3_i = prefuz2 * box_geo.length_inv()[2];
181 determine_bessel_radii();
182 prepare_polygamma_series();
187 auto constexpr c_2pi = 2. * std::numbers::pi;
189 auto const n_modPsi =
static_cast<int>(modPsi.size()) >> 1;
190 auto const rxy2 = d[0] * d[0] + d[1] * d[1];
192 auto const z_d = d[2] * box_geo.length_inv()[2];
195 if (
rxy2 <= far_switch_radius_sq) {
200 for (
int n = 1; n <
n_modPsi; n++) {
201 auto const deriv =
static_cast<double>(2 * n);
215 double Fx = prefL3_i *
sr * d[0];
216 double Fy = prefL3_i *
sr * d[1];
217 double Fz = prefuz2 *
sz;
223 pref = 1. / Utils::int_pow<3>(
dist);
228 shift_z = d[2] + box_geo.length()[2];
231 pref = 1. / (
rt2 *
rt);
236 shift_z = d[2] - box_geo.length()[2];
239 pref = 1. / (
rt2 *
rt);
248 auto const rxy_d =
rxy * box_geo.length_inv()[2];
249 auto sr = 0.,
sz = 0.;
251 for (
int bp = 1;
bp < MAXIMAL_B_CUT;
bp++) {
252 if (bessel_radii[
bp - 1] <
rxy)
264 auto const pref =
sr /
rxy + 2. * box_geo.length_inv()[2] /
rxy2;
266 force = {pref * d[0], pref * d[1],
sz};
273 double const dist)
const {
277 auto constexpr c_2pi = 2. * std::numbers::pi;
279 auto const n_modPsi =
static_cast<int>(modPsi.size()) >> 1;
280 auto const rxy2 = d[0] * d[0] + d[1] * d[1];
282 auto const z_d = d[2] * box_geo.length_inv()[2];
285 if (
rxy2 <= far_switch_radius_sq) {
287 energy = -2. * std::numbers::egamma;
291 for (
int n = 0; n <
n_modPsi; n++) {
300 energy *= box_geo.length_inv()[2];
308 shift_z = d[2] + box_geo.length()[2];
312 shift_z = d[2] - box_geo.length()[2];
318 auto const rxy_d =
rxy * box_geo.length_inv()[2];
322 -0.25 *
log(
rxy2_d) + 0.5 * (std::numbers::ln2 - std::numbers::egamma);
323 for (
int bp = 1;
bp < MAXIMAL_B_CUT;
bp++) {
324 if (bessel_radii[
bp - 1] <
rxy)
330 energy *= 4. * box_geo.length_inv()[2];
340 recalc_boxl_parameters();
343 if (far_switch_radius_sq < 0.) {
344 auto const &box_geo = *
system.box_geo;
345 auto const maxrad = box_geo.length()[2];
346 auto min_time = std::numeric_limits<double>::infinity();
354 system.on_coulomb_change();
375 }
else if (far_switch_radius_sq <=
Utils::sqr(bessel_radii.back())) {
377 throw std::runtime_error(
"MMM1D could not find a reasonable Bessel cutoff");
381 system.on_coulomb_change();
@ NSQUARE
Atom decomposition (N-square).
Vector implementation and trait types for boost qvm interoperability.
void set_prefactor(double new_prefactor)
double prefactor
Electrostatics prefactor.
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
static double evaluateAsTaylorSeriesAt(std::span< const double > series, double x)
static double mod_psi_odd(auto const &modPsi, int n, double x)
Modified polygamma for odd order 2*n+1, n>= 0
static auto determine_minrad(double maxPWerror, int P, Utils::Vector3d const &box_l, Utils::Vector3d const &box_l_inv)
static double mod_psi_even(auto const &modPsi, int n, double x)
Modified polygamma for even order 2*n, n >= 0
static auto far_error(int P, double minrad, Utils::Vector3d const &box_l_inv)
MMM1D algorithm for long-range Coulomb interactions on the CPU.
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
CoulombMMM1D(double prefactor, double maxPWerror, double switch_rad, int tune_timings, bool tune_verbose)
double far_switch_radius
Far switch radius.
Utils::Vector3d pair_force(double q1q2, Utils::Vector3d const &d, double dist) const
Compute the pair force.
double pair_energy(double q1q2, Utils::Vector3d const &d, double dist) const
Compute the pair energy.
double maxPWerror
Maximal allowed pairwise error for the potential and force.
double benchmark_integration_step(System::System &system, int int_steps)
Benchmark the integration loop.