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-2025 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
29#include "actor/visitors.hpp"
31#include "communication.hpp"
33#include "errorhandling.hpp"
34#include "system/System.hpp"
35
36#include <utils/Vector.hpp>
37#include <utils/demangle.hpp>
38
39#include <boost/accumulators/accumulators.hpp>
40#include <boost/accumulators/statistics/sum_kahan.hpp>
41#include <boost/mpi/collectives/broadcast.hpp>
42#include <boost/mpi/collectives/gather.hpp>
43
44#include <algorithm>
45#include <cassert>
46#include <cmath>
47#include <cstdio>
48#include <iomanip>
49#include <limits>
50#include <memory>
51#include <optional>
52#include <sstream>
53#include <stdexcept>
54#include <variant>
55#include <vector>
56
57namespace Coulomb {
58
60 impl = std::make_unique<Implementation>();
62}
63
65 if (impl->solver) {
66 std::visit([](auto const &ptr) { ptr->sanity_checks(); }, *impl->solver);
67 }
68}
69
72 if (impl->solver) {
73 visit_try_catch([](auto &ptr) { ptr->init(); }, *impl->solver);
74 }
75}
76
78 if (impl->solver) {
79 visit_try_catch([](auto &ptr) { ptr->on_boxl_change(); }, *impl->solver);
80 }
81}
82
84 if (impl->solver) {
85 std::visit([](auto &ptr) { ptr->on_node_grid_change(); }, *impl->solver);
86 }
87}
88
90 if (impl->solver) {
91 visit_try_catch([](auto &ptr) { ptr->on_periodicity_change(); },
92 *impl->solver);
93 }
94}
95
97 if (impl->solver) {
98 visit_try_catch([](auto &ptr) { ptr->on_cell_structure_change(); },
99 *impl->solver);
100 }
101}
102
104#ifdef ESPRESSO_P3M
105 auto operator()(std::shared_ptr<CoulombP3M> const &actor) const {
106 return actor->long_range_pressure();
107 }
108#endif // ESPRESSO_P3M
109
110 auto operator()(std::shared_ptr<DebyeHueckel> const &) const {
111 return Utils::Vector9d{};
112 }
113
114 auto operator()(std::shared_ptr<ReactionField> const &) const {
115 return Utils::Vector9d{};
116 }
117
118 template <typename T>
120 auto operator()(std::shared_ptr<T> const &) const {
121 runtimeWarningMsg() << "Pressure calculation not implemented by "
122 << "electrostatics method " << Utils::demangle<T>();
123 return Utils::Vector9d{};
124 }
125};
126
128 if (impl->solver) {
129 return std::visit(LongRangePressure{}, *impl->solver);
130 }
131 return {};
132}
133
135#ifdef ESPRESSO_P3M
136 auto operator()(std::shared_ptr<CoulombP3M> const &actor) const {
137 return actor->p3m_params.r_cut;
138 }
139 auto
140 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
141 return std::max(actor->elc.space_layer,
142 std::visit(*this, actor->base_solver));
143 }
144#endif // ESPRESSO_P3M
145#ifdef ESPRESSO_GSL
146 auto operator()(std::shared_ptr<CoulombMMM1D> const &) const {
147 return std::numeric_limits<double>::infinity();
148 }
149#endif
150#ifdef ESPRESSO_SCAFACOS
151 auto operator()(std::shared_ptr<CoulombScafacos> const &actor) const {
152 return actor->get_r_cut();
153 }
154#endif // ESPRESSO_SCAFACOS
155 auto operator()(std::shared_ptr<ReactionField> const &actor) const {
156 return actor->r_cut;
157 }
158 auto operator()(std::shared_ptr<DebyeHueckel> const &actor) const {
159 return actor->r_cut;
160 }
161};
162
163double Solver::cutoff() const {
164 if (impl->solver) {
165 return std::visit(ShortRangeCutoff(), *impl->solver);
166 }
167 return inactive_cutoff;
168}
169
171 template <typename T> void operator()(std::shared_ptr<T> const &) const {}
172
173#ifdef ESPRESSO_P3M
174 void operator()(std::shared_ptr<CoulombP3M> const &actor) const {
175 actor->count_charged_particles();
176 }
177 void
178 operator()(std::shared_ptr<ElectrostaticLayerCorrection> const &actor) const {
179 std::visit(*this, actor->base_solver);
180 }
181#endif // ESPRESSO_P3M
182};
183
186 if (impl->solver) {
187 std::visit(EventOnObservableCalc(), *impl->solver);
188 }
190 }
191}
192
194 template <class Solver>
195 void operator()(std::shared_ptr<Solver> const &actor) const {
196 actor->add_long_range_forces();
197 }
198 /* Several algorithms only provide near-field kernels */
199#ifdef ESPRESSO_GSL
200 void operator()(std::shared_ptr<CoulombMMM1D> const &) const {}
201#endif
202 void operator()(std::shared_ptr<DebyeHueckel> const &) const {}
203 void operator()(std::shared_ptr<ReactionField> const &) const {}
204};
205
207 template <class Solver>
208 auto operator()(std::shared_ptr<Solver> const &actor) const {
209 return actor->long_range_energy();
210 }
211 /* Several algorithms only provide near-field kernels */
212#ifdef ESPRESSO_GSL
213 auto operator()(std::shared_ptr<CoulombMMM1D> const &) const { return 0.; }
214#endif
215 auto operator()(std::shared_ptr<DebyeHueckel> const &) const { return 0.; }
216 auto operator()(std::shared_ptr<ReactionField> const &) const { return 0.; }
217};
218
220 if (impl->solver) {
221 std::visit(LongRangeForce{}, *impl->solver);
222 }
223}
224
226 if (impl->solver) {
227 return std::visit(LongRangeEnergy{}, *impl->solver);
228 }
229 return 0.;
230}
231
232/** @brief Compute the net charge rescaled by the smallest non-zero charge. */
233static auto calc_charge_excess_ratio(std::vector<double> const &charges) {
234 using namespace boost::accumulators;
235 using KahanSum = accumulator_set<double, features<tag::sum_kahan>>;
236
237 KahanSum q_sum;
238 auto q_min = std::numeric_limits<double>::infinity();
239
240 for (auto const q : charges) {
241 if (q != 0.) {
242 q_sum(q);
243 q_min = std::min(q_min, std::abs(q));
244 }
245 }
246
247 return std::abs(sum_kahan(q_sum)) / q_min;
248}
249
251 double relative_tolerance) {
252 // collect non-zero particle charges from all nodes
253 std::vector<double> local_charges;
254 for (auto const &p : system.cell_structure->local_particles()) {
255 local_charges.push_back(p.q());
256 }
257 std::vector<std::vector<double>> node_charges;
258 boost::mpi::gather(comm_cart, local_charges, node_charges, 0);
259
260 // run Kahan sum on charges
261 auto excess_ratio = 0.;
262 if (this_node == 0) {
263 auto charges = std::move(local_charges);
264 for (auto it = ++node_charges.begin(); it != node_charges.end(); ++it) {
265 charges.insert(charges.end(), it->begin(), it->end());
266 }
267 excess_ratio = calc_charge_excess_ratio(charges);
268 }
269 boost::mpi::broadcast(comm_cart, excess_ratio, 0);
270
271 if (excess_ratio >= relative_tolerance) {
272 std::ostringstream serializer;
273 serializer << std::scientific << std::setprecision(4);
274 serializer << excess_ratio;
275 throw std::runtime_error(
276 "The system is not charge neutral. Please neutralize the system "
277 "before adding a new actor by adding the corresponding counterions "
278 "to the system. Alternatively you can turn off the electroneutrality "
279 "check by supplying check_neutrality=False when creating the actor. "
280 "In this case you may be simulating a non-neutral system which will "
281 "affect physical observables like e.g. the pressure, the chemical "
282 "potentials of charged species or potential energies of the system. "
283 "Since simulations of non charge neutral systems are special please "
284 "make sure you know what you are doing. The relative charge excess "
285 "was " +
286 serializer.str());
287 }
288}
289
290} // namespace Coulomb
291#endif // ESPRESSO_ELECTROSTATICS
Vector implementation and trait types for boost qvm interoperability.
Main system class.
std::shared_ptr< CellStructure > cell_structure
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:250
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:233
void operator()(std::shared_ptr< T > const &) const
Definition coulomb.cpp:171
void operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:178
void operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:174
auto operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:216
auto operator()(std::shared_ptr< Solver > const &actor) const
Definition coulomb.cpp:208
auto operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:213
auto operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:215
void operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:203
void operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:200
void operator()(std::shared_ptr< Solver > const &actor) const
Definition coulomb.cpp:195
void operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:202
auto operator()(std::shared_ptr< ReactionField > const &) const
Definition coulomb.cpp:114
auto operator()(std::shared_ptr< DebyeHueckel > const &) const
Definition coulomb.cpp:110
auto operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:105
auto operator()(std::shared_ptr< CoulombP3M > const &actor) const
Definition coulomb.cpp:136
auto operator()(std::shared_ptr< CoulombMMM1D > const &) const
Definition coulomb.cpp:146
auto operator()(std::shared_ptr< ElectrostaticLayerCorrection > const &actor) const
Definition coulomb.cpp:140
auto operator()(std::shared_ptr< ReactionField > const &actor) const
Definition coulomb.cpp:155
auto operator()(std::shared_ptr< DebyeHueckel > const &actor) const
Definition coulomb.cpp:158
auto operator()(std::shared_ptr< CoulombScafacos > const &actor) const
Definition coulomb.cpp:151
Utils::Vector9d calc_pressure_long_range() const
Definition coulomb.cpp:127
double calc_energy_long_range() const
Definition coulomb.cpp:225
void on_observable_calc()
Definition coulomb.cpp:184
double cutoff() const
Definition coulomb.cpp:163
void sanity_checks() const
Definition coulomb.cpp:64
void on_cell_structure_change()
Definition coulomb.cpp:96
void on_periodicity_change()
Definition coulomb.cpp:89
void calc_long_range_force() const
Definition coulomb.cpp:219
void on_node_grid_change()
Definition coulomb.cpp:83
void on_coulomb_change()
Definition coulomb.cpp:70
std::unique_ptr< Implementation > impl
Pointer-to-implementation.
void on_boxl_change()
Definition coulomb.cpp:77
bool reinit_on_observable_calc
Whether to reinitialize the solver on observable calculation.
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.