37#include "communication.hpp"
39#include "system/System.hpp"
43#include <Kokkos_Core.hpp>
45#include <boost/mpi/collectives/all_reduce.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 std::ranges::fill_n(pdc,
static_cast<std::ptrdiff_t
>(size), 0.);
152static void add_vec(
double *pdc_d,
double const *pdc_s1,
double const *pdc_s2,
154 for (std::size_t i = 0; i < size; i++)
155 pdc_d[i] = pdc_s1[i] + pdc_s2[i];
158static void addscale_vec(
double *pdc_d,
double scale,
double const *pdc_s1,
159 double const *pdc_s2, std::size_t size) {
160 for (std::size_t i = 0; i < size; i++)
161 pdc_d[i] = scale * pdc_s1[i] + pdc_s2[i];
164static void scale_vec(
double scale,
double *pdc, std::size_t size) {
165 for (std::size_t i = 0; i < size; i++)
169static double *
block(
double *p, std::size_t index, std::size_t size) {
170 return &p[index * size];
176 std::ranges::copy_n(
gblcblk,
static_cast<std::ptrdiff_t
>(size), send_buf);
177 boost::mpi::all_reduce(
comm_cart, send_buf,
static_cast<int>(size),
gblcblk,
181void ElectrostaticLayerCorrection::check_gap(
Particle const &p)
const {
183 auto const z = p.
pos()[2];
186 <<
"region by " << ((z < 0.) ? z : z -
elc.box_h);
198void ElectrostaticLayerCorrection::add_dipole_force()
const {
199 constexpr std::size_t size = 3;
201 auto const &box_geo = *system.box_geo;
202 auto const particles = system.cell_structure->local_particles();
203 auto const pref =
prefactor * 4. * std::numbers::pi / box_geo.volume();
207 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()
const {
268 constexpr std::size_t size = 7;
270 auto const &box_geo = *system.box_geo;
271 auto const particles = system.cell_structure->local_particles();
272 auto const pref =
prefactor * 2. * std::numbers::pi / box_geo.volume();
273 auto const lz = box_geo.length()[2];
276 auto const shift = box_geo.length_half()[2];
290 auto const q = p.
q();
291 auto const z = p.
pos()[2];
320 energy += 2. * pref *
357 double b(
double q,
double z)
const {
362 double t(
double q,
double z)
const {
367double ElectrostaticLayerCorrection::z_energy()
const {
368 constexpr std::size_t size = 4;
370 auto const &box_geo = *system.box_geo;
371 auto const particles = system.cell_structure->local_particles();
372 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
373 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
377 auto const shift = box_geo.length_half()[2];
384 auto const z = p.
pos()[2];
385 auto const q = p.
q();
402 auto const fac_delta = delta / (1. - delta);
405 ImageSum const image_sum{delta, shift, h};
407 auto const z = p.
pos()[2];
408 auto const q = p.
q();
416 image_sum.b(delta, -(2. * h - z)));
420 image_sum.b(delta, -(2. * h - z)));
427 image_sum.t(delta, 2. * h + z));
432 image_sum.t(delta, 2. * h + z));
441 return (
this_node == 0) ? -pref * energy : 0.;
444void ElectrostaticLayerCorrection::add_z_force()
const {
445 constexpr std::size_t size = 1;
447 auto const &box_geo = *system.box_geo;
448 auto const particles = system.cell_structure->local_particles();
449 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
450 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
458 auto const z = p.
pos()[2];
459 auto const q = p.
q();
470 auto const fac_delta = delta / (1. - delta);
473 auto const z = p.
pos()[2];
474 auto const q = p.
q();
511 constexpr std::size_t size = 4;
513 auto const pref_di = prefactor * 4. * std::numbers::pi * xy_area_inv;
514 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
515 double lclimgebot[4], lclimgetop[4], lclimge[4];
516 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
520 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
531 auto const o = (index - 1) * particles.
size();
532 for (
auto const &p : particles) {
533 auto const z = p.
pos()[2];
534 auto const q = p.
q();
535 auto e = exp(omega * z);
552 lclimgebot[
POQESM] = sc_cache[o + ic].s / e;
553 lclimgebot[
POQESP] = sc_cache[o + ic].s * e;
554 lclimgebot[
POQECM] = sc_cache[o + ic].c / e;
555 lclimgebot[
POQECP] = sc_cache[o + ic].c * e;
560 exp(omega * (+z - 2. * elc.
box_h))) *
563 e = (exp(-omega * z) +
568 lclimge[
POQESP] += q * sc_cache[o + ic].s * e;
569 lclimge[
POQECP] += q * sc_cache[o + ic].c * e;
572 e = exp(omega * (2. * elc.
box_h - z));
576 lclimgetop[
POQESM] = sc_cache[o + ic].s / e;
577 lclimgetop[
POQESP] = sc_cache[o + ic].s * e;
578 lclimgetop[
POQECM] = sc_cache[o + ic].c / e;
579 lclimgetop[
POQECP] = sc_cache[o + ic].c * e;
584 exp(omega * (-z - 2. * elc.
box_h))) *
587 e = (exp(omega * (+z - 2. * elc.
box_h)) +
592 lclimge[
POQESM] += q * sc_cache[o + ic].s * e;
593 lclimge[
POQECM] += q * sc_cache[o + ic].c * e;
608 constexpr auto i =
static_cast<int>(axis);
609 constexpr std::size_t size = 4;
612 for (
auto &p : particles) {
613 auto &force = p.
force();
627 constexpr std::size_t size = 4;
630 for (std::size_t ic = 0; ic < n_part; ic++) {
637 return energy / omega;
648 std::size_t index_q,
double omega,
651 assert(index_p >= 1);
652 assert(index_q >= 1);
653 constexpr std::size_t size = 8;
655 auto const pref_di = prefactor * 8. * std::numbers::pi * xy_area_inv;
656 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
657 double lclimgebot[8], lclimgetop[8], lclimge[8];
658 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
661 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
671 auto const ox = (index_p - 1) * particles.
size();
672 auto const oy = (index_q - 1) * particles.
size();
673 for (
auto const &p : particles) {
674 auto const z = p.
pos()[2];
675 auto const q = p.
q();
676 auto e = exp(omega * z);
718 exp(omega * (+z - 2. * elc.
box_h))) *
723 e = (exp(-omega * z) +
725 fac_delta_mid_bot * q;
735 e = exp(omega * (2. * elc.
box_h - z));
751 exp(omega * (-z - 2. * elc.
box_h))) *
756 e = (exp(omega * (+z - 2. * elc.
box_h)) +
758 fac_delta_mid_top * q;
777static void add_PQ_force(std::size_t index_p, std::size_t index_q,
double omega,
780 auto constexpr c_2pi = 2. * std::numbers::pi;
782 c_2pi * box_geo.
length_inv()[0] *
static_cast<double>(index_p) / omega;
784 c_2pi * box_geo.
length_inv()[1] *
static_cast<double>(index_q) / omega;
785 constexpr std::size_t size = 8;
788 for (
auto &p : particles) {
789 auto &force = p.
force();
818static double PQ_energy(
double omega, std::size_t n_part) {
819 constexpr std::size_t size = 8;
822 for (std::size_t ic = 0; ic < n_part; ic++) {
832 return energy / omega;
836void ElectrostaticLayerCorrection::add_force()
const {
837 auto constexpr c_2pi = 2. * std::numbers::pi;
839 auto const &box_geo = *system.box_geo;
840 auto const particles = system.cell_structure->local_particles();
842 auto const n_scxcache = std::get<0>(n_freqs);
843 auto const n_scycache = std::get<1>(n_freqs);
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()
const {
893 auto constexpr c_2pi = 2. * std::numbers::pi;
895 auto const &box_geo = *system.box_geo;
896 auto const particles = system.cell_structure->local_particles();
897 auto energy = dipole_energy() + z_energy();
899 auto const n_scxcache = std::get<0>(n_freqs);
900 auto const n_scycache = std::get<1>(n_freqs);
903 partblk.resize(n_localpart * 8);
906 for (std::size_t p = 1;
907 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
910 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
911 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
916 for (std::size_t q = 1;
917 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
920 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
921 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
926 for (std::size_t p = 1;
927 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
930 for (std::size_t q = 1;
931 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
933 static_cast<double>(q - 1)) <
939 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
940 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
950double ElectrostaticLayerCorrection::tune_far_cut()
const {
952 auto constexpr maximal_far_cut = 50.;
954 auto const box_l_x_inv = box_geo.length_inv()[0];
955 auto const box_l_y_inv = box_geo.length_inv()[1];
956 auto const min_inv_boxl = std::min(box_l_x_inv, box_l_y_inv);
957 auto const box_l_z = box_geo.length()[2];
962 auto tuned_far_cut = min_inv_boxl;
966 auto const pref = 2. * std::numbers::pi * tuned_far_cut;
967 auto const sum = pref + 2. * (box_l_x_inv + box_l_y_inv);
968 auto const den = expm1(pref * lz);
969 auto const num1 = exp(pref * h);
970 auto const num2 = 1. / num1;
973 (num1 / (lz - h) * (sum + 1. / (lz - h)) +
974 num2 / (lz + h) * (sum + 1. / (lz + h)));
976 tuned_far_cut += min_inv_boxl;
977 }
while (err >
elc.
maxPWerror and tuned_far_cut < maximal_far_cut);
978 if (tuned_far_cut >= maximal_far_cut) {
979 throw std::runtime_error(
"ELC tuning failed: maxPWerror too small");
981 return tuned_far_cut - min_inv_boxl;
984void ElectrostaticLayerCorrection::sanity_checks_periodicity()
const {
986 if (!box_geo.periodic(0) || !box_geo.periodic(1) || !box_geo.periodic(2)) {
987 throw std::runtime_error(
"ELC: requires periodicity (True, True, True)");
991void ElectrostaticLayerCorrection::adapt_solver() {
993 [
this](
auto &solver) {
995 solver->adapt_epsilon_elc();
1001void ElectrostaticLayerCorrection::recalc_box_h() {
1005 if (new_box_h < 0.) {
1006 throw std::runtime_error(
"ELC gap size (" + std::to_string(
elc.
gap_size) +
1007 ") larger than box length in z-direction (" +
1008 std::to_string(box_z) +
")");
1013void ElectrostaticLayerCorrection::recalc_space_layer() {
1015 auto const p3m_r_cut = std::visit(
1016 [](
auto &solver) {
return solver->p3m_params.r_cut; },
base_solver);
1024 auto const half_box_h =
elc.
box_h / 2.;
1025 auto const max_space_layer = std::min(free_space, half_box_h);
1027 if (max_space_layer <= 0.) {
1028 throw std::runtime_error(
"P3M real-space cutoff too large for ELC w/ "
1029 "dielectric contrast");
1038 bool neutralize,
double delta_top,
double delta_bot,
1039 bool with_const_pot,
double potential_diff)
1040 : maxPWerror{maxPWerror}, gap_size{gap_size}, box_h{-1.}, far_cut{far_cut},
1041 far_cut2{-1.}, far_calculated{far_cut == -1.},
1042 dielectric_contrast_on{delta_top != 0. or delta_bot != 0.},
1043 const_pot{with_const_pot and dielectric_contrast_on},
1044 neutralize{neutralize and !dielectric_contrast_on},
1045 delta_mid_top{
std::clamp(delta_top, -1., +1.)},
1046 delta_mid_bot{
std::clamp(delta_bot, -1., +1.)},
1047 pot_diff{(with_const_pot) ? potential_diff : 0.},
1050 space_layer{(dielectric_contrast_on) ? gap_size / 3. : 0.},
1051 space_box{gap_size - ((dielectric_contrast_on) ? 2. * space_layer : 0.)} {
1055 throw std::domain_error(
"Parameter 'far_cut' must be > 0");
1058 throw std::domain_error(
"Parameter 'maxPWerror' must be > 0");
1061 throw std::domain_error(
"Parameter 'gap_size' must be > 0");
1063 if (potential_diff != 0. and not with_const_pot) {
1064 throw std::invalid_argument(
1065 "Parameter 'const_pot' must be True when 'pot_diff' is non-zero");
1068 throw std::invalid_argument(
1069 "Parameter 'const_pot' requires a dielectric contrast; set "
1070 "'delta_mid_top' and 'delta_mid_bot' (use -1 for metallic walls)");
1072 if (delta_top < -delta_range or delta_top > delta_range) {
1073 throw std::domain_error(
1074 "Parameter 'delta_mid_top' must be >= -1 and <= +1");
1076 if (delta_bot < -delta_range or delta_bot > delta_range) {
1077 throw std::domain_error(
1078 "Parameter 'delta_mid_bot' must be >= -1 and <= +1");
1085 throw std::domain_error(
"ELC with two parallel metallic boundaries "
1086 "requires the const_pot option");
1092 : elc{parameters}, base_solver{solver} {
1096 std::visit([](
auto const &solver_ptr) {
return solver_ptr->is_gpu(); },
1099 throw std::runtime_error(
1100 "ELC with a dielectric contrast is not supported by the GPU "
1107template <ChargeProtocol protocol>
1114 auto constexpr include_neutral_particles =
true;
1115 auto const &aosoa = cs.get_aosoa();
1116 auto const n_part = cs.count_local_particles();
1118 for (std::size_t p_index = 0; p_index < n_part; ++p_index) {
1119 auto const p_q = aosoa.charge(p_index);
1120 auto const p_pos = aosoa.get_span_at(aosoa.position, p_index);
1121 if (include_neutral_particles or p_q != 0.) {
1125 solver.
assign_charge(p_q, {p_pos[0], p_pos[1], p_pos[2]},
false);
1132 solver.
assign_charge(q_eff, {p_pos[0], p_pos[1], -p_pos[2]},
true);
1137 q_eff, {p_pos[0], p_pos[1], 2. * elc.
box_h - p_pos[2]},
true);
1144template <ChargeProtocol protocol>
1147 auto const &aosoa = cs.get_aosoa();
1148 auto const n_part = cs.count_local_particles();
1149 auto local_n = std::size_t{0u};
1150 auto local_q2 = 0.0;
1152 for (std::size_t p_index = 0; p_index < n_part; ++p_index) {
1153 auto const p_q = aosoa.charge(p_index);
1155 auto const p_z = aosoa.position(p_index, 2ul);
1181 auto global_n = std::size_t{0u};
1182 auto global_q2 = 0.;
1184 boost::mpi::all_reduce(
comm_cart, local_n, global_n, std::plus<>());
1185 boost::mpi::all_reduce(
comm_cart, local_q2, global_q2, std::plus<>());
1186 boost::mpi::all_reduce(
comm_cart, local_q, global_q, std::plus<>());
1192 auto const energy = std::visit(
1193 [
this, &system](
auto const &solver_ptr) {
1194 auto &solver = *solver_ptr;
1195 auto const &cs = *system.cell_structure;
1196 auto const &box_geo = *system.box_geo;
1197 auto const particles = cs.local_particles();
1200 solver.charge_assign();
1203 return solver.long_range_energy();
1207 energy += 0.5 * solver.long_range_energy();
1212 charge_assign<ChargeProtocol::BOTH>(
elc, solver, cs);
1213 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, cs);
1214 energy += 0.5 * solver.long_range_energy();
1217 charge_assign<ChargeProtocol::IMAGE>(
elc, solver, cs);
1218 modify_p3m_sums<ChargeProtocol::IMAGE>(
elc, solver, cs);
1219 energy -= 0.5 * solver.long_range_energy();
1222 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, cs);
1227 return energy + calc_energy();
1233 [
this, &system](
auto const &solver_ptr) {
1234 auto const &cs = *system.cell_structure;
1235 auto &solver = *solver_ptr;
1237 auto const &box_geo = *system.box_geo;
1238 auto const particles = cs.local_particles();
1239 modify_p3m_sums<ChargeProtocol::BOTH>(elc, solver, cs);
1240 charge_assign<ChargeProtocol::BOTH>(elc, solver, cs);
1241 elc.dielectric_layers_self_forces(solver, box_geo, particles);
1243 solver.charge_assign();
1245 solver.add_long_range_forces();
1247 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, cs);
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.
constexpr auto round_error_prec
Precision below which a double-precision float is assumed to be zero.
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 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)
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)
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 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.
void modify_p3m_sums(elc_data const &elc, CoulombP3M &solver, auto const &cs)
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
void charge_assign(elc_data const &elc, CoulombP3M &solver, auto const &cs)
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)
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 h)
double t(double q, double z) const
Image sum from the top layer.
Struct holding all information for one particle.
constexpr auto const & pos() const
constexpr auto const & force() const
constexpr auto const & id() const
constexpr auto const & q() 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.
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.