ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
coulomb.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "config/config.hpp"
21
23
24#ifdef ESPRESSO_ELECTROSTATICS
25
27
28#include "ParticleRange.hpp"
30#include "actor/visitors.hpp"
32#include "communication.hpp"
34#include "errorhandling.hpp"
35#include "system/System.hpp"
36
37#include <utils/Vector.hpp>
38#include <utils/demangle.hpp>
39
40#include <boost/accumulators/accumulators.hpp>
41#include <boost/accumulators/statistics/sum_kahan.hpp>
42#include <boost/mpi/collectives/broadcast.hpp>
43#include <boost/mpi/collectives/gather.hpp>
44
45#include <algorithm>
46#include <cassert>
47#include <cmath>
48#include <cstdio>
49#include <iomanip>
50#include <limits>
51#include <memory>
52#include <optional>
53#include <sstream>
54#include <stdexcept>
55#include <variant>
56#include <vector>
57
58namespace Coulomb {
59
61 impl = std::make_unique<Implementation>();
63}
64
66
68 if (impl->solver) {
69 std::visit([](auto const &ptr) { ptr->sanity_checks(); }, *impl->solver);
70 }
71}
72
75 if (impl->solver) {
76 visit_try_catch([](auto &ptr) { ptr->init(); }, *impl->solver);
77 }
78}
79
81 if (impl->solver) {
82 visit_try_catch([](auto &ptr) { ptr->on_boxl_change(); }, *impl->solver);
83 }
84}
85
87 if (impl->solver) {
88 std::visit([](auto &ptr) { ptr->on_node_grid_change(); }, *impl->solver);
89 }
90}
91
93 if (impl->solver) {
94 visit_try_catch([](auto &ptr) { ptr->on_periodicity_change(); },
95 *impl->solver);
96 }
97}
98
100 if (impl->solver) {
101 visit_try_catch([](auto &ptr) { ptr->on_cell_structure_change(); },
102 *impl->solver);
103 }
104}
105
107 explicit LongRangePressure(ParticleRange const &particles)
108 : m_particles{particles} {}
109
110#ifdef ESPRESSO_P3M
111 auto operator()(std::shared_ptr<CoulombP3M> const &actor) const {
112 return actor->long_range_pressure(m_particles);
113 }
114#endif // ESPRESSO_P3M
115
116 auto operator()(std::shared_ptr<DebyeHueckel> const &) const {
117 return Utils::Vector9d{};
118 }
119
120 auto operator()(std::shared_ptr<ReactionField> const &) const {
121 return Utils::Vector9d{};
122 }
123
124 template <typename T>
126 auto operator()(std::shared_ptr<T> const &) const {
127 runtimeWarningMsg() << "Pressure calculation not implemented by "
128 << "electrostatics method " << Utils::demangle<T>();
129 return Utils::Vector9d{};
130 }
131
132private:
133 ParticleRange const &m_particles;
134};
135
138 if (impl->solver) {
139 return std::visit(LongRangePressure(particles), *impl->solver);
140 }
141 return {};
142}
143
145#ifdef ESPRESSO_P3M
146 auto operator()(std::shared_ptr<CoulombP3M> const &actor) const {
147 return actor->p3m_params.r_cut;
148 }
149 auto
150 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
151 return std::max(actor->elc.space_layer,
152 std::visit(*this, actor->base_solver));
153 }
154#endif // ESPRESSO_P3M
155#ifdef ESPRESSO_GSL
156 auto operator()(std::shared_ptr<CoulombMMM1D> const &) const {
157 return std::numeric_limits<double>::infinity();
158 }
159#endif
160#ifdef ESPRESSO_SCAFACOS
161 auto operator()(std::shared_ptr<CoulombScafacos> const &actor) const {
162 return actor->get_r_cut();
163 }
164#endif // ESPRESSO_SCAFACOS
165 auto operator()(std::shared_ptr<ReactionField> const &actor) const {
166 return actor->r_cut;
167 }
168 auto operator()(std::shared_ptr<DebyeHueckel> const &actor) const {
169 return actor->r_cut;
170 }
171};
172
173double Solver::cutoff() const {
174 if (impl->solver) {
175 return std::visit(ShortRangeCutoff(), *impl->solver);
176 }
177 return inactive_cutoff;
178}
179
181 template <typename T> void operator()(std::shared_ptr<T> const &) const {}
182
183#ifdef ESPRESSO_P3M
184 void operator()(std::shared_ptr<CoulombP3M> const &actor) const {
185 actor->count_charged_particles();
186 }
187 void
188 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
189 std::visit(*this, actor->base_solver);
190 }
191#endif // ESPRESSO_P3M
192};
193
196 if (impl->solver) {
197 std::visit(EventOnObservableCalc(), *impl->solver);
198 }
200 }
201}
202
204 explicit LongRangeForce(ParticleRange const &particles)
205 : m_particles(particles) {}
206
207#ifdef ESPRESSO_P3M
208 void operator()(std::shared_ptr<CoulombP3M> const &actor) const {
209 actor->add_long_range_forces(m_particles);
210 }
211 void
212 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
213 actor->add_long_range_forces(m_particles);
214 }
215#endif // ESPRESSO_P3M
216#ifdef ESPRESSO_SCAFACOS
217 void operator()(std::shared_ptr<CoulombScafacos> const &actor) const {
218 actor->add_long_range_forces();
219 }
220#endif
221 /* Several algorithms only provide near-field kernels */
222#ifdef ESPRESSO_GSL
223 void operator()(std::shared_ptr<CoulombMMM1D> const &) const {}
224#endif
225 void operator()(std::shared_ptr<DebyeHueckel> const &) const {}
226 void operator()(std::shared_ptr<ReactionField> const &) const {}
227
228private:
229 ParticleRange const &m_particles;
230};
231
233 explicit LongRangeEnergy(ParticleRange const &particles)
234 : m_particles(particles) {}
235
236#ifdef ESPRESSO_P3M
237 auto operator()(std::shared_ptr<CoulombP3M> const &actor) const {
238 return actor->long_range_energy(m_particles);
239 }
240 auto
241 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
242 return actor->long_range_energy(m_particles);
243 }
244#endif // ESPRESSO_P3M
245#ifdef ESPRESSO_SCAFACOS
246 auto operator()(std::shared_ptr<CoulombScafacos> const &actor) const {
247 return actor->long_range_energy();
248 }
249#endif
250 /* Several algorithms only provide near-field kernels */
251#ifdef ESPRESSO_GSL
252 auto operator()(std::shared_ptr<CoulombMMM1D> const &) const { return 0.; }
253#endif
254 auto operator()(std::shared_ptr<DebyeHueckel> const &) const { return 0.; }
255 auto operator()(std::shared_ptr<ReactionField> const &) const { return 0.; }
256
257private:
258 ParticleRange const &m_particles;
259};
260
261void Solver::calc_long_range_force(ParticleRange const &particles) const {
262 if (impl->solver) {
263 std::visit(LongRangeForce(particles), *impl->solver);
264 }
265}
266
267double Solver::calc_energy_long_range(ParticleRange const &particles) const {
268 if (impl->solver) {
269 return std::visit(LongRangeEnergy(particles), *impl->solver);
270 }
271 return 0.;
272}
273
274/** @brief Compute the net charge rescaled by the smallest non-zero charge. */
275static auto calc_charge_excess_ratio(std::vector<double> const &charges) {
276 using namespace boost::accumulators;
277 using KahanSum = accumulator_set<double, features<tag::sum_kahan>>;
278
279 KahanSum q_sum;
280 auto q_min = std::numeric_limits<double>::infinity();
281
282 for (auto const q : charges) {
283 if (q != 0.) {
284 q_sum(q);
285 q_min = std::min(q_min, std::abs(q));
286 }
287 }
288
289 return std::abs(sum_kahan(q_sum)) / q_min;
290}
291
293 double relative_tolerance) {
294 // collect non-zero particle charges from all nodes
295 std::vector<double> local_charges;
296 for (auto const &p : system.cell_structure->local_particles()) {
297 local_charges.push_back(p.q());
298 }
299 std::vector<std::vector<double>> node_charges;
300 boost::mpi::gather(comm_cart, local_charges, node_charges, 0);
301
302 // run Kahan sum on charges
303 auto excess_ratio = 0.;
304 if (this_node == 0) {
305 auto charges = std::move(local_charges);
306 for (auto it = ++node_charges.begin(); it != node_charges.end(); ++it) {
307 charges.insert(charges.end(), it->begin(), it->end());
308 }
309 excess_ratio = calc_charge_excess_ratio(charges);
310 }
311 boost::mpi::broadcast(comm_cart, excess_ratio, 0);
312
313 if (excess_ratio >= relative_tolerance) {
314 std::ostringstream serializer;
315 serializer << std::scientific << std::setprecision(4);
316 serializer << excess_ratio;
317 throw std::runtime_error(
318 "The system is not charge neutral. Please neutralize the system "
319 "before adding a new actor by adding the corresponding counterions "
320 "to the system. Alternatively you can turn off the electroneutrality "
321 "check by supplying check_neutrality=False when creating the actor. "
322 "In this case you may be simulating a non-neutral system which will "
323 "affect physical observables like e.g. the pressure, the chemical "
324 "potentials of charged species or potential energies of the system. "
325 "Since simulations of non charge neutral systems are special please "
326 "make sure you know what you are doing. The relative charge excess "
327 "was " +
328 serializer.str());
329 }
330}
331
332} // namespace Coulomb
333#endif // ESPRESSO_ELECTROSTATICS
Vector implementation and trait types for boost qvm interoperability.
A range of particles.
Main system class.
std::shared_ptr< CellStructure > cell_structure
Coulomb::Solver coulomb
boost::mpi::communicator comm_cart
The communicator.
int this_node
The number of this node.
constexpr double inactive_cutoff
Special cutoff value for an inactive interaction.
Definition config.hpp:44
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeWarningMsg()
ICC is a method that allows to take into account the influence of arbitrarily shaped dielectric inter...
void check_charge_neutrality(System::System const &system, double relative_tolerance)
Check if the system is charge-neutral.
Definition coulomb.cpp:292
static auto calc_charge_excess_ratio(std::vector< double > const &charges)
Compute the net charge rescaled by the smallest non-zero charge.
Definition coulomb.cpp:275
Solver const & get_coulomb()
Definition coulomb.cpp:65
System & get_system()
void operator()(std::shared_ptr< T > const &) const
Definition coulomb.cpp:181
void operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:188
void operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:184
auto operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:255
LongRangeEnergy(ParticleRange const &particles)
Definition coulomb.cpp:233
auto operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:237
auto operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:252
auto operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:241
auto operator()(std::shared_ptr< CoulombScafacos > const &actor) const
Definition coulomb.cpp:246
auto operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:254
void operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:226
LongRangeForce(ParticleRange const &particles)
Definition coulomb.cpp:204
void operator()(std::shared_ptr< CoulombScafacos > const &actor) const
Definition coulomb.cpp:217
void operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:223
void operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:208
void operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:225
void operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:212
LongRangePressure(ParticleRange const &particles)
Definition coulomb.cpp:107
auto operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:120
auto operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:116
auto operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:111
auto operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:146
auto operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:156
auto operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:150
auto operator()(std::shared_ptr< ReactionField > const &actor) const
Definition coulomb.cpp:165
auto operator()(std::shared_ptr< DebyeHueckel > const &actor) const
Definition coulomb.cpp:168
auto operator()(std::shared_ptr< CoulombScafacos > const &actor) const
Definition coulomb.cpp:161
void on_observable_calc()
Definition coulomb.cpp:194
double cutoff() const
Definition coulomb.cpp:173
void sanity_checks() const
Definition coulomb.cpp:67
void on_cell_structure_change()
Definition coulomb.cpp:99
void on_periodicity_change()
Definition coulomb.cpp:92
void calc_long_range_force(ParticleRange const &particles) const
Definition coulomb.cpp:261
void on_node_grid_change()
Definition coulomb.cpp:86
Utils::Vector9d calc_pressure_long_range(ParticleRange const &particles) const
Definition coulomb.cpp:137
void on_coulomb_change()
Definition coulomb.cpp:73
std::unique_ptr< Implementation > impl
Pointer-to-implementation.
void on_boxl_change()
Definition coulomb.cpp:80
bool reinit_on_observable_calc
Whether to reinitialize the solver on observable calculation.
double calc_energy_long_range(ParticleRange const &particles) const
Definition coulomb.cpp:267
The electrostatic method supports pressure calculation.
Definition coulomb.hpp:78
void visit_try_catch(Visitor &&visitor, Variant &actor)
Run a kernel on a variant and queue errors.