24#ifdef ESPRESSO_DIPOLES
36#include "communication.hpp"
38#include "system/System.hpp"
42#include <boost/mpi/collectives/all_reduce.hpp>
43#include <boost/mpi/collectives/reduce.hpp>
44#include <boost/mpi/operations.hpp>
60void DipolarLayerCorrection::check_gap(
Particle const &p)
const {
62 auto const z = p.
pos()[2];
65 <<
"by " << z - ((z < 0.) ? 0. :
dlc.box_h);
73 local_particles.begin(), local_particles.end(), 0.,
74 [](
double mu,
Particle const &p) { return std::max(mu, p.dipm()); });
77 boost::mpi::maximum<double>());
82 auto const cc2 = c * c;
83 auto const x3 = x * x * x;
84 auto const a =
g *
g *
g / x + 1.5 *
cc2 + 1.5 *
g /
x3 + 0.75 / (
x3 * x);
89 auto const x2 = x * x;
90 auto const a =
g *
g / x + 2.0 *
g /
x2 + 2.0 / (
x2 * x);
98 for (
auto const &p : particles) {
112 std::vector<Utils::Vector3d> &
fs,
113 std::vector<Utils::Vector3d> &
ts,
134 auto const gx =
static_cast<double>(
ix) *
facux;
135 auto const gy =
static_cast<double>(
iy) *
facuy;
144 double S[4] = {0., 0., 0., 0.};
146 for (
auto const &p : particles) {
147 if (p.
dipm() != 0.) {
148 auto const &pos = p.
pos();
151 auto const a =
gx * dip[0] +
gy * dip[1];
152 auto const b =
gr * dip[2];
153 auto const er =
gx * pos[0] +
gy * pos[1];
154 auto const ez =
gr * pos[2];
155 auto const c =
cos(
er);
156 auto const d =
sin(
er);
157 auto const f =
exp(
ez);
159 ReSjp[
ip] = (b * c - a * d) * f;
160 ImSjp[
ip] = (c * a + b * d) * f;
161 ReSjm[
ip] = (-b * c - a * d) / f;
162 ImSjm[
ip] = (c * a - b * d) / f;
181 for (
auto &p : particles) {
182 if (p.
dipm() != 0.) {
226 for (
auto const &p : particles) {
227 if (p.
dipm() != 0.) {
254 double sum_S[4] = {0., 0., 0., 0.};
257 if ((
ix == 0 &&
iy == 0)) {
260 auto const gx =
static_cast<double>(
ix) *
facux;
261 auto const gy =
static_cast<double>(
iy) *
facuy;
269 double S[4] = {0., 0., 0., 0.};
271 for (
auto const &p : particles) {
272 if (p.
dipm() != 0.) {
273 auto const &pos = p.
pos();
276 auto const a =
gx * dip[0] +
gy * dip[1];
277 auto const b =
gr * dip[2];
278 auto const er =
gx * pos[0] +
gy * pos[1];
279 auto const ez =
gr * pos[2];
280 auto const c =
cos(
er);
281 auto const d =
sin(
er);
282 auto const f =
exp(
ez);
284 S[0] += (b * c - a * d) * f;
285 S[1] += (c * a + b * d) * f;
286 S[2] += (-b * c - a * d) / f;
287 S[3] += (c * a - b * d) / f;
307 auto const &box_geo = *
system.box_geo;
308 auto const particles =
system.cell_structure->local_particles();
309 auto const volume = box_geo.volume();
310 auto const correc = 4. * std::numbers::pi / volume;
336 for (
auto &p : particles) {
339 if (p.
dipm() != 0.) {
360 auto const &box_geo = *
system.box_geo;
361 auto const particles =
system.cell_structure->local_particles();
362 auto const volume = box_geo.volume();
363 auto const pref =
prefactor * 2. * std::numbers::pi / volume;
368 for (
auto const &p : particles) {
403 for (
auto const &p : particles) {
404 if (p.
dipm() != 0.) {
418double DipolarLayerCorrection::tune_far_cut()
const {
422 auto const &box_geo = *
system.box_geo;
423 auto const lx = box_geo.length()[0];
424 auto const ly = box_geo.length()[1];
425 auto const lz = box_geo.length()[2];
429 if (std::abs(
lx -
ly) > 0.001) {
430 throw std::runtime_error(
"DLC tuning: box size in x direction is "
431 "different from y direction. The tuning "
432 "formula requires both to be equal.");
436 auto constexpr exp_max = +709.8;
437 auto constexpr exp_min = -708.4;
438 auto const log_max = std::log(std::numeric_limits<double>::max());
439 auto const piarea = std::numbers::pi / (
lx *
ly);
444 auto const gc =
kc * 2. * std::numbers::pi /
lx;
460 far_cut =
static_cast<double>(
kc);
465 throw std::runtime_error(
"DLC tuning failed: maxPWerror too small");
474 void operator()(std::shared_ptr<DipolarDirectSum>
const &solver) {
482 m_actor->
epsilon = solver->dp3m_params.epsilon;
497void DipolarLayerCorrection::sanity_checks_periodicity()
const {
499 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
500 throw std::runtime_error(
"DLC: requires periodicity (True, True, True)");
505 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
506 far_calculated{far_cut == -1.} {
508 throw std::domain_error(
"Parameter 'far_cut' must be > 0");
511 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
514 throw std::domain_error(
"Parameter 'gap_size' must be > 0");
519 auto const lz =
get_system().box_geo->length()[2];
522 throw std::runtime_error(
"DLC gap size (" + std::to_string(
dlc.
gap_size) +
523 ") larger than box length in z-direction (" +
524 std::to_string(lz) +
")");
536 std::visit([](
auto const &solver) { solver->add_long_range_forces(); },
543 std::visit([](
auto const &solver) {
return solver->long_range_energy(); },
Utils::Vector3d const & length() const
Box length.
Utils::Vector3d const & length_inv() const
Inverse box length.
base_type::size_type size() const
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
__device__ void vector_product(float const *a, float const *b, float *out)
static void dipolar_force_corrections(int kcut, std::vector< Utils::Vector3d > &fs, std::vector< Utils::Vector3d > &ts, ParticleRange const &particles, BoxGeometry const &box_geo)
Compute the dipolar force and torque corrections.
static double g1_DLC_dip(double g, double x)
static double dipolar_energy_correction(int kcut, ParticleRange const &particles, BoxGeometry const &box_geo)
Compute the dipolar DLC energy correction.
static double g2_DLC_dip(double g, double x)
static std::size_t count_magnetic_particles(ParticleRange const &particles)
static Utils::Vector3d calc_slab_dipole(ParticleRange const &particles)
Compute the box magnetic dipole.
static double calc_mu_max(ParticleRange const &local_particles)
Calculate the maximal dipole moment in the system.
P3M algorithm for long-range magnetic dipole-dipole interaction.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeErrorMsg()
ParticleRange particles(std::span< Cell *const > cells)
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
auto sqrt(Vector< T, N > const &a)
Common functions for dipolar and charge P3M.
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
Lock an actor and modify its parameters.
AdaptSolver(DipolarLayerCorrection *this_ptr)
void operator()(std::shared_ptr< DipolarDirectSum > const &solver)
void operator()(std::shared_ptr< DipolarP3M > const &solver)
Adapt a magnetostatics solver to remove contributions from the z-direction.
double epsilon_correction
void add_force_corrections() const
Add the dipolar force and torque corrections.
BaseSolver base_solver
Magnetostatics solver that is adapted.
double energy_correction() const
Calculate the dipolar energy correction.
DipolarLayerCorrection(dlc_data &¶meters, BaseSolver &&solver)
std::variant< std::shared_ptr< DipolarP3M >, std::shared_ptr< DipolarDirectSum > > BaseSolver
void add_long_range_forces() const
Accumulate long-range dipolar forces with corrections.
double long_range_energy() const
Calculate long-range dipolar energy with corrections.
Struct holding all information for one particle.
auto const & torque() const
auto const & dipm() const
auto const & force() const
Parameters for the DLC method.
bool far_calculated
Flag whether far_cut was set by the user, or calculated by ESPResSo.
dlc_data(double maxPWerror, double gap_size, double far_cut)
double maxPWerror
maximal pairwise error of the potential and force
double far_cut
Cutoff of the exponential sum.
double gap_size
Size of the empty gap.
double box_h
Up to where particles can be found.