36#include "communication.hpp"
38#include "system/System.hpp"
42#include <Kokkos_Core.hpp>
44#include <boost/mpi/collectives/all_reduce.hpp>
45#include <boost/range/combine.hpp>
109template <std::
size_t dir>
111 std::size_t n_freq,
double u) {
112 auto constexpr c_2pi = 2. * std::numbers::pi;
113 auto const n_part = particles.
size();
114 std::vector<SCCache> ret(n_freq * n_part);
116 for (std::size_t freq = 1; freq <= n_freq; freq++) {
117 auto const pref = c_2pi * u *
static_cast<double>(freq);
119 std::size_t o = (freq - 1) * n_part;
120 for (
auto const &p : particles) {
121 auto const arg = pref * p.pos()[dir];
122 ret[o++] = {sin(arg), cos(arg)};
129static std::pair<std::size_t, std::size_t>
132 assert(far_cut >= 0.);
133 auto const n_freq_x =
134 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[0]) + 1.);
135 auto const n_freq_y =
136 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[1]) + 1.);
139 scxcache = calc_sc_cache<0>(particles, n_freq_x, u_x);
140 scycache = calc_sc_cache<1>(particles, n_freq_y, u_y);
141 return {n_freq_x, n_freq_y};
149 for (std::size_t i = 0; i < size; i++)
153static void copy_vec(
double *pdc_d,
double const *pdc_s, std::size_t size) {
154 for (std::size_t i = 0; i < size; i++)
158static void add_vec(
double *pdc_d,
double const *pdc_s1,
double const *pdc_s2,
160 for (std::size_t i = 0; i < size; i++)
161 pdc_d[i] = pdc_s1[i] + pdc_s2[i];
164static void addscale_vec(
double *pdc_d,
double scale,
double const *pdc_s1,
165 double const *pdc_s2, std::size_t size) {
166 for (std::size_t i = 0; i < size; i++)
167 pdc_d[i] = scale * pdc_s1[i] + pdc_s2[i];
170static void scale_vec(
double scale,
double *pdc, std::size_t size) {
171 for (std::size_t i = 0; i < size; i++)
175static double *
block(
double *p, std::size_t index, std::size_t size) {
176 return &p[index * size];
183 boost::mpi::all_reduce(
comm_cart, send_buf,
static_cast<int>(size),
gblcblk,
187void ElectrostaticLayerCorrection::check_gap(
Particle const &p)
const {
189 auto const z = p.
pos()[2];
192 <<
"region by " << ((z < 0.) ? z : z -
elc.box_h);
204void ElectrostaticLayerCorrection::add_dipole_force()
const {
205 constexpr std::size_t size = 3;
207 auto const &box_geo = *system.box_geo;
208 auto const particles = system.cell_structure->local_particles();
209 auto const pref =
prefactor * 4. * std::numbers::pi / box_geo.volume();
213 auto const shift = box_geo.length_half()[2];
223 auto const q = p.
q();
224 auto const z = p.
pos()[2];
253 auto const field_induced =
gblcblk[1];
255 field_tot -= field_applied + field_induced;
259 p.
force()[2] -= field_tot * p.
q();
271double ElectrostaticLayerCorrection::dipole_energy()
const {
272 constexpr std::size_t size = 7;
274 auto const &box_geo = *system.box_geo;
275 auto const particles = system.cell_structure->local_particles();
276 auto const pref =
prefactor * 2. * std::numbers::pi / box_geo.volume();
277 auto const lz = box_geo.length()[2];
280 auto const shift = box_geo.length_half()[2];
294 auto const q = p.
q();
295 auto const z = p.
pos()[2];
324 energy += 2. * pref *
360 double b(
double q,
double z)
const {
365 double t(
double q,
double z)
const {
370double ElectrostaticLayerCorrection::z_energy()
const {
371 constexpr std::size_t size = 4;
373 auto const &box_geo = *system.box_geo;
374 auto const particles = system.cell_structure->local_particles();
375 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
376 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
380 auto const shift = box_geo.length_half()[2];
381 auto const lz = box_geo.length()[2];
388 auto const z = p.
pos()[2];
389 auto const q = p.
q();
406 auto const fac_delta = delta / (1. - delta);
411 auto const z = p.
pos()[2];
412 auto const q = p.
q();
445 return (
this_node == 0) ? -pref * energy : 0.;
448void ElectrostaticLayerCorrection::add_z_force()
const {
449 constexpr std::size_t size = 1;
451 auto const &box_geo = *system.box_geo;
452 auto const particles = system.cell_structure->local_particles();
453 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
454 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
462 auto const z = p.
pos()[2];
463 auto const q = p.
q();
474 auto const fac_delta = delta / (1. - delta);
477 auto const z = p.
pos()[2];
478 auto const q = p.
q();
515 constexpr std::size_t size = 4;
517 auto const pref_di = prefactor * 4. * std::numbers::pi * xy_area_inv;
518 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
519 double lclimgebot[4], lclimgetop[4], lclimge[4];
520 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
524 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
535 auto const o = (index - 1) * particles.
size();
536 for (
auto const &p : particles) {
537 auto const z = p.
pos()[2];
538 auto const q = p.
q();
539 auto e = exp(omega * z);
556 lclimgebot[
POQESM] = sc_cache[o + ic].s / e;
557 lclimgebot[
POQESP] = sc_cache[o + ic].s * e;
558 lclimgebot[
POQECM] = sc_cache[o + ic].c / e;
559 lclimgebot[
POQECP] = sc_cache[o + ic].c * e;
564 exp(omega * (+z - 2. * elc.
box_h))) *
567 e = (exp(-omega * z) +
572 lclimge[
POQESP] += q * sc_cache[o + ic].s * e;
573 lclimge[
POQECP] += q * sc_cache[o + ic].c * e;
576 e = exp(omega * (2. * elc.
box_h - z));
580 lclimgetop[
POQESM] = sc_cache[o + ic].s / e;
581 lclimgetop[
POQESP] = sc_cache[o + ic].s * e;
582 lclimgetop[
POQECM] = sc_cache[o + ic].c / e;
583 lclimgetop[
POQECP] = sc_cache[o + ic].c * e;
588 exp(omega * (-z - 2. * elc.
box_h))) *
591 e = (exp(omega * (+z - 2. * elc.
box_h)) +
596 lclimge[
POQESM] += q * sc_cache[o + ic].s * e;
597 lclimge[
POQECM] += q * sc_cache[o + ic].c * e;
612 constexpr auto i =
static_cast<int>(axis);
613 constexpr std::size_t size = 4;
616 for (
auto &p : particles) {
617 auto &force = p.
force();
631 constexpr std::size_t size = 4;
634 for (std::size_t ic = 0; ic < n_part; ic++) {
641 return energy / omega;
652 std::size_t index_q,
double omega,
655 assert(index_p >= 1);
656 assert(index_q >= 1);
657 constexpr std::size_t size = 8;
659 auto const pref_di = prefactor * 8. * std::numbers::pi * xy_area_inv;
660 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
661 double lclimgebot[8], lclimgetop[8], lclimge[8];
662 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
665 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
675 auto const ox = (index_p - 1) * particles.
size();
676 auto const oy = (index_q - 1) * particles.
size();
677 for (
auto const &p : particles) {
678 auto const z = p.
pos()[2];
679 auto const q = p.
q();
680 auto e = exp(omega * z);
722 exp(omega * (+z - 2. * elc.
box_h))) *
727 e = (exp(-omega * z) +
729 fac_delta_mid_bot * q;
739 e = exp(omega * (2. * elc.
box_h - z));
755 exp(omega * (-z - 2. * elc.
box_h))) *
760 e = (exp(omega * (+z - 2. * elc.
box_h)) +
762 fac_delta_mid_top * q;
781static void add_PQ_force(std::size_t index_p, std::size_t index_q,
double omega,
784 auto constexpr c_2pi = 2. * std::numbers::pi;
786 c_2pi * box_geo.
length_inv()[0] *
static_cast<double>(index_p) / omega;
788 c_2pi * box_geo.
length_inv()[1] *
static_cast<double>(index_q) / omega;
789 constexpr std::size_t size = 8;
792 for (
auto &p : particles) {
793 auto &force = p.
force();
822static double PQ_energy(
double omega, std::size_t n_part) {
823 constexpr std::size_t size = 8;
826 for (std::size_t ic = 0; ic < n_part; ic++) {
836 return energy / omega;
840void ElectrostaticLayerCorrection::add_force()
const {
841 auto constexpr c_2pi = 2. * std::numbers::pi;
843 auto const &box_geo = *system.box_geo;
844 auto const particles = system.cell_structure->local_particles();
846 auto const n_scxcache = std::get<0>(n_freqs);
847 auto const n_scycache = std::get<1>(n_freqs);
854 for (std::size_t p = 1;
855 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
858 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
859 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
861 add_PoQ_force<PoQ::P>(particles);
864 for (std::size_t q = 1;
865 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
868 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
869 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
871 add_PoQ_force<PoQ::Q>(particles);
874 for (std::size_t p = 1;
875 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
878 for (std::size_t q = 1;
879 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
881 static_cast<double>(q - 1)) <
887 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
888 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
896double ElectrostaticLayerCorrection::calc_energy()
const {
897 auto constexpr c_2pi = 2. * std::numbers::pi;
899 auto const &box_geo = *system.box_geo;
900 auto const particles = system.cell_structure->local_particles();
901 auto energy = dipole_energy() + z_energy();
903 auto const n_scxcache = std::get<0>(n_freqs);
904 auto const n_scycache = std::get<1>(n_freqs);
907 partblk.resize(n_localpart * 8);
910 for (std::size_t p = 1;
911 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
914 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
915 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
920 for (std::size_t q = 1;
921 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
924 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
925 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
930 for (std::size_t p = 1;
931 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
934 for (std::size_t q = 1;
935 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
937 static_cast<double>(q - 1)) <
943 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
944 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
954double ElectrostaticLayerCorrection::tune_far_cut()
const {
956 auto constexpr maximal_far_cut = 50.;
958 auto const box_l_x_inv = box_geo.length_inv()[0];
959 auto const box_l_y_inv = box_geo.length_inv()[1];
960 auto const min_inv_boxl = std::min(box_l_x_inv, box_l_y_inv);
961 auto const box_l_z = box_geo.length()[2];
966 auto tuned_far_cut = min_inv_boxl;
970 auto const pref = 2. * std::numbers::pi * tuned_far_cut;
971 auto const sum = pref + 2. * (box_l_x_inv + box_l_y_inv);
972 auto const den = expm1(pref * lz);
973 auto const num1 = exp(pref * h);
974 auto const num2 = 1. / num1;
977 (num1 / (lz - h) * (sum + 1. / (lz - h)) +
978 num2 / (lz + h) * (sum + 1. / (lz + h)));
980 tuned_far_cut += min_inv_boxl;
981 }
while (err >
elc.
maxPWerror and tuned_far_cut < maximal_far_cut);
982 if (tuned_far_cut >= maximal_far_cut) {
983 throw std::runtime_error(
"ELC tuning failed: maxPWerror too small");
985 return tuned_far_cut - min_inv_boxl;
993 return boost::mpi::all_reduce(
comm_cart, local_q, std::plus<>());
996void ElectrostaticLayerCorrection::sanity_checks_periodicity()
const {
998 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
999 throw std::runtime_error(
"ELC: requires periodicity (True, True, True)");
1003void ElectrostaticLayerCorrection::sanity_checks_dielectric_contrasts()
const {
1005 auto const &cell_structure = *
get_system().cell_structure;
1008 if (total_charge >= precision_threshold) {
1012 throw std::runtime_error(
"ELC does not currently support non-neutral "
1013 "systems with a dielectric contrast.");
1017 throw std::runtime_error(
"ELC does not work for non-neutral systems and "
1018 "non-metallic dielectric contrast.");
1023void ElectrostaticLayerCorrection::adapt_solver() {
1025 [
this](
auto &solver) {
1027 solver->adapt_epsilon_elc();
1033void ElectrostaticLayerCorrection::recalc_box_h() {
1037 if (new_box_h < 0.) {
1038 throw std::runtime_error(
"ELC gap size (" + std::to_string(
elc.
gap_size) +
1039 ") larger than box length in z-direction (" +
1040 std::to_string(box_z) +
")");
1045void ElectrostaticLayerCorrection::recalc_space_layer() {
1047 auto const p3m_r_cut = std::visit(
1048 [](
auto &solver) {
return solver->p3m_params.r_cut; },
base_solver);
1056 auto const half_box_h =
elc.
box_h / 2.;
1057 auto const max_space_layer = std::min(free_space, half_box_h);
1059 if (max_space_layer <= 0.) {
1060 throw std::runtime_error(
"P3M real-space cutoff too large for ELC w/ "
1061 "dielectric contrast");
1070 bool neutralize,
double delta_top,
double delta_bot,
1071 bool with_const_pot,
double potential_diff)
1072 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
1073 far_cut2{-1.}, far_calculated{far_cut == -1.},
1074 dielectric_contrast_on{delta_top != 0. or delta_bot != 0.},
1075 const_pot{with_const_pot and dielectric_contrast_on},
1076 neutralize{neutralize and !dielectric_contrast_on},
1077 delta_mid_top{
std::clamp(delta_top, -1., +1.)},
1078 delta_mid_bot{
std::clamp(delta_bot, -1., +1.)},
1079 pot_diff{(with_const_pot) ? potential_diff : 0.},
1082 space_layer{(dielectric_contrast_on) ? gap_size / 3. : 0.},
1083 space_box{gap_size - ((dielectric_contrast_on) ? 2. * space_layer : 0.)} {
1087 throw std::domain_error(
"Parameter 'far_cut' must be > 0");
1090 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
1093 throw std::domain_error(
"Parameter 'gap_size' must be > 0");
1095 if (potential_diff != 0. and not with_const_pot) {
1096 throw std::invalid_argument(
1097 "Parameter 'const_pot' must be True when 'pot_diff' is non-zero");
1099 if (delta_top < -delta_range or delta_top > delta_range) {
1100 throw std::domain_error(
1101 "Parameter 'delta_mid_top' must be >= -1 and <= +1");
1103 if (delta_bot < -delta_range or delta_bot > delta_range) {
1104 throw std::domain_error(
1105 "Parameter 'delta_mid_bot' must be >= -1 and <= +1");
1112 throw std::domain_error(
"ELC with two parallel metallic boundaries "
1113 "requires the const_pot option");
1119 : elc{parameters}, base_solver{solver} {
1123template <ChargeProtocol protocol,
typename combined_ranges>
1125 combined_ranges
const &p_q_pos_range) {
1131 auto constexpr include_neutral_particles =
true;
1133 for (
auto zipped : p_q_pos_range) {
1134 auto const p_q = boost::get<0>(zipped);
1135 auto const &p_pos = boost::get<1>(zipped);
1136 if (include_neutral_particles or p_q != 0.) {
1147 solver.
assign_charge(q_eff, {p_pos[0], p_pos[1], -p_pos[2]},
true);
1152 q_eff, {p_pos[0], p_pos[1], 2. * elc.
box_h - p_pos[2]},
true);
1159template <ChargeProtocol protocol,
typename combined_range>
1161 combined_range
const &p_q_pos_range) {
1163 auto local_n = std::size_t{0u};
1164 auto local_q2 = 0.0;
1166 for (
auto zipped : p_q_pos_range) {
1167 auto const p_q = boost::get<0>(zipped);
1168 auto const &p_pos = boost::get<1>(zipped);
1170 auto const p_z = p_pos[2];
1196 auto global_n = std::size_t{0u};
1197 auto global_q2 = 0.;
1199 boost::mpi::all_reduce(
comm_cart, local_n, global_n, std::plus<>());
1200 boost::mpi::all_reduce(
comm_cart, local_q2, global_q2, std::plus<>());
1201 boost::mpi::all_reduce(
comm_cart, local_q, global_q, std::plus<>());
1207 auto const energy = std::visit(
1208 [
this, &system](
auto const &solver_ptr) {
1209 auto &solver = *solver_ptr;
1210 auto const particles = system.cell_structure->local_particles();
1211 auto const &box_geo = *system.box_geo;
1215 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1218 solver.charge_assign();
1221 return solver.long_range_energy();
1225 energy += 0.5 * solver.long_range_energy();
1230 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1231 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1232 energy += 0.5 * solver.long_range_energy();
1235 charge_assign<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1236 modify_p3m_sums<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1237 energy -= 0.5 * solver.long_range_energy();
1240 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1245 return energy + calc_energy();
1251 [
this, &system](
auto const &solver_ptr) {
1252 auto const particles = system.cell_structure->local_particles();
1253 auto &solver = *solver_ptr;
1256 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1258 auto const &box_geo = *system.box_geo;
1259 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1260 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1263 solver.charge_assign();
1265 solver.add_long_range_forces();
1267 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
Utils::Vector3d const & length() const
Box length.
Utils::Vector3d const & length_inv() const
Inverse box length.
Describes a cell structure / cell system.
ParticleRange local_particles() const
void set_prefactor(double new_prefactor)
double prefactor
Electrostatics prefactor.
base_type::size_type size() const
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.
T image_sum(InputIterator begin, InputIterator end, InputIterator it, bool with_replicas, Utils::Vector3i const &ncut, BoxGeometry const &box_geo, T init, F f)
Sum over all pairs with periodic images.
static void addscale_vec(double *pdc_d, double scale, double const *pdc_s1, double const *pdc_s2, std::size_t size)
static std::pair< std::size_t, std::size_t > prepare_sc_cache(ParticleRange const &particles, BoxGeometry const &box_geo, double far_cut)
static void add_PQ_force(std::size_t index_p, std::size_t index_q, double omega, ParticleRange const &particles, BoxGeometry const &box_geo)
static auto calc_total_charge(CellStructure const &cell_structure)
static std::vector< double > partblk
temporary buffers for product decomposition
static void clear_vec(double *pdc, std::size_t size)
static double * block(double *p, std::size_t index, std::size_t size)
void setup_PoQ(elc_data const &elc, double prefactor, std::size_t index, double omega, ParticleRange const &particles, BoxGeometry const &box_geo)
static void distribute(std::size_t size)
void modify_p3m_sums(elc_data const &elc, CoulombP3M &solver, combined_range const &p_q_pos_range)
static double PoQ_energy(double omega, std::size_t n_part)
static std::vector< SCCache > scxcache
Cached sin/cos values along the x-axis and y-axis.
static double PQ_energy(double omega, std::size_t n_part)
void charge_assign(elc_data const &elc, CoulombP3M &solver, combined_ranges const &p_q_pos_range)
static std::vector< SCCache > scycache
static void setup_PQ(elc_data const &elc, double prefactor, std::size_t index_p, std::size_t index_q, double omega, ParticleRange const &particles, BoxGeometry const &box_geo)
static void copy_vec(double *pdc_d, double const *pdc_s, std::size_t size)
static void add_vec(double *pdc_d, double const *pdc_s1, double const *pdc_s2, std::size_t size)
static void scale_vec(double scale, double *pdc, std::size_t size)
void add_PoQ_force(ParticleRange const &particles)
ChargeProtocol
ELC charge sum/assign protocol: real charges, image charges, or both.
static std::vector< SCCache > calc_sc_cache(ParticleRange const &particles, std::size_t n_freq, double u)
Calculate cached sin/cos values for one direction.
static double gblcblk[8]
collected data from the other cells
PoQ
ELC axes (x and y directions)
ELC algorithm for long-range Coulomb interactions.
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)
auto charge_range(ParticleRange const &particles)
auto pos_range(ParticleRange const &particles)
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
auto sqrt(Vector< T, N > const &a)
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
P3M algorithm for long-range Coulomb interaction.
virtual void prepare_fft_mesh(bool reset_weights)=0
virtual void count_charged_particles_elc(std::size_t, double, double)=0
virtual void assign_charge(double q, Utils::Vector3d const &real_pos, bool skip_cache)=0
Assign a single charge into the current charge grid.
void add_long_range_forces() const
Accumulate long-range electrostatic forces with corrections.
std::variant< std::shared_ptr< CoulombP3M > > BaseSolver
BaseSolver base_solver
Electrostatics solver that is adapted.
ElectrostaticLayerCorrection(elc_data &¶meters, BaseSolver &&solver)
double long_range_energy() const
Calculate long-range electrostatic energy with corrections.
double b(double q, double z) const
Image sum from the bottom layer.
ImageSum(double delta, double shift, double lz)
double t(double q, double z) const
Image sum from the top layer.
Struct holding all information for one particle.
auto const & force() const
structure for caching sin and cos values
Parameters for the ELC method.
double dielectric_layers_self_energy(CoulombP3M const &p3m, BoxGeometry const &box_geo, ParticleRange const &particles) const
self energies of top and bottom layers with their virtual images
double maxPWerror
Maximal allowed pairwise error for the potential and force.
double pot_diff
Constant potential difference.
double box_h
Up to where particles can be found.
bool dielectric_contrast_on
Flag whether there is any dielectric contrast in the system.
elc_data(double maxPWerror, double gap_size, double far_cut, bool neutralize, double delta_top, double delta_bot, bool const_pot, double pot_diff)
double space_box
The space that is finally left.
bool neutralize
Flag whether the box is neutralized by a homogeneous background.
double far_cut
Cutoff of the exponential sum.
double space_layer
Layer around the dielectric contrast in which we trick around.
bool far_calculated
Flag whether far_cut was set by the user, or calculated by ESPResSo.
double gap_size
Size of the empty gap.
double delta_mid_bot
dielectric contrast in the lower part of the simulation cell.
void dielectric_layers_self_forces(CoulombP3M const &p3m, BoxGeometry const &box_geo, ParticleRange const &particles) const
forces of particles in border layers with themselves
bool const_pot
Flag whether a constant potential difference is applied.
double far_cut2
Squared value of far_cut.
double delta_mid_top
dielectric contrast in the upper part of the simulation cell.