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()
const {
206 constexpr std::size_t size = 3;
208 auto const &box_geo = *system.box_geo;
209 auto const particles = system.cell_structure->local_particles();
210 auto const pref =
prefactor * 4. * std::numbers::pi / box_geo.volume();
214 auto const shift = box_geo.length_half()[2];
224 auto const q = p.
q();
225 auto const z = p.
pos()[2];
254 auto const field_induced =
gblcblk[1];
256 field_tot -= field_applied + field_induced;
260 p.
force()[2] -= field_tot * p.
q();
272double ElectrostaticLayerCorrection::dipole_energy()
const {
273 constexpr std::size_t size = 7;
275 auto const &box_geo = *system.box_geo;
276 auto const particles = system.cell_structure->local_particles();
277 auto const pref =
prefactor * 2. * std::numbers::pi / box_geo.volume();
278 auto const lz = box_geo.length()[2];
281 auto const shift = box_geo.length_half()[2];
295 auto const q = p.
q();
296 auto const z = p.
pos()[2];
325 energy += 2. * pref *
361 double b(
double q,
double z)
const {
366 double t(
double q,
double z)
const {
371double ElectrostaticLayerCorrection::z_energy()
const {
372 constexpr std::size_t size = 4;
374 auto const &box_geo = *system.box_geo;
375 auto const particles = system.cell_structure->local_particles();
376 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
377 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
381 auto const shift = box_geo.length_half()[2];
382 auto const lz = box_geo.length()[2];
389 auto const z = p.
pos()[2];
390 auto const q = p.
q();
407 auto const fac_delta = delta / (1. - delta);
412 auto const z = p.
pos()[2];
413 auto const q = p.
q();
446 return (
this_node == 0) ? -pref * energy : 0.;
449void ElectrostaticLayerCorrection::add_z_force()
const {
450 constexpr std::size_t size = 1;
452 auto const &box_geo = *system.box_geo;
453 auto const particles = system.cell_structure->local_particles();
454 auto const xy_area_inv = box_geo.length_inv()[0] * box_geo.length_inv()[1];
455 auto const pref =
prefactor * 2. * std::numbers::pi * xy_area_inv;
463 auto const z = p.
pos()[2];
464 auto const q = p.
q();
475 auto const fac_delta = delta / (1. - delta);
478 auto const z = p.
pos()[2];
479 auto const q = p.
q();
516 constexpr std::size_t size = 4;
518 auto const pref_di = prefactor * 4. * std::numbers::pi * xy_area_inv;
519 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
520 double lclimgebot[4], lclimgetop[4], lclimge[4];
521 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
525 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
536 auto const o = (index - 1) * particles.
size();
537 for (
auto const &p : particles) {
538 auto const z = p.
pos()[2];
539 auto const q = p.
q();
540 auto e = exp(omega * z);
557 lclimgebot[
POQESM] = sc_cache[o + ic].s / e;
558 lclimgebot[
POQESP] = sc_cache[o + ic].s * e;
559 lclimgebot[
POQECM] = sc_cache[o + ic].c / e;
560 lclimgebot[
POQECP] = sc_cache[o + ic].c * e;
565 exp(omega * (+z - 2. * elc.
box_h))) *
568 e = (exp(-omega * z) +
573 lclimge[
POQESP] += q * sc_cache[o + ic].s * e;
574 lclimge[
POQECP] += q * sc_cache[o + ic].c * e;
577 e = exp(omega * (2. * elc.
box_h - z));
581 lclimgetop[
POQESM] = sc_cache[o + ic].s / e;
582 lclimgetop[
POQESP] = sc_cache[o + ic].s * e;
583 lclimgetop[
POQECM] = sc_cache[o + ic].c / e;
584 lclimgetop[
POQECP] = sc_cache[o + ic].c * e;
589 exp(omega * (-z - 2. * elc.
box_h))) *
592 e = (exp(omega * (+z - 2. * elc.
box_h)) +
597 lclimge[
POQESM] += q * sc_cache[o + ic].s * e;
598 lclimge[
POQECM] += q * sc_cache[o + ic].c * e;
613 constexpr auto i =
static_cast<int>(axis);
614 constexpr std::size_t size = 4;
617 for (
auto &p : particles) {
618 auto &force = p.
force();
632 constexpr std::size_t size = 4;
635 for (std::size_t ic = 0; ic < n_part; ic++) {
642 return energy / omega;
653 std::size_t index_q,
double omega,
656 assert(index_p >= 1);
657 assert(index_q >= 1);
658 constexpr std::size_t size = 8;
660 auto const pref_di = prefactor * 8. * std::numbers::pi * xy_area_inv;
661 auto const pref = -pref_di / expm1(omega * box_geo.
length()[2]);
662 double lclimgebot[8], lclimgetop[8], lclimge[8];
663 double fac_delta_mid_bot = 1., fac_delta_mid_top = 1., fac_delta = 1.;
666 auto const fac_elc = 1. / (1. - delta * exp(-omega * 2. * elc.
box_h));
676 auto const ox = (index_p - 1) * particles.
size();
677 auto const oy = (index_q - 1) * particles.
size();
678 for (
auto const &p : particles) {
679 auto const z = p.
pos()[2];
680 auto const q = p.
q();
681 auto e = exp(omega * z);
723 exp(omega * (+z - 2. * elc.
box_h))) *
728 e = (exp(-omega * z) +
730 fac_delta_mid_bot * q;
740 e = exp(omega * (2. * elc.
box_h - z));
756 exp(omega * (-z - 2. * elc.
box_h))) *
761 e = (exp(omega * (+z - 2. * elc.
box_h)) +
763 fac_delta_mid_top * q;
782static void add_PQ_force(std::size_t index_p, std::size_t index_q,
double omega,
785 auto constexpr c_2pi = 2. * std::numbers::pi;
787 c_2pi * box_geo.
length_inv()[0] *
static_cast<double>(index_p) / omega;
789 c_2pi * box_geo.
length_inv()[1] *
static_cast<double>(index_q) / omega;
790 constexpr std::size_t size = 8;
793 for (
auto &p : particles) {
794 auto &force = p.
force();
823static double PQ_energy(
double omega, std::size_t n_part) {
824 constexpr std::size_t size = 8;
827 for (std::size_t ic = 0; ic < n_part; ic++) {
837 return energy / omega;
841void ElectrostaticLayerCorrection::add_force()
const {
842 auto constexpr c_2pi = 2. * std::numbers::pi;
844 auto const &box_geo = *system.box_geo;
845 auto const particles = system.cell_structure->local_particles();
847 auto const n_scxcache = std::get<0>(n_freqs);
848 auto const n_scycache = std::get<1>(n_freqs);
855 for (std::size_t p = 1;
856 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
859 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
860 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
862 add_PoQ_force<PoQ::P>(particles);
865 for (std::size_t q = 1;
866 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
869 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
870 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
872 add_PoQ_force<PoQ::Q>(particles);
875 for (std::size_t p = 1;
876 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
879 for (std::size_t q = 1;
880 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
882 static_cast<double>(q - 1)) <
888 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
889 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
897double ElectrostaticLayerCorrection::calc_energy()
const {
898 auto constexpr c_2pi = 2. * std::numbers::pi;
900 auto const &box_geo = *system.box_geo;
901 auto const particles = system.cell_structure->local_particles();
902 auto energy = dipole_energy() + z_energy();
904 auto const n_scxcache = std::get<0>(n_freqs);
905 auto const n_scycache = std::get<1>(n_freqs);
908 partblk.resize(n_localpart * 8);
911 for (std::size_t p = 1;
912 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
915 auto const omega = c_2pi * box_geo.length_inv()[0] *
static_cast<double>(p);
916 setup_PoQ<PoQ::P>(
elc,
prefactor, p, omega, particles, box_geo);
921 for (std::size_t q = 1;
922 box_geo.length_inv()[1] *
static_cast<double>(q - 1) <
elc.
far_cut &&
925 auto const omega = c_2pi * box_geo.length_inv()[1] *
static_cast<double>(q);
926 setup_PoQ<PoQ::Q>(
elc,
prefactor, q, omega, particles, box_geo);
931 for (std::size_t p = 1;
932 box_geo.length_inv()[0] *
static_cast<double>(p - 1) <
elc.
far_cut &&
935 for (std::size_t q = 1;
936 Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p - 1)) +
938 static_cast<double>(q - 1)) <
944 sqrt(
Utils::sqr(box_geo.length_inv()[0] *
static_cast<double>(p)) +
945 Utils::sqr(box_geo.length_inv()[1] *
static_cast<double>(q)));
955double ElectrostaticLayerCorrection::tune_far_cut()
const {
957 auto constexpr maximal_far_cut = 50.;
959 auto const box_l_x_inv = box_geo.length_inv()[0];
960 auto const box_l_y_inv = box_geo.length_inv()[1];
961 auto const min_inv_boxl = std::min(box_l_x_inv, box_l_y_inv);
962 auto const box_l_z = box_geo.length()[2];
967 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 * (
elc.
box_h - lz));
974 auto const num2 = exp(-pref * (
elc.
box_h + lz));
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) {
1130#ifdef ESPRESSO_SHARED_MEMORY_PARALLELISM
1132 auto const include_neutral_particles = Kokkos::num_threads() > 1;
1134 auto constexpr include_neutral_particles =
false;
1137 for (
auto zipped : p_q_pos_range) {
1138 auto const p_q = boost::get<0>(zipped);
1139 auto const &p_pos = boost::get<1>(zipped);
1140 if (include_neutral_particles or p_q != 0.) {
1151 solver.
assign_charge(q_eff, {p_pos[0], p_pos[1], -p_pos[2]},
true);
1156 q_eff, {p_pos[0], p_pos[1], 2. * elc.
box_h - p_pos[2]},
true);
1163template <ChargeProtocol protocol,
typename combined_range>
1165 combined_range
const &p_q_pos_range) {
1167 auto local_n = std::size_t{0u};
1168 auto local_q2 = 0.0;
1170 for (
auto zipped : p_q_pos_range) {
1171 auto const p_q = boost::get<0>(zipped);
1172 auto const &p_pos = boost::get<1>(zipped);
1174 auto const p_z = p_pos[2];
1200 auto global_n = std::size_t{0u};
1201 auto global_q2 = 0.;
1203 boost::mpi::all_reduce(
comm_cart, local_n, global_n, std::plus<>());
1204 boost::mpi::all_reduce(
comm_cart, local_q2, global_q2, std::plus<>());
1205 boost::mpi::all_reduce(
comm_cart, local_q, global_q, std::plus<>());
1211 auto const energy = std::visit(
1212 [
this, &system](
auto const &solver_ptr) {
1213 auto &solver = *solver_ptr;
1214 auto const particles = system.cell_structure->local_particles();
1215 auto const &box_geo = *system.box_geo;
1219 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1222 solver.charge_assign();
1225 return solver.long_range_energy();
1229 energy += 0.5 * solver.long_range_energy();
1234 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1235 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1236 energy += 0.5 * solver.long_range_energy();
1239 charge_assign<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1240 modify_p3m_sums<ChargeProtocol::IMAGE>(
elc, solver, p_q_pos_range);
1241 energy -= 0.5 * solver.long_range_energy();
1244 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
1249 return energy + calc_energy();
1255 [
this, &system](
auto const &solver_ptr) {
1256 auto const particles = system.cell_structure->local_particles();
1257 auto &solver = *solver_ptr;
1260 auto p_q_pos_range = boost::combine(p_q_range, p_pos_range);
1262 auto const &box_geo = *system.box_geo;
1263 modify_p3m_sums<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1264 charge_assign<ChargeProtocol::BOTH>(
elc, solver, p_q_pos_range);
1267 solver.charge_assign();
1269 solver.add_long_range_forces();
1271 modify_p3m_sums<ChargeProtocol::REAL>(
elc, solver, p_q_pos_range);
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.
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.