36#include "communication.hpp"
38#include "system/System.hpp"
43#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
44#include <Kokkos_Core.hpp>
47#include <boost/mpi/collectives/all_reduce.hpp>
48#include <boost/range/combine.hpp>
110template <std::
size_t dir>
112 std::size_t n_freq,
double u) {
113 auto constexpr c_2pi = 2. * std::numbers::pi;
114 auto const n_part = particles.
size();
115 std::vector<SCCache> ret(n_freq * n_part);
117 for (std::size_t freq = 1; freq <= n_freq; freq++) {
118 auto const pref = c_2pi * u *
static_cast<double>(freq);
120 std::size_t o = (freq - 1) * n_part;
121 for (
auto const &p : particles) {
122 auto const arg = pref * p.pos()[dir];
123 ret[o++] = {sin(arg), cos(arg)};
130static std::pair<std::size_t, std::size_t>
133 assert(far_cut >= 0.);
134 auto const n_freq_x =
135 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[0]) + 1.);
136 auto const n_freq_y =
137 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[1]) + 1.);
140 scxcache = calc_sc_cache<0>(particles, n_freq_x, u_x);
141 scycache = calc_sc_cache<1>(particles, n_freq_y, u_y);
142 return {n_freq_x, n_freq_y};
150 for (std::size_t i = 0; i < size; i++)
154static void copy_vec(
double *pdc_d,
double const *pdc_s, std::size_t size) {
155 for (std::size_t i = 0; i < size; i++)
159static void add_vec(
double *pdc_d,
double const *pdc_s1,
double const *pdc_s2,
161 for (std::size_t i = 0; i < size; i++)
162 pdc_d[i] = pdc_s1[i] + pdc_s2[i];
165static void addscale_vec(
double *pdc_d,
double scale,
double const *pdc_s1,
166 double const *pdc_s2, std::size_t size) {
167 for (std::size_t i = 0; i < size; i++)
168 pdc_d[i] = scale * pdc_s1[i] + pdc_s2[i];
171static void scale_vec(
double scale,
double *pdc, std::size_t size) {
172 for (std::size_t i = 0; i < size; i++)
176static double *
block(
double *p, std::size_t index, std::size_t size) {
177 return &p[index * size];
184 boost::mpi::all_reduce(
comm_cart, send_buf,
static_cast<int>(size),
gblcblk,
188void ElectrostaticLayerCorrection::check_gap(
Particle const &p)
const {
190 auto const z = p.
pos()[2];
193 <<
"region by " << ((z < 0.) ? z : z -
elc.box_h);
205void ElectrostaticLayerCorrection::add_dipole_force(
207 constexpr std::size_t size = 3;
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(
273 constexpr std::size_t size = 7;
275 auto const pref =
prefactor * 2. * std::numbers::pi / box_geo.volume();
276 auto const lz = box_geo.length()[2];
279 auto const shift = box_geo.length_half()[2];
293 auto const q = p.
q();
294 auto const z = p.
pos()[2];
323 energy += 2. * pref *
359 double b(
double q,
double z)
const {
364 double t(
double q,
double z)
const {
370ElectrostaticLayerCorrection::z_energy(
ParticleRange const &particles)
const {
371 constexpr std::size_t size = 4;
373 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
374 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
378 auto const shift = box_geo.length_half()[2];
379 auto const lz = box_geo.length()[2];
386 auto const z = p.
pos()[2];
387 auto const q = p.
q();
404 auto const fac_delta = delta / (1. - delta);
409 auto const z = p.
pos()[2];
410 auto const q = p.
q();
443 return (
this_node == 0) ? -pref * energy : 0.;
446void ElectrostaticLayerCorrection::add_z_force(
448 constexpr std::size_t size = 1;
450 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
451 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
459 auto const z = p.
pos()[2];
460 auto const q = p.
q();
471 auto const fac_delta = delta / (1. - delta);
474 auto const z = p.
pos()[2];
475 auto const q = p.
q();
512 constexpr std::size_t size = 4;
514 auto const pref_di = prefactor * 4. * std::numbers::pi * xy_area_inv;
515 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
516 double lclimgebot[4], lclimgetop[4], lclimge[4];
517 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
521 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
532 auto const o = (index - 1) * particles.
size();
533 for (
auto const &p : particles) {
534 auto const z = p.
pos()[2];
535 auto const q = p.
q();
536 auto e = exp(omega * z);
553 lclimgebot[
POQESM] = sc_cache[o + ic].s / e;
554 lclimgebot[
POQESP] = sc_cache[o + ic].s * e;
555 lclimgebot[
POQECM] = sc_cache[o + ic].c / e;
556 lclimgebot[
POQECP] = sc_cache[o + ic].c * e;
561 exp(omega * (+z - 2. * elc.
box_h))) *
564 e = (exp(-omega * z) +
569 lclimge[
POQESP] += q * sc_cache[o + ic].s * e;
570 lclimge[
POQECP] += q * sc_cache[o + ic].c * e;
573 e = exp(omega * (2. * elc.
box_h - z));
577 lclimgetop[
POQESM] = sc_cache[o + ic].s / e;
578 lclimgetop[
POQESP] = sc_cache[o + ic].s * e;
579 lclimgetop[
POQECM] = sc_cache[o + ic].c / e;
580 lclimgetop[
POQECP] = sc_cache[o + ic].c * e;
585 exp(omega * (-z - 2. * elc.
box_h))) *
588 e = (exp(omega * (+z - 2. * elc.
box_h)) +
593 lclimge[
POQESM] += q * sc_cache[o + ic].s * e;
594 lclimge[
POQECM] += q * sc_cache[o + ic].c * e;
609 constexpr auto i =
static_cast<int>(axis);
610 constexpr std::size_t size = 4;
613 for (
auto &p : particles) {
614 auto &force = p.
force();
628 constexpr std::size_t size = 4;
631 for (std::size_t ic = 0; ic < n_part; ic++) {
638 return energy / omega;
649 std::size_t index_q,
double omega,
652 assert(index_p >= 1);
653 assert(index_q >= 1);
654 constexpr std::size_t size = 8;
656 auto const pref_di = prefactor * 8. * std::numbers::pi * xy_area_inv;
657 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
658 double lclimgebot[8], lclimgetop[8], lclimge[8];
659 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
662 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
672 auto const ox = (index_p - 1) * particles.
size();
673 auto const oy = (index_q - 1) * particles.
size();
674 for (
auto const &p : particles) {
675 auto const z = p.
pos()[2];
676 auto const q = p.
q();
677 auto e = exp(omega * z);
719 exp(omega * (+z - 2. * elc.
box_h))) *
724 e = (exp(-omega * z) +
726 fac_delta_mid_bot * q;
736 e = exp(omega * (2. * elc.
box_h - z));
752 exp(omega * (-z - 2. * elc.
box_h))) *
757 e = (exp(omega * (+z - 2. * elc.
box_h)) +
759 fac_delta_mid_top * q;
778static void add_PQ_force(std::size_t index_p, std::size_t index_q,
double omega,
781 auto constexpr c_2pi = 2. * std::numbers::pi;
783 c_2pi * box_geo.
length_inv()[0] *
static_cast<double>(index_p) / omega;
785 c_2pi * box_geo.
length_inv()[1] *
static_cast<double>(index_q) / omega;
786 constexpr std::size_t size = 8;
789 for (
auto &p : particles) {
790 auto &force = p.
force();
819static double PQ_energy(
double omega, std::size_t n_part) {
820 constexpr std::size_t size = 8;
823 for (std::size_t ic = 0; ic < n_part; ic++) {
833 return energy / omega;
837void ElectrostaticLayerCorrection::add_force(
839 auto constexpr c_2pi = 2. * std::numbers::pi;
842 auto const n_scxcache = std::get<0>(n_freqs);
843 auto const n_scycache = std::get<1>(n_freqs);
846 add_dipole_force(particles);
847 add_z_force(particles);
850 for (std::size_t p = 1;
851 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
854 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
855 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
857 add_PoQ_force<PoQ::P>(particles);
860 for (std::size_t q = 1;
861 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
864 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
865 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
867 add_PoQ_force<PoQ::Q>(particles);
870 for (std::size_t p = 1;
871 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
874 for (std::size_t q = 1;
875 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
877 static_cast<double>(q - 1)) <
883 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
884 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
892double ElectrostaticLayerCorrection::calc_energy(
894 auto constexpr c_2pi = 2. * std::numbers::pi;
896 auto energy = dipole_energy(particles) + z_energy(particles);
898 auto const n_scxcache = std::get<0>(n_freqs);
899 auto const n_scycache = std::get<1>(n_freqs);
902 partblk.resize(n_localpart * 8);
905 for (std::size_t p = 1;
906 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
909 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
910 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
915 for (std::size_t q = 1;
916 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
919 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
920 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
925 for (std::size_t p = 1;
926 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
929 for (std::size_t q = 1;
930 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
932 static_cast<double>(q - 1)) <
938 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
939 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
949double ElectrostaticLayerCorrection::tune_far_cut()
const {
951 auto constexpr maximal_far_cut = 50.;
953 auto const box_l_x_inv = box_geo.length_inv()[0];
954 auto const box_l_y_inv = box_geo.length_inv()[1];
955 auto const min_inv_boxl = std::min(box_l_x_inv, box_l_y_inv);
956 auto const box_l_z = box_geo.length()[2];
961 auto tuned_far_cut = min_inv_boxl;
964 auto const pref = 2. * std::numbers::pi * tuned_far_cut;
965 auto const sum = pref + 2. * (box_l_x_inv + box_l_y_inv);
966 auto const den = -expm1(-pref * lz);
967 auto const num1 = exp(pref * (
elc.
box_h - lz));
968 auto const num2 = exp(-pref * (
elc.
box_h + lz));
974 tuned_far_cut += min_inv_boxl;
975 }
while (err >
elc.
maxPWerror and tuned_far_cut < maximal_far_cut);
976 if (tuned_far_cut >= maximal_far_cut) {
977 throw std::runtime_error(
"ELC tuning failed: maxPWerror too small");
979 return tuned_far_cut - min_inv_boxl;
987 return boost::mpi::all_reduce(
comm_cart, local_q, std::plus<>());
990void ElectrostaticLayerCorrection::sanity_checks_periodicity()
const {
992 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
993 throw std::runtime_error(
"ELC: requires periodicity (True, True, True)");
997void ElectrostaticLayerCorrection::sanity_checks_dielectric_contrasts()
const {
999 auto const &cell_structure = *
get_system().cell_structure;
1002 if (total_charge >= precision_threshold) {
1006 throw std::runtime_error(
"ELC does not currently support non-neutral "
1007 "systems with a dielectric contrast.");
1011 throw std::runtime_error(
"ELC does not work for non-neutral systems and "
1012 "non-metallic dielectric contrast.");
1017void ElectrostaticLayerCorrection::adapt_solver() {
1019 [
this](
auto &solver) {
1021 solver->adapt_epsilon_elc();
1027void ElectrostaticLayerCorrection::recalc_box_h() {
1031 if (new_box_h < 0.) {
1032 throw std::runtime_error(
"ELC gap size (" + std::to_string(
elc.
gap_size) +
1033 ") larger than box length in z-direction (" +
1034 std::to_string(box_z) +
")");
1039void ElectrostaticLayerCorrection::recalc_space_layer() {
1041 auto const p3m_r_cut = std::visit(
1042 [](
auto &solver) {
return solver->p3m_params.r_cut; },
base_solver);
1050 auto const half_box_h =
elc.
box_h / 2.;
1051 auto const max_space_layer = std::min(free_space, half_box_h);
1053 if (max_space_layer <= 0.) {
1054 throw std::runtime_error(
"P3M real-space cutoff too large for ELC w/ "
1055 "dielectric contrast");
1064 bool neutralize,
double delta_top,
double delta_bot,
1065 bool with_const_pot,
double potential_diff)
1066 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
1067 far_cut2{-1.}, far_calculated{far_cut == -1.},
1068 dielectric_contrast_on{delta_top != 0. or delta_bot != 0.},
1069 const_pot{with_const_pot and dielectric_contrast_on},
1070 neutralize{neutralize and !dielectric_contrast_on},
1071 delta_mid_top{
std::clamp(delta_top, -1., +1.)},
1072 delta_mid_bot{
std::clamp(delta_bot, -1., +1.)},
1073 pot_diff{(with_const_pot) ? potential_diff : 0.},
1076 space_layer{(dielectric_contrast_on) ? gap_size / 3. : 0.},
1077 space_box{gap_size - ((dielectric_contrast_on) ? 2. * space_layer : 0.)} {
1081 throw std::domain_error(
"Parameter 'far_cut' must be > 0");
1084 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
1087 throw std::domain_error(
"Parameter 'gap_size' must be > 0");
1089 if (potential_diff != 0. and not with_const_pot) {
1090 throw std::invalid_argument(
1091 "Parameter 'const_pot' must be True when 'pot_diff' is non-zero");
1093 if (delta_top < -delta_range or delta_top > delta_range) {
1094 throw std::domain_error(
1095 "Parameter 'delta_mid_top' must be >= -1 and <= +1");
1097 if (delta_bot < -delta_range or delta_bot > delta_range) {
1098 throw std::domain_error(
1099 "Parameter 'delta_mid_bot' must be >= -1 and <= +1");
1106 throw std::domain_error(
"ELC with two parallel metallic boundaries "
1107 "requires the const_pot option");
1113 : elc{parameters}, base_solver{solver} {
1117template <ChargeProtocol protocol,
typename combined_ranges>
1119 combined_ranges
const &p_q_pos_range) {
1124#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
1126 auto const include_neutral_particles = Kokkos::num_threads() > 1;
1128 auto constexpr include_neutral_particles =
false;
1131 for (
auto zipped : p_q_pos_range) {
1132 auto const p_q = boost::get<0>(zipped);
1133 auto const &p_pos = boost::get<1>(zipped);
1134 if (include_neutral_particles or p_q != 0.) {
1145 solver.
assign_charge(q_eff, {p_pos[0], p_pos[1], -p_pos[2]},
true);
1150 q_eff, {p_pos[0], p_pos[1], 2. * elc.
box_h - p_pos[2]},
true);
1157template <ChargeProtocol protocol,
typename combined_range>
1159 combined_range
const &p_q_pos_range) {
1161 auto local_n = std::size_t{0u};
1162 auto local_q2 = 0.0;
1164 for (
auto zipped : p_q_pos_range) {
1165 auto const p_q = boost::get<0>(zipped);
1166 auto const &p_pos = boost::get<1>(zipped);
1168 auto const p_z = p_pos[2];
1194 auto global_n = std::size_t{0u};
1195 auto global_q2 = 0.;
1197 boost::mpi::all_reduce(
comm_cart, local_n, global_n, std::plus<>());
1198 boost::mpi::all_reduce(
comm_cart, local_q2, global_q2, std::plus<>());
1199 boost::mpi::all_reduce(
comm_cart, local_q, global_q, std::plus<>());
1205 auto const energy = std::visit(
1206 [
this, &particles](
auto const &solver_ptr) {
1207 auto &solver = *solver_ptr;
1212 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1215 solver.charge_assign(particles);
1218 return solver.long_range_energy(particles);
1222 energy += 0.5 * solver.long_range_energy(particles);
1227 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1228 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1229 energy += 0.5 * solver.long_range_energy(particles);
1232 charge_assign<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1233 modify_p3m_sums<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1234 energy -= 0.5 * solver.long_range_energy(particles);
1237 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1242 return energy + calc_energy(particles);
1248 [
this, &particles](
auto const &solver_ptr) {
1249 auto &solver = *solver_ptr;
1252 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1255 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1256 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1259 solver.charge_assign(particles);
1261 solver.add_long_range_forces(particles);
1263 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1267 add_force(particles);
Vector implementation and trait types for boost qvm interoperability.
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.
std::variant< std::shared_ptr< CoulombP3M > > BaseSolver
BaseSolver base_solver
Electrostatics solver that is adapted.
ElectrostaticLayerCorrection(elc_data &¶meters, BaseSolver &&solver)
void add_long_range_forces(ParticleRange const &particles) const
double long_range_energy(ParticleRange const &particles) const
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.