36#include "communication.hpp"
38#include "system/System.hpp"
43#include <boost/mpi/collectives/all_reduce.hpp>
44#include <boost/range/combine.hpp>
106template <std::
size_t dir>
108 std::size_t n_freq,
double u) {
109 auto constexpr c_2pi = 2. * std::numbers::pi;
110 auto const n_part = particles.
size();
111 std::vector<SCCache> ret(n_freq * n_part);
113 for (std::size_t freq = 1; freq <= n_freq; freq++) {
114 auto const pref = c_2pi * u *
static_cast<double>(freq);
116 std::size_t o = (freq - 1) * n_part;
117 for (
auto const &p : particles) {
118 auto const arg = pref * p.pos()[dir];
119 ret[o++] = {sin(arg), cos(arg)};
126static std::pair<std::size_t, std::size_t>
129 assert(far_cut >= 0.);
130 auto const n_freq_x =
131 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[0]) + 1.);
132 auto const n_freq_y =
133 static_cast<std::size_t
>(std::ceil(far_cut * box_geo.
length()[1]) + 1.);
136 scxcache = calc_sc_cache<0>(particles, n_freq_x, u_x);
137 scycache = calc_sc_cache<1>(particles, n_freq_y, u_y);
138 return {n_freq_x, n_freq_y};
146 for (std::size_t i = 0; i < size; i++)
150static void copy_vec(
double *pdc_d,
double const *pdc_s, std::size_t size) {
151 for (std::size_t i = 0; i < size; i++)
155static void add_vec(
double *pdc_d,
double const *pdc_s1,
double const *pdc_s2,
157 for (std::size_t i = 0; i < size; i++)
158 pdc_d[i] = pdc_s1[i] + pdc_s2[i];
161static void addscale_vec(
double *pdc_d,
double scale,
double const *pdc_s1,
162 double const *pdc_s2, std::size_t size) {
163 for (std::size_t i = 0; i < size; i++)
164 pdc_d[i] = scale * pdc_s1[i] + pdc_s2[i];
167static void scale_vec(
double scale,
double *pdc, std::size_t size) {
168 for (std::size_t i = 0; i < size; i++)
172static double *
block(
double *p, std::size_t index, std::size_t size) {
173 return &p[index * size];
180 boost::mpi::all_reduce(
comm_cart, send_buf,
static_cast<int>(size),
gblcblk,
184void ElectrostaticLayerCorrection::check_gap(
Particle const &p)
const {
186 auto const z = p.
pos()[2];
189 <<
"region by " << ((z < 0.) ? z : z -
elc.box_h);
201void ElectrostaticLayerCorrection::add_dipole_force(
203 constexpr std::size_t size = 3;
205 auto const pref =
prefactor * 4. * std::numbers::pi / box_geo.volume();
209 auto const shift = box_geo.length_half()[2];
219 auto const q = p.
q();
220 auto const z = p.
pos()[2];
249 auto const field_induced =
gblcblk[1];
251 field_tot -= field_applied + field_induced;
255 p.
force()[2] -= field_tot * p.
q();
267double ElectrostaticLayerCorrection::dipole_energy(
269 constexpr std::size_t size = 7;
271 auto const pref =
prefactor * 2. * std::numbers::pi / box_geo.volume();
272 auto const lz = box_geo.length()[2];
275 auto const shift = box_geo.length_half()[2];
289 auto const q = p.
q();
290 auto const z = p.
pos()[2];
319 energy += 2. * pref *
355 double b(
double q,
double z)
const {
360 double t(
double q,
double z)
const {
366ElectrostaticLayerCorrection::z_energy(
ParticleRange const &particles)
const {
367 constexpr std::size_t size = 4;
369 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
370 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
374 auto const shift = box_geo.length_half()[2];
375 auto const lz = box_geo.length()[2];
382 auto const z = p.
pos()[2];
383 auto const q = p.
q();
400 auto const fac_delta = delta / (1. - delta);
405 auto const z = p.
pos()[2];
406 auto const q = p.
q();
439 return (
this_node == 0) ? -pref * energy : 0.;
442void ElectrostaticLayerCorrection::add_z_force(
444 constexpr std::size_t size = 1;
446 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
447 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
455 auto const z = p.
pos()[2];
456 auto const q = p.
q();
467 auto const fac_delta = delta / (1. - delta);
470 auto const z = p.
pos()[2];
471 auto const q = p.
q();
508 constexpr std::size_t size = 4;
510 auto const pref_di = prefactor * 4. * std::numbers::pi * xy_area_inv;
511 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
512 double lclimgebot[4], lclimgetop[4], lclimge[4];
513 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
517 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
528 auto const o = (index - 1) * particles.
size();
529 for (
auto const &p : particles) {
530 auto const z = p.
pos()[2];
531 auto const q = p.
q();
532 auto e = exp(omega * z);
549 lclimgebot[
POQESM] = sc_cache[o + ic].s / e;
550 lclimgebot[
POQESP] = sc_cache[o + ic].s * e;
551 lclimgebot[
POQECM] = sc_cache[o + ic].c / e;
552 lclimgebot[
POQECP] = sc_cache[o + ic].c * e;
557 exp(omega * (+z - 2. * elc.
box_h))) *
560 e = (exp(-omega * z) +
565 lclimge[
POQESP] += q * sc_cache[o + ic].s * e;
566 lclimge[
POQECP] += q * sc_cache[o + ic].c * e;
569 e = exp(omega * (2. * elc.
box_h - z));
573 lclimgetop[
POQESM] = sc_cache[o + ic].s / e;
574 lclimgetop[
POQESP] = sc_cache[o + ic].s * e;
575 lclimgetop[
POQECM] = sc_cache[o + ic].c / e;
576 lclimgetop[
POQECP] = sc_cache[o + ic].c * e;
581 exp(omega * (-z - 2. * elc.
box_h))) *
584 e = (exp(omega * (+z - 2. * elc.
box_h)) +
589 lclimge[
POQESM] += q * sc_cache[o + ic].s * e;
590 lclimge[
POQECM] += q * sc_cache[o + ic].c * e;
605 constexpr auto i =
static_cast<int>(axis);
606 constexpr std::size_t size = 4;
609 for (
auto &p : particles) {
610 auto &force = p.
force();
624 constexpr std::size_t size = 4;
627 for (std::size_t ic = 0; ic < n_part; ic++) {
634 return energy / omega;
645 std::size_t index_q,
double omega,
648 assert(index_p >= 1);
649 assert(index_q >= 1);
650 constexpr std::size_t size = 8;
652 auto const pref_di = prefactor * 8. * std::numbers::pi * xy_area_inv;
653 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
654 double lclimgebot[8], lclimgetop[8], lclimge[8];
655 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
658 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
668 auto const ox = (index_p - 1) * particles.
size();
669 auto const oy = (index_q - 1) * particles.
size();
670 for (
auto const &p : particles) {
671 auto const z = p.
pos()[2];
672 auto const q = p.
q();
673 auto e = exp(omega * z);
715 exp(omega * (+z - 2. * elc.
box_h))) *
720 e = (exp(-omega * z) +
722 fac_delta_mid_bot * q;
732 e = exp(omega * (2. * elc.
box_h - z));
748 exp(omega * (-z - 2. * elc.
box_h))) *
753 e = (exp(omega * (+z - 2. * elc.
box_h)) +
755 fac_delta_mid_top * q;
774static void add_PQ_force(std::size_t index_p, std::size_t index_q,
double omega,
777 auto constexpr c_2pi = 2. * std::numbers::pi;
779 c_2pi * box_geo.
length_inv()[0] *
static_cast<double>(index_p) / omega;
781 c_2pi * box_geo.
length_inv()[1] *
static_cast<double>(index_q) / omega;
782 constexpr std::size_t size = 8;
785 for (
auto &p : particles) {
786 auto &force = p.
force();
815static double PQ_energy(
double omega, std::size_t n_part) {
816 constexpr std::size_t size = 8;
819 for (std::size_t ic = 0; ic < n_part; ic++) {
829 return energy / omega;
833void ElectrostaticLayerCorrection::add_force(
835 auto constexpr c_2pi = 2. * std::numbers::pi;
838 auto const n_scxcache = std::get<0>(n_freqs);
839 auto const n_scycache = std::get<1>(n_freqs);
842 add_dipole_force(particles);
843 add_z_force(particles);
846 for (std::size_t p = 1;
847 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
850 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
851 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
853 add_PoQ_force<PoQ::P>(particles);
856 for (std::size_t q = 1;
857 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
860 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
861 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
863 add_PoQ_force<PoQ::Q>(particles);
866 for (std::size_t p = 1;
867 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
870 for (std::size_t q = 1;
871 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
873 static_cast<double>(q - 1)) <
879 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
880 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
888double ElectrostaticLayerCorrection::calc_energy(
890 auto constexpr c_2pi = 2. * std::numbers::pi;
892 auto energy = dipole_energy(particles) + z_energy(particles);
894 auto const n_scxcache = std::get<0>(n_freqs);
895 auto const n_scycache = std::get<1>(n_freqs);
898 partblk.resize(n_localpart * 8);
901 for (std::size_t p = 1;
902 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
905 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
906 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
911 for (std::size_t q = 1;
912 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
915 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
916 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
921 for (std::size_t p = 1;
922 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
925 for (std::size_t q = 1;
926 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
928 static_cast<double>(q - 1)) <
934 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
935 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
945double ElectrostaticLayerCorrection::tune_far_cut()
const {
947 auto constexpr maximal_far_cut = 50.;
949 auto const box_l_x_inv = box_geo.length_inv()[0];
950 auto const box_l_y_inv = box_geo.length_inv()[1];
951 auto const min_inv_boxl = std::min(box_l_x_inv, box_l_y_inv);
952 auto const box_l_z = box_geo.length()[2];
957 auto tuned_far_cut = min_inv_boxl;
960 auto const pref = 2. * std::numbers::pi * tuned_far_cut;
961 auto const sum = pref + 2. * (box_l_x_inv + box_l_y_inv);
962 auto const den = -expm1(-pref * lz);
963 auto const num1 = exp(pref * (
elc.
box_h - lz));
964 auto const num2 = exp(-pref * (
elc.
box_h + lz));
970 tuned_far_cut += min_inv_boxl;
971 }
while (err >
elc.
maxPWerror and tuned_far_cut < maximal_far_cut);
972 if (tuned_far_cut >= maximal_far_cut) {
973 throw std::runtime_error(
"ELC tuning failed: maxPWerror too small");
975 return tuned_far_cut - min_inv_boxl;
983 return boost::mpi::all_reduce(
comm_cart, local_q, std::plus<>());
986void ElectrostaticLayerCorrection::sanity_checks_periodicity()
const {
988 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
989 throw std::runtime_error(
"ELC: requires periodicity (True, True, True)");
993void ElectrostaticLayerCorrection::sanity_checks_dielectric_contrasts()
const {
995 auto const &cell_structure = *
get_system().cell_structure;
998 if (total_charge >= precision_threshold) {
1002 throw std::runtime_error(
"ELC does not currently support non-neutral "
1003 "systems with a dielectric contrast.");
1007 throw std::runtime_error(
"ELC does not work for non-neutral systems and "
1008 "non-metallic dielectric contrast.");
1013void ElectrostaticLayerCorrection::adapt_solver() {
1015 [
this](
auto &solver) {
1017 solver->adapt_epsilon_elc();
1023void ElectrostaticLayerCorrection::recalc_box_h() {
1027 if (new_box_h < 0.) {
1028 throw std::runtime_error(
"ELC gap size (" + std::to_string(
elc.
gap_size) +
1029 ") larger than box length in z-direction (" +
1030 std::to_string(box_z) +
")");
1035void ElectrostaticLayerCorrection::recalc_space_layer() {
1037 auto const p3m_r_cut = std::visit(
1038 [](
auto &solver) {
return solver->p3m_params.r_cut; },
base_solver);
1046 auto const half_box_h =
elc.
box_h / 2.;
1047 auto const max_space_layer = std::min(free_space, half_box_h);
1049 if (max_space_layer <= 0.) {
1050 throw std::runtime_error(
"P3M real-space cutoff too large for ELC w/ "
1051 "dielectric contrast");
1060 bool neutralize,
double delta_top,
double delta_bot,
1061 bool with_const_pot,
double potential_diff)
1062 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
1063 far_cut2{-1.}, far_calculated{far_cut == -1.},
1064 dielectric_contrast_on{delta_top != 0. or delta_bot != 0.},
1065 const_pot{with_const_pot and dielectric_contrast_on},
1066 neutralize{neutralize and !dielectric_contrast_on},
1067 delta_mid_top{std::clamp(delta_top, -1., +1.)},
1068 delta_mid_bot{std::clamp(delta_bot, -1., +1.)},
1069 pot_diff{(with_const_pot) ? potential_diff : 0.},
1072 space_layer{(dielectric_contrast_on) ? gap_size / 3. : 0.},
1073 space_box{gap_size - ((dielectric_contrast_on) ? 2. * space_layer : 0.)} {
1077 throw std::domain_error(
"Parameter 'far_cut' must be > 0");
1080 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
1083 throw std::domain_error(
"Parameter 'gap_size' must be > 0");
1085 if (potential_diff != 0. and not with_const_pot) {
1086 throw std::invalid_argument(
1087 "Parameter 'const_pot' must be True when 'pot_diff' is non-zero");
1089 if (delta_top < -delta_range or delta_top > delta_range) {
1090 throw std::domain_error(
1091 "Parameter 'delta_mid_top' must be >= -1 and <= +1");
1093 if (delta_bot < -delta_range or delta_bot > delta_range) {
1094 throw std::domain_error(
1095 "Parameter 'delta_mid_bot' must be >= -1 and <= +1");
1102 throw std::domain_error(
"ELC with two parallel metallic boundaries "
1103 "requires the const_pot option");
1109 : elc{parameters}, base_solver{solver} {
1113template <ChargeProtocol protocol,
typename combined_ranges>
1115 combined_ranges
const &p_q_pos_range) {
1120 for (
auto zipped : p_q_pos_range) {
1121 auto const p_q = boost::get<0>(zipped);
1122 auto const &p_pos = boost::get<1>(zipped);
1134 solver.
assign_charge(q_eff, {p_pos[0], p_pos[1], -p_pos[2]},
true);
1139 q_eff, {p_pos[0], p_pos[1], 2. * elc.
box_h - p_pos[2]},
true);
1146template <ChargeProtocol protocol,
typename combined_range>
1148 combined_range
const &p_q_pos_range) {
1151 auto local_q2 = 0.0;
1153 for (
auto zipped : p_q_pos_range) {
1154 auto const p_q = boost::get<0>(zipped);
1155 auto const &p_pos = boost::get<1>(zipped);
1157 auto const p_z = p_pos[2];
1184 auto global_q2 = 0.;
1186 boost::mpi::all_reduce(
comm_cart, local_n, global_n, std::plus<>());
1187 boost::mpi::all_reduce(
comm_cart, local_q2, global_q2, std::plus<>());
1188 boost::mpi::all_reduce(
comm_cart, local_q, global_q, std::plus<>());
1194 auto const energy = std::visit(
1195 [
this, &particles](
auto const &solver_ptr) {
1196 auto &solver = *solver_ptr;
1201 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1204 solver.charge_assign(particles);
1207 return solver.long_range_energy(particles);
1211 energy += 0.5 * solver.long_range_energy(particles);
1216 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1217 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1218 energy += 0.5 * solver.long_range_energy(particles);
1221 charge_assign<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1222 modify_p3m_sums<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1223 energy -= 0.5 * solver.long_range_energy(particles);
1226 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1231 return energy + calc_energy(particles);
1237 [
this, &particles](
auto const &solver_ptr) {
1238 auto &solver = *solver_ptr;
1241 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1244 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1245 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1248 solver.charge_assign(particles);
1250 solver.add_long_range_forces(particles);
1252 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1256 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.
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.
This file contains the defaults for ESPResSo.
#define ROUND_ERROR_PREC
Precision for capture of round off errors.
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.
Vector< T, N > sqrt(Vector< T, N > const &a)
auto constexpr P3M_EPSILON_METALLIC
This value indicates metallic boundary conditions.
P3M algorithm for long-range Coulomb interaction.
Describes a cell structure / cell system.
ParticleRange local_particles() const
virtual void count_charged_particles_elc(int, double, double)=0
virtual void prepare_fft_mesh(bool reset_weights)=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.