58#include "communication.hpp"
66#include "system/System.hpp"
75#include <boost/mpi/collectives/all_reduce.hpp>
76#include <boost/mpi/collectives/broadcast.hpp>
77#include <boost/mpi/collectives/reduce.hpp>
78#include <boost/mpi/communicator.hpp>
79#include <boost/range/combine.hpp>
80#include <boost/range/numeric.hpp>
82#include <Kokkos_Core.hpp>
83#include <Kokkos_ScatterView.hpp>
91#include <initializer_list>
103template <
typename FloatType>
104std::complex<FloatType>
107 return std::complex<FloatType>(-z.imag() * k, z.real() * k);
110template <
typename FloatType>
111std::complex<FloatType>
114 return std::complex<FloatType>(z.real() * k, z.imag() * k);
119 return mesh[0u] % node_grid[0u] == 0 and mesh[1u] % node_grid[1u] == 0 and
120 mesh[2u] % node_grid[2u] == 0;
123template <
typename FloatType, Arch Architecture,
class FFTConfig>
125 FFTConfig>::count_charged_particles() {
127 std::size_t local_n = std::size_t{0u};
128 double local_q = 0.0;
129 double local_q2 = 0.0;
131 auto kernel = [](Res &acc,
auto const &p) {
135 acc.local_q += p.q();
139 auto reduce = [](Res &a, Res
const &b) {
140 a.local_n += b.local_n;
141 a.local_q += b.local_q;
142 a.local_q2 += b.local_q2;
144 auto res = reduce_over_local_particles<Res>(*(get_system().cell_structure),
147 boost::mpi::all_reduce(
comm_cart, res.local_n, p3m.sum_qpart, std::plus<>());
148 boost::mpi::all_reduce(
comm_cart, res.local_q2, p3m.sum_q2, std::plus<>());
149 boost::mpi::all_reduce(
comm_cart, res.local_q, p3m.square_sum_q,
151 p3m.square_sum_q =
Utils::sqr(p3m.square_sum_q);
163template <
typename FloatType, Arch Architecture,
class FFTConfig>
165 FFTConfig>::calc_influence_function_force() {
167 FFTConfig::k_space_order>(
168 p3m.params, p3m.fft->ks_local_ld_index(), p3m.fft->ks_local_ur_index(),
169 get_system().
box_geo->length_inv());
170 if constexpr (FFTConfig::use_r2c) {
171 influence_function_r2c<FFTConfig::r2c_dir>(p3m.g_force, p3m.params.mesh,
172 p3m.fft->ks_local_size(),
173 p3m.fft->ks_local_ld_index());
180template <
typename FloatType, Arch Architecture,
class FFTConfig>
182 FFTConfig>::calc_influence_function_energy() {
184 FFTConfig::k_space_order>(
185 p3m.params, p3m.fft->ks_local_ld_index(), p3m.fft->ks_local_ur_index(),
186 get_system().
box_geo->length_inv());
187 if constexpr (FFTConfig::use_r2c) {
188 influence_function_r2c<FFTConfig::r2c_dir>(p3m.g_energy, p3m.params.mesh,
189 p3m.fft->ks_local_size(),
190 p3m.fft->ks_local_ld_index());
202 auto constexpr exp_min = -708.4;
203 auto const factor1 =
Utils::sqr(std::numbers::pi * alpha_L_i);
211 mesh_start, mesh_stop, indices,
213 auto const norm_sq = nm.norm2();
214 auto const exponent = -factor1 * norm_sq;
215 auto const exp_limit = (exp_min + std::log(norm_sq)) / 2.;
216 auto const ex = (exponent < exp_limit) ? 0. : std::exp(exponent);
219 alias2 += energy * ex * (shift * nm) / norm_sq;
221 [&](
unsigned dim,
int n) {
222 nm[dim] = shift[dim] + n * mesh[dim];
226 return std::make_pair(alias1, alias2);
240 std::size_t n_c_part,
double sum_q2,
244 return (2. * pref * sum_q2 * exp(-
Utils::sqr(r_cut_iL * alpha_L))) /
245 sqrt(
static_cast<double>(n_c_part) * r_cut_iL * box_l[0] * volume);
262 int cao, std::size_t n_c_part,
double sum_q2,
267 auto const alpha_L_i = 1. / alpha_L;
268 auto const mesh_stop = mesh / 2;
269 auto const mesh_start = -mesh_stop;
275 mesh_start, mesh_stop, indices,
277 if ((indices[0] != 0) or (indices[1] != 0) or (indices[2] != 0)) {
278 auto const n2 = indices.norm2();
280 auto const [alias1, alias2] =
282 auto const d = alias1 -
Utils::sqr(alias2 / cs) / n2;
290 [&values, &mesh_i, cotangent_sum](
unsigned dim,
int n) {
291 values[dim] = cotangent_sum(n, mesh_i[dim]);
294 return 2. * pref * sum_q2 * sqrt(he_q /
static_cast<double>(n_c_part)) /
295 (box_l[1] * box_l[2]);
298template <
typename FloatType, Arch Architecture,
class FFTConfig>
302 assert(p3m.params.alpha > 0.);
304 auto const &system = get_system();
305 auto const &box_geo = *system.box_geo;
306 auto const &local_geo = *system.local_geo;
307 auto const skin = system.cell_structure->get_verlet_skin();
309 p3m.params.cao3 = Utils::int_pow<3>(p3m.params.cao);
310 p3m.params.recalc_a_ai_cao_cut(box_geo.length());
314 auto const &solver = system.coulomb.impl->solver;
315 double elc_layer = 0.;
316 if (
auto actor = get_actor_by_type<ElectrostaticLayerCorrection>(solver)) {
317 elc_layer = actor->elc.space_layer;
320 p3m.local_mesh.calc_local_ca_mesh(p3m.params, local_geo, skin, elc_layer);
321 std::shared_ptr<P3MFFTBackend<FloatType, FFTConfig>> fft_backend;
327 if constexpr (Architecture ==
Arch::CPU and
328 std::is_same_v<FFTConfig, P3MFFTKokkosConfig>) {
329 fft_backend = make_p3m_kokkos_fft_backend<FloatType>(
330 ::comm_cart, p3m.params.mesh, p3m.local_mesh.ld_no_halo,
333 if (not fft_backend) {
334 fft_backend = std::make_shared<P3MFFTHeffte<FloatType, FFTConfig>>(
335 ::comm_cart, p3m.params.mesh, p3m.local_mesh.ld_no_halo,
338 p3m.fft = std::move(fft_backend);
339 auto const rs_array_size =
341 auto const rs_array_size_no_halo =
342 static_cast<std::size_t
>(
Utils::product(p3m.local_mesh.dim_no_halo));
343 auto const fft_mesh_size =
344 static_cast<std::size_t
>(
Utils::product(p3m.fft->ks_local_size()));
345 p3m.rs_charge_density.resize(rs_array_size);
346 p3m.ks_charge_density.resize(fft_mesh_size);
347 for (
auto d : {0u, 1u, 2u}) {
348 p3m.ks_E_fields[d].resize(fft_mesh_size);
349 p3m.rs_E_fields[d].resize(rs_array_size);
350 p3m.rs_E_fields_no_halo[d].resize(rs_array_size_no_halo);
352 p3m.calc_differential_operator();
357 count_charged_particles();
366 p3m_interpolate(p3m.local_mesh, weights, [q, &p3m](
int ind,
double w) {
367 p3m.rs_charge_density[ind] += value_type(w * q);
374 auto const weights = p3m_calculate_interpolation_weights<cao, memory_order>(
375 real_pos.
as_span(), p3m.params.ai, p3m.local_mesh);
376 inter_weights.
store(weights);
377 this->operator()(p3m, q, weights);
382 auto const weights = p3m_calculate_interpolation_weights<cao, memory_order>(
383 real_pos.
as_span(), p3m.params.ai, p3m.local_mesh);
384 this->operator()(p3m, q, weights);
390 using execution_space = Kokkos::DefaultHostExecutionSpace;
391 auto const &aosoa = cell_structure.get_aosoa();
392 auto const n_part = cell_structure.count_local_particles();
394 kokkos_parallel_range_for<execution_space>(
395 "InterpolateCharges", std::size_t{0u}, n_part, [&](
auto p_index) {
397 auto const tid = omp_get_thread_num();
398 auto const pos = aosoa.get_span_at(aosoa.position, p_index);
399 auto const q = aosoa.charge(p_index);
401 p3m_calculate_interpolation_weights<cao, memory_order>(
402 pos, p3m.params.ai, p3m.local_mesh);
403 p3m.inter_weights.store_at(p_index, weights);
405 p3m.local_mesh, weights, [&, tid, q](
int ind,
double w) {
406 p3m.rs_charge_density_kokkos(tid, ind) += value_type(w * q);
410 int num_threads = execution_space().concurrency();
411 kokkos_parallel_range_for<execution_space>(
412 "ReduceInterpolatedCharges", std::size_t{0}, p3m.local_mesh.size,
413 [&p3m, num_threads](std::size_t
const i) {
415 for (
int tid = 0; tid < num_threads; ++tid) {
416 acc += p3m.rs_charge_density_kokkos(tid, i);
418 p3m.rs_charge_density.at(i) += acc;
425template <
typename FloatType, Arch Architecture,
class FFTConfig>
427 prepare_fft_mesh(
true);
429 Utils::integral_parameter<int, AssignCharge, p3m_min_cao, p3m_max_cao>(
430 p3m.params.cao, p3m, *get_system().cell_structure);
433template <
typename FloatType, Arch Architecture,
class FFTConfig>
437 Utils::integral_parameter<int, AssignCharge, p3m_min_cao, p3m_max_cao>(
438 p3m.params.cao, p3m, q, real_pos);
440 Utils::integral_parameter<int, AssignCharge, p3m_min_cao, p3m_max_cao>(
441 p3m.params.cao, p3m, q, real_pos, p3m.inter_weights);
450 assert(cao == p3m.inter_weights.cao());
451 using execution_space = Kokkos::DefaultHostExecutionSpace;
453 auto const kernel = [&p3m](
auto pref,
auto &p_force, std::size_t p_index) {
454 auto const weights = p3m.inter_weights.template load<cao>(p_index);
458 [&force, &p3m](
int ind,
double w) {
459 force[0u] += w * double(p3m.rs_E_fields[0u][ind]);
460 force[1u] += w * double(p3m.rs_E_fields[1u][ind]);
461 force[2u] += w * double(p3m.rs_E_fields[2u][ind]);
464 auto access = p_force.access();
465 access(p_index, 0) -= pref * force[0];
466 access(p_index, 1) -= pref * force[1];
467 access(p_index, 2) -= pref * force[2];
471 auto const &aosoa = cell_structure.
get_aosoa();
473 kokkos_parallel_range_for<execution_space>(
474 "AssignForces", std::size_t{0u}, n_part, [&](std::size_t p_index) {
475 if (
auto const pref = aosoa.charge(p_index) * force_prefac) {
476 kernel(pref, scatter_force, p_index);
484 auto const &cs,
auto const &box_geo) {
485 auto const local_dip = reduce_over_local_particles<Utils::Vector3d>(
488 acc += p.
q() * box_geo.unfolded_position(p.
pos(), p.
image_box());
491 return boost::mpi::all_reduce(comm, local_dip, std::plus<>());
494template <
typename FloatType, Arch Architecture,
class FFTConfig>
496 FFTConfig>::kernel_ks_charge_density() {
498 p3m.halo_comm.gather_grid(
comm_cart, p3m.rs_charge_density.data(),
504 auto *
const fft_input = p3m.fft->forward_input_buffer();
505 extract_block_into<Utils::MemoryOrder::ROW_MAJOR, FFTConfig::r_space_order>(
506 fft_input, p3m.rs_charge_density, p3m.local_mesh.dim,
507 p3m.local_mesh.n_halo_ld, p3m.local_mesh.dim - p3m.local_mesh.n_halo_ur);
513 p3m.fft->forward(fft_input, p3m.ks_charge_density.data());
516template <
typename FloatType, Arch Architecture,
class FFTConfig>
518 FFTConfig>::kernel_rs_electric_field() {
519 auto const mesh_start = p3m.fft->ks_local_ld_index();
520 auto const mesh_stop = p3m.fft->ks_local_ur_index();
524 auto const wavevector =
528 for_each_3d_lin<FFTConfig::k_space_order>(
529 mesh_start, mesh_stop,
531#ifdef ESPRESSO_ADDITIONAL_CHECKS
532 assert(local_index ==
533 Utils::get_linear_index<FFTConfig::k_space_order>(
534 indices - mesh_start, p3m.fft->ks_local_size()));
537 p3m.ks_charge_density[local_index], p3m.g_force[local_index]);
539 for (
auto d : {0u, 1u, 2u}) {
541 auto const k = FloatType(p3m.d_op[d][indices[d]]) * wavevector[d];
543 p3m.ks_E_fields[d][local_index] =
549 auto const size = p3m.local_mesh.ur_no_halo - p3m.local_mesh.ld_no_halo;
551 for (
auto d : {0u, 1u, 2u}) {
552 auto k_space = p3m.ks_E_fields[d].data();
553 auto r_space = p3m.rs_E_fields_no_halo[d].data();
554 p3m.fft->backward(k_space, r_space);
559 auto const begin = p3m.rs_E_fields_no_halo[d].begin();
560 assert(p3m.rs_E_fields[d].size() ==
564 p3m.rs_E_fields[d].data(), std::span(begin, rs_mesh_size_no_halo),
565 p3m.local_mesh.dim_no_halo, p3m.local_mesh.n_halo_ld,
566 p3m.local_mesh.n_halo_ur);
570 std::array<FloatType *, 3u> rs_fields = {{p3m.rs_E_fields[0u].data(),
571 p3m.rs_E_fields[1u].data(),
572 p3m.rs_E_fields[2u].data()}};
573 p3m.halo_comm.spread_grid(
comm_cart, rs_fields, p3m.local_mesh.dim);
581template <
typename FloatType, Arch Architecture,
class FFTConfig>
584 auto const &box_geo = *get_system().
box_geo;
587 if (p3m.sum_q2 > 0.) {
589 kernel_ks_charge_density();
591 auto constexpr r2c_dir = FFTConfig::r2c_dir;
593 auto const &global_size = p3m.params.mesh;
594 auto const local_size = p3m.fft->ks_local_size();
595 auto const local_origin = p3m.fft->ks_local_ld_index();
596 auto const half_alpha_inv_sq =
Utils::sqr(1. / 2. / p3m.params.alpha);
597 auto const wavevector = (2. * std::numbers::pi) * box_geo.length_inv();
598 auto const cutoff_left = 1 - local_origin[r2c_dir];
599 auto const cutoff_right = global_size[r2c_dir] / 2 - local_origin[r2c_dir];
601 auto &short_dim = local_index[r2c_dir];
603 std::size_t index = 0u;
604 for_each_3d_order<FFTConfig::k_space_order>(
605 mesh_start, local_size, local_index, [&]() {
606 if (short_dim <= cutoff_right) {
607 auto const global_index = local_index + local_origin;
608 auto const kx = p3m.d_op[0u][global_index[0u]] * wavevector[0u];
609 auto const ky = p3m.d_op[1u][global_index[1u]] * wavevector[1u];
610 auto const kz = p3m.d_op[2u][global_index[2u]] * wavevector[2u];
616 static_cast<double>(p3m.g_energy[index] *
617 std::norm(p3m.ks_charge_density[index]));
618 if (short_dim >= cutoff_left and short_dim <= cutoff_right - 1) {
626 auto const vterm = -2. * (1. / norm_sq + half_alpha_inv_sq);
627 auto const pref = cell_energy * vterm;
628 diagonal += cell_energy;
629 node_k_space_pressure_tensor[0u] += pref * kx * kx;
630 node_k_space_pressure_tensor[1u] += pref * kx * ky;
631 node_k_space_pressure_tensor[2u] += pref * kx * kz;
632 node_k_space_pressure_tensor[4u] += pref * ky * ky;
633 node_k_space_pressure_tensor[5u] += pref * ky * kz;
634 node_k_space_pressure_tensor[8u] += pref * kz * kz;
640 node_k_space_pressure_tensor[0u] += diagonal;
641 node_k_space_pressure_tensor[4u] += diagonal;
642 node_k_space_pressure_tensor[8u] += diagonal;
643 node_k_space_pressure_tensor[3u] = node_k_space_pressure_tensor[1u];
644 node_k_space_pressure_tensor[6u] = node_k_space_pressure_tensor[2u];
645 node_k_space_pressure_tensor[7u] = node_k_space_pressure_tensor[5u];
648 return node_k_space_pressure_tensor * prefactor / (2. * box_geo.volume());
651template <
typename FloatType, Arch Architecture,
class FFTConfig>
653 bool force_flag,
bool energy_flag) {
655 auto const &system = get_system();
656 auto const &box_geo = *system.box_geo;
658 auto const npt_flag = force_flag and system.has_npt_enabled();
660 auto constexpr npt_flag =
false;
662 if (p3m.sum_qpart == 0u) {
665 auto &cell_structure = *system.cell_structure;
667 if (not has_actor_of_type<ElectrostaticLayerCorrection>(
668 system.coulomb.impl->solver)) {
672 kernel_ks_charge_density();
674 auto scatter_force = system.cell_structure->get_scatter_force();
675 auto const &aosoa = cell_structure.get_aosoa();
682 auto const volume = box_geo.volume();
684 4. * std::numbers::pi / volume / (2. * p3m.params.epsilon + 1.);
689 kernel_rs_electric_field();
692 auto const force_prefac = prefactor / volume;
693 auto &particle_data = cell_structure;
694 Utils::integral_parameter<int, AssignForces, p3m_min_cao, p3m_max_cao>(
695 p3m.params.cao, p3m, force_prefac, particle_data);
700 using execution_space = Kokkos::DefaultHostExecutionSpace;
701 auto const dm = prefactor * pref * box_dipole.value();
702 auto const n_part = cell_structure.count_local_particles();
703 kokkos_parallel_range_for<execution_space>(
704 "AssignForcesBoxDipole", std::size_t{0u}, n_part,
705 [&aosoa, &scatter_force, dm](
auto p_index) {
706 auto access = scatter_force.access();
707 auto const q = aosoa.charge(p_index);
708 access(p_index, 0) -= q * dm[0];
709 access(p_index, 1) -= q * dm[1];
710 access(p_index, 2) -= q * dm[2];
716 if (energy_flag or npt_flag) {
717 auto constexpr r2c_dir = FFTConfig::r2c_dir;
719 auto const &global_size = p3m.params.mesh;
720 auto const local_size = p3m.fft->ks_local_size();
721 auto const local_origin = p3m.fft->ks_local_ld_index();
722 auto const cutoff_left = 1 - local_origin[r2c_dir];
723 auto const cutoff_right = global_size[r2c_dir] / 2 - local_origin[r2c_dir];
725 auto &short_dim = local_index[r2c_dir];
726 auto node_energy = 0.;
727 std::size_t index = 0u;
728 for_each_3d_order<FFTConfig::k_space_order>(
729 mesh_start, local_size, local_index, [&]() {
730 if (short_dim <= cutoff_right) {
731 auto const &cell_field = p3m.ks_charge_density[index];
732 auto cell_energy =
static_cast<double>(p3m.g_energy[index] *
733 std::norm(cell_field));
734 if (short_dim >= cutoff_left and short_dim <= cutoff_right - 1) {
737 cell_energy += cell_energy;
739 node_energy += cell_energy;
743 node_energy /= 2. * volume;
746 boost::mpi::reduce(
::comm_cart, node_energy, energy, std::plus<>(), 0);
750 energy -= p3m.sum_q2 * p3m.params.alpha * std::numbers::inv_sqrtpi;
753 energy -= p3m.square_sum_q * std::numbers::pi /
758 energy += pref * box_dipole.value().norm2();
767 if (not energy_flag) {
775template <
typename FloatType, Arch Architecture,
class FFTConfig>
780 double m_mesh_density_min = -1., m_mesh_density_max = -1.;
782 bool m_tune_mesh =
false;
783 std::pair<std::optional<int>, std::optional<int>> m_tune_limits;
790 auto constexpr memory_order = FFTConfig::k_space_order;
791 auto constexpr layout_col_major = std::tuple(2, 1, 0);
792 auto constexpr layout_row_major = std::tuple(0, 1, 2);
793 return (memory_order == COLUMN_MAJOR) ? layout_col_major : layout_row_major;
798 double prefactor,
int timings,
799 decltype(m_tune_limits) tune_limits)
801 m_tune_limits{
std::move(tune_limits)} {}
808 auto const on_gpu = Architecture ==
Arch::CUDA;
810 auto const on_gpu =
false;
812 m_logger = std::make_unique<TuningLogger>(
813 verbose and
this_node == 0, (on_gpu) ?
"CoulombP3MGPU" :
"CoulombP3M",
820 std::optional<std::string>
823 if (
auto actor = get_actor_by_type<ElectrostaticLayerCorrection>(solver)) {
824 return actor->veto_r_cut(r_cut);
837 auto valid_decomposition =
false;
843 return Utils::Vector3i{{std::max(lhs[0u], rhs[0u]),
844 std::max(lhs[1u], rhs[1u]),
845 std::max(lhs[2u], rhs[2u])}};
848 if constexpr (FFTConfig::use_r2c) {
850 mesh_size_k_space[FFTConfig::r2c_dir] -= 1;
851 mesh_size_k_space[FFTConfig::r2c_dir] *= 2;
856 valid_decomposition =
857 (mesh_size_r_space[0u] == mesh_size_k_space[KX] and
858 mesh_size_r_space[1u] == mesh_size_k_space[KY] and
859 mesh_size_r_space[2u] == mesh_size_k_space[KZ] and
862 boost::mpi::broadcast(
::comm_cart, valid_decomposition, 0);
863 std::optional<std::string> retval{
"conflict with FFT domain decomposition"};
864 if (valid_decomposition) {
865 retval = std::nullopt;
870 std::tuple<double, double, double, double>
872 double r_cut_iL)
const override {
874 auto const &box_geo = *m_system.box_geo;
875 double alpha_L, rs_err, ks_err;
879 p3m.
sum_q2, 0., box_geo.length());
883 alpha_L = sqrt(log(std::numbers::sqrt2 * rs_err / p3m.
params.
accuracy)) /
894 p3m.
sum_q2, alpha_L, box_geo.length());
900 p3m.
sum_q2, alpha_L, box_geo.length().data());
902 boost::mpi::broadcast(
comm_cart, ks_err, 0);
906 p3m.
sum_q2, alpha_L, box_geo.length());
912 auto const &box_geo = *m_system.box_geo;
913 auto const mesh_density =
914 static_cast<double>(p3m.
params.
mesh[0]) * box_geo.length_inv()[0];
918 auto const normalized_box_dim = std::cbrt(box_geo.volume());
919 auto const max_npart_per_dim = 512.;
923 auto const min_npart_per_dim = std::min(
924 max_npart_per_dim, std::cbrt(
static_cast<double>(p3m.
sum_qpart)));
925 m_mesh_density_min = min_npart_per_dim / normalized_box_dim;
926 m_mesh_density_max = max_npart_per_dim / normalized_box_dim;
927 if (m_tune_limits.first or m_tune_limits.second) {
928 auto const &box_l = box_geo.length();
929 auto const dim = std::max({box_l[0], box_l[1], box_l[2]});
930 if (m_tune_limits.first) {
931 m_mesh_density_min =
static_cast<double>(*m_tune_limits.first) / dim;
933 if (m_tune_limits.second) {
934 m_mesh_density_max =
static_cast<double>(*m_tune_limits.second) / dim;
939 m_mesh_density_min = m_mesh_density_max = mesh_density;
943 for (
auto i : {1u, 2u}) {
945 static_cast<int>(std::round(mesh_density * box_geo.length()[i]));
955 auto const &box_geo = *m_system.box_geo;
956 auto const &solver = m_system.coulomb.impl->solver;
958 auto time_best = time_sentinel;
959 auto mesh_density = m_mesh_density_min;
962 for (
auto i : {0u, 1u, 2u}) {
964 static_cast<int>(std::round(box_geo.length()[i] * mesh_density));
966 current_mesh[i] += current_mesh[i] % 2;
970 while (mesh_density <= m_mesh_density_max) {
972 trial_params.
mesh = current_mesh;
973 trial_params.cao = cao_best;
974 trial_params.cao = cao_best;
976 auto const trial_time =
977 get_m_time(trial_params.mesh, trial_params.cao, trial_params.r_cut_iL,
978 trial_params.alpha_L, trial_params.accuracy);
980 if (trial_time >= 0.) {
983 if (has_actor_of_type<CoulombP3M>(solver)) {
984 m_r_cut_iL_max = trial_params.r_cut_iL;
987 if (trial_time < time_best) {
990 tuned_params = trial_params;
991 time_best = tuned_params.time = trial_time;
992 }
else if (trial_time > time_best + time_granularity or
993 get_n_trials() > max_n_consecutive_trials) {
1000 mesh_density = current_mesh[0] / box_geo.length()[0];
1005 return tuned_params;
1009template <
typename FloatType, Arch Architecture,
class FFTConfig>
1011 auto &system = get_system();
1012 auto const &box_geo = *system.
box_geo;
1019 if (not is_tuned()) {
1020 count_charged_particles();
1022 throw std::runtime_error(
1023 "CoulombP3M: no charged particles in the system");
1027 system, p3m, prefactor, tuning.timings, tuning.limits);
1047 auto const &box_geo = *system.
box_geo;
1048 auto const &local_geo = *system.
local_geo;
1049 for (
auto i = 0u; i < 3u; i++) {
1052 std::stringstream msg;
1054 <<
" is larger than half of box dimension " << box_geo.length()[i];
1055 throw std::runtime_error(msg.str());
1058 std::stringstream msg;
1060 <<
" is larger than local box dimension " << local_geo.length()[i];
1061 throw std::runtime_error(msg.str());
1066 if ((box_geo.length()[0] != box_geo.length()[1]) or
1067 (box_geo.length()[1] != box_geo.length()[2]) or
1070 throw std::runtime_error(
1071 "CoulombP3M: non-metallic epsilon requires cubic box");
1078 if (!box_geo.periodic(0) or !box_geo.periodic(1) or !box_geo.periodic(2)) {
1079 throw std::runtime_error(
1080 "CoulombP3M: requires periodicity (True, True, True)");
1085 auto const &local_geo = *
get_system().local_geo;
1088 throw std::runtime_error(
1089 "CoulombP3M: requires the regular or hybrid decomposition cell system");
1093 throw std::runtime_error(
1094 "CoulombP3M: does not work with the hybrid decomposition cell system, "
1095 "if using more than one MPI node");
1099template <
typename FloatType, Arch Architecture,
class FFTConfig>
1101 auto const &box_geo = *get_system().
box_geo;
1106 sanity_checks_boxl();
1107 calc_influence_function_force();
1108 calc_influence_function_energy();
1113template <
typename FloatType, Arch Architecture,
class FFTConfig>
1115 FFTConfig>::add_long_range_forces_gpu() {
1123 auto &gpu = *get_system().
gpu;
1135template <
typename FloatType, Arch Architecture,
class FFTConfig>
1138 auto &system = get_system();
1139 if (has_actor_of_type<ElectrostaticLayerCorrection>(
1144 system.
box_geo->length(), system.
gpu->n_particles());
1148template <
typename FloatType, Arch Architecture,
class FFTConfig>
1151 auto &gpu_particle_data = *get_system().
gpu;
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
Vector implementation and trait types for boost qvm interoperability.
Describes a cell structure / cell system.
std::size_t count_local_particles() const
void determine_mesh_limits() override
std::optional< std::string > layer_correction_veto_r_cut(double r_cut) const override
TuningAlgorithm::Parameters get_time() override
void setup_logger(bool verbose) override
std::tuple< double, double, double, double > calculate_accuracy(Utils::Vector3i const &mesh, int cao, double r_cut_iL) const override
void on_solver_change() const override
CoulombTuningAlgorithm(System::System &system, auto &input_p3m, double prefactor, int timings, decltype(m_tune_limits) tune_limits)
static constexpr std::tuple< int, int, int > get_memory_layout()
std::optional< std::string > fft_decomposition_veto(Utils::Vector3i const &mesh_size_r_space) const override
P3MParameters & get_params() override
std::shared_ptr< LocalBox > local_geo
void npt_add_virial_contribution(double energy)
std::shared_ptr< GpuParticleData > gpu
bool has_npt_enabled() const
std::shared_ptr< BoxGeometry > box_geo
Tuning algorithm for P3M.
System::System & m_system
void determine_cao_limits(int initial_cao)
Determine a sensible range for the charge assignment order.
void determine_r_cut_limits()
Determine a sensible range for the real-space cutoff.
std::unique_ptr< TuningLogger > m_logger
DEVICE_QUALIFIER constexpr pointer data() noexcept
constexpr std::span< const T, N > as_span() const noexcept
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Cache for interpolation weights.
void zfill(std::size_t size)
Fill cache with zero-initialized data.
void store(InterpolationWeights< cao > const &weights)
Push back weights for one point.
Communicator communicator
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
constexpr auto round_error_prec
Precision below which a double-precision float is assumed to be zero.
void charge_assign(elc_data const &elc, CoulombP3M &solver, auto const &cs)
ELC algorithm for long-range Coulomb interactions.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
void pad_with_zeros_discard_imag_into(OutValue *out, std::span< T > cropped_array, Utils::Vector3i const &cropped_dim, Utils::Vector3i const &pad_left, Utils::Vector3i const &pad_right)
Pad a 3D matrix with zeros to restore halo regions, writing into a caller-provided buffer of product(...
and std::invocable< Projector, unsigned, int > void for_each_3d(detail::IndexVectorConcept auto &&start, detail::IndexVectorConcept auto &&stop, detail::IndexVectorConcept auto &&counters, Kernel &&kernel, Projector &&projector=detail::noop_projector)
Repeat an operation on every element of a 3D grid.
std::vector< FloatType > grid_influence_function(P3MParameters const ¶ms, Utils::Vector3i const &n_start, Utils::Vector3i const &n_stop, Utils::Vector3d const &inv_box_l)
Map influence function over a grid.
void p3m_interpolate(P3MLocalMesh const &local_mesh, WeightsStorage< cao > const &weights, Kernel kernel)
P3M grid interpolation.
constexpr int p3m_min_cao
Minimal charge assignment order.
constexpr int p3m_max_cao
Maximal charge assignment order.
#define P3M_BRILLOUIN
P3M: Number of Brillouin zones taken into account in the calculation of the optimal influence functio...
T product(Vector< T, N > const &v)
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
DEVICE_QUALIFIER auto sinc(T x)
Calculate the function .
auto get_analytic_cotangent_sum_kernel(int cao)
Exports for the NpT code.
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
P3M algorithm for long-range Coulomb interaction.
double p3m_k_space_error(double pref, Utils::Vector3i const &mesh, int cao, std::size_t n_c_part, double sum_q2, double alpha_L, Utils::Vector3d const &box_l)
Calculate the analytic expression of the error estimate for the P3M method in (eq.
std::complex< FloatType > multiply_complex_by_real(std::complex< FloatType > const &z, FloatType k)
auto p3m_tune_aliasing_sums(Utils::Vector3i const &shift, Utils::Vector3i const &mesh, Utils::Vector3d const &mesh_i, int cao, double alpha_L_i)
Aliasing sum used by p3m_k_space_error.
double p3m_real_space_error(double pref, double r_cut_iL, std::size_t n_c_part, double sum_q2, double alpha_L, Utils::Vector3d const &box_l)
Calculate the real space contribution to the rms error in the force (as described by Kolafa and Perra...
std::complex< FloatType > multiply_complex_by_imaginary(std::complex< FloatType > const &z, FloatType k)
auto calc_dipole_moment(boost::mpi::communicator const &comm, auto const &cs, auto const &box_geo)
bool is_node_grid_compatible_with_mesh(Utils::Vector3i const &node_grid, Utils::Vector3i const &mesh)
void p3m_gpu_add_farfield_force(P3MGpuParams &data, GpuParticleData &gpu, double prefactor, std::size_t n_part)
The long-range part of the P3M algorithm.
void p3m_gpu_init(std::shared_ptr< P3MGpuParams > &data, int cao, Utils::Vector3i const &mesh, double alpha, Utils::Vector3d const &box_l, std::size_t n_part)
Initialize the internal data structure of the P3M GPU.
P3M electrostatics on GPU.
double p3m_k_space_error_gpu(double prefactor, const int *mesh, int cao, int npart, double sum_q2, double alpha_L, const double *box)
Utils::Vector3i node_grid
void charge_assign() override
double long_range_kernel(bool force_flag, bool energy_flag)
Compute the k-space part of forces and energies.
Utils::Vector9d long_range_pressure() override
void scaleby_box_l() override
void assign_charge(double q, Utils::Vector3d const &real_pos, bool skip_cache) override
Base class for the electrostatics P3M algorithm.
std::shared_ptr< P3MFFTBackend< FloatType, FFTConfig > > fft
p3m_interpolation_cache inter_weights
std::size_t sum_qpart
number of charged particles.
p3m_send_mesh< FloatType > halo_comm
double sum_q2
Sum of square of charges.
void sanity_checks_periodicity() const
void sanity_checks_boxl() const
Checks for correctness of the k-space cutoff.
void sanity_checks_cell_structure() const
P3MParameters const & p3m_params
std::unique_ptr< Implementation > impl
Pointer-to-implementation.
static constexpr std::size_t force
static constexpr std::size_t pos
static constexpr std::size_t q
Interpolation weights for one point.
void recalc_ld_pos(P3MParameters const ¶ms)
Recalculate quantities derived from the mesh and box length: ld_pos (position of the left down mesh).
Structure to hold P3M parameters and some dependent variables.
Utils::Vector3d cao_cut
cutoff for charge assignment.
double alpha
unscaled alpha_L for use with fast inline functions only
double r_cut_iL
cutoff radius for real space electrostatics (>0), rescaled to r_cut_iL = r_cut * box_l_i.
int cao
charge assignment order ([0,7]).
double accuracy
accuracy of the actual parameter set.
double alpha_L
Ewald splitting parameter (0.
double r_cut
unscaled r_cut_iL for use with fast inline functions only
void recalc_a_ai_cao_cut(Utils::Vector3d const &box_l)
Recalculate quantities derived from the mesh and box length: a, ai and cao_cut.
bool tuning
tuning or production?
Utils::Vector3i mesh
number of mesh points per coordinate direction (>0), in real space.
double epsilon
epsilon of the "surrounding dielectric".
P3MLocalMesh local_mesh
Local mesh geometry information for this MPI rank.
P3MParameters params
P3M base parameters.
Struct holding all information for one particle.
constexpr auto const & pos() const
constexpr auto const & image_box() const
constexpr auto const & q() const
void operator()(auto &p3m, double q, Utils::Vector3d const &real_pos, p3m_interpolation_cache &inter_weights)
void operator()(auto &p3m, auto &cell_structure)
void operator()(auto &p3m, double q, Utils::Vector3d const &real_pos)
void operator()(auto &p3m, double q, InterpolationWeights< cao > const &weights)
void operator()(auto &p3m, auto force_prefac, CellStructure &cell_structure) const