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]);
181 prefL3_i = prefuz2 * box_geo.length_inv()[2];
183 determine_bessel_radii();
184 prepare_polygamma_series();
189 auto constexpr c_2pi = 2. * std::numbers::pi;
191 auto const n_modPsi =
static_cast<int>(modPsi.size()) >> 1;
192 auto const rxy2 = d[0] * d[0] + d[1] * d[1];
194 auto const z_d = d[2] * box_geo.length_inv()[2];
197 if (
rxy2 <= far_switch_radius_sq) {
202 for (
int n = 1; n <
n_modPsi; n++) {
203 auto const deriv =
static_cast<double>(2 * n);
217 double Fx = prefL3_i *
sr * d[0];
218 double Fy = prefL3_i *
sr * d[1];
219 double Fz = prefuz2 *
sz;
225 pref = 1. / Utils::int_pow<3>(
dist);
230 shift_z = d[2] + box_geo.length()[2];
233 pref = 1. / (
rt2 *
rt);
238 shift_z = d[2] - box_geo.length()[2];
241 pref = 1. / (
rt2 *
rt);
250 auto const rxy_d =
rxy * box_geo.length_inv()[2];
251 auto sr = 0.,
sz = 0.;
253 for (
int bp = 1;
bp < MAXIMAL_B_CUT;
bp++) {
254 if (bessel_radii[
bp - 1] <
rxy)
266 auto const pref =
sr /
rxy + 2. * box_geo.length_inv()[2] /
rxy2;
268 force = {pref * d[0], pref * d[1],
sz};
275 double const dist)
const {
279 auto constexpr c_2pi = 2. * std::numbers::pi;
281 auto const n_modPsi =
static_cast<int>(modPsi.size()) >> 1;
282 auto const rxy2 = d[0] * d[0] + d[1] * d[1];
284 auto const z_d = d[2] * box_geo.length_inv()[2];
287 if (
rxy2 <= far_switch_radius_sq) {
289 energy = -2. * std::numbers::egamma;
293 for (
int n = 0; n <
n_modPsi; n++) {
302 energy *= box_geo.length_inv()[2];
310 shift_z = d[2] + box_geo.length()[2];
314 shift_z = d[2] - box_geo.length()[2];
320 auto const rxy_d =
rxy * box_geo.length_inv()[2];
324 -0.25 *
log(
rxy2_d) + 0.5 * (std::numbers::ln2 - std::numbers::egamma);
325 for (
int bp = 1;
bp < MAXIMAL_B_CUT;
bp++) {
326 if (bessel_radii[
bp - 1] <
rxy)
332 energy *= 4. * box_geo.length_inv()[2];
342 recalc_boxl_parameters();
345 if (far_switch_radius_sq < 0.) {
346 auto const &box_geo = *
system.box_geo;
347 auto const maxrad = box_geo.length()[2];
348 auto min_time = std::numeric_limits<double>::infinity();
356 system.on_coulomb_change();
378 }
else if (far_switch_radius_sq <=
Utils::sqr(bessel_radii.back())) {
380 throw std::runtime_error(
"MMM1D could not find a reasonable Bessel cutoff");
384 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.