ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
forces.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2026 The ESPResSo project
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
4 * Max-Planck-Institute for Polymer Research, Theory Group
5 *
6 * This file is part of ESPResSo.
7 *
8 * ESPResSo is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * ESPResSo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <config/config.hpp>
23
24#include "BoxGeometry.hpp"
25#include "Particle.hpp"
26#include "PropagationMode.hpp"
30#include "cells.hpp"
31#include "collision_detection/CollisionDetection.hpp"
32#include "communication.hpp"
33#include "constraints/Constraints.hpp"
35#include "forces_init.hpp"
36#include "forces_inline.hpp"
37#include "galilei/ComFixed.hpp"
44#include "npt.hpp"
45#include "rotation.hpp"
47#include "short_range_loop.hpp"
50#include "system/System.hpp"
51#include "thermostat.hpp"
52#include "virtual_sites/com.hpp"
54
55#include <utils/Vector.hpp>
56#include <utils/math/sqr.hpp>
57
58#ifdef ESPRESSO_CALIPER
59#include <caliper/cali.h>
60#endif
61
62#include <Cabana_Core.hpp>
63
64#include <cassert>
65#include <cmath>
66#include <cstddef>
67#include <memory>
68#include <span>
69#include <variant>
70
71static void force_capping(CellStructure &cell_structure, double force_cap) {
72 if (force_cap > 0.) {
73 auto const force_cap_sq = Utils::sqr(force_cap);
74 cell_structure.for_each_local_particle(
75 [&force_cap, &force_cap_sq](Particle &p) {
76 auto const force_sq = p.force().norm2();
77 if (force_sq > force_cap_sq) {
78 p.force() *= force_cap / std::sqrt(force_sq);
79 }
80 });
81 }
82}
83
84#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
85static void reinit_dip_fld(CellStructure const &cell_structure) {
86 cell_structure.for_each_local_particle(
87 [](Particle &p) { p.dip_fld() = {0., 0., 0.}; });
88}
89#endif
90
91static BondsKernelData
93 auto scatter_force = system.cell_structure->get_scatter_force();
94#ifdef ESPRESSO_NPT
95 auto scatter_virial = system.cell_structure->get_scatter_virial();
96#endif
97 auto const &aosoa = system.cell_structure->get_aosoa();
98 return /* BondsKernelData */ {*system.bonded_ias,
99 *system.bond_breakage,
100 *system.box_geo,
102#ifdef ESPRESSO_NPT
104#endif
105 aosoa,
106 !system.bond_breakage->breakage_specs.empty()};
107}
108
111 auto const &elc_kernel, auto const &coulomb_kernel,
112 auto const &dipoles_kernel, auto const &coulomb_u_kernel) {
113
114 auto const &unique_particles = system.cell_structure->get_unique_particles();
115
116 auto scatter_force = system.cell_structure->get_scatter_force();
117#ifdef ESPRESSO_ROTATION
118 auto scatter_torque = system.cell_structure->get_scatter_torque();
119#endif
120#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
121 auto scatter_dip_fld = system.cell_structure->get_scatter_dip_fld();
122#endif
123#ifdef ESPRESSO_NPT
124 auto scatter_virial = system.cell_structure->get_scatter_virial();
125#endif
126 auto const &aosoa = system.cell_structure->get_aosoa();
127
128 return /* ForcesKernel */ {*system.bonded_ias,
129 *system.nonbonded_ias,
130 get_ptr(coulomb_kernel),
131 get_ptr(dipoles_kernel),
132 get_ptr(elc_kernel),
133 get_ptr(coulomb_u_kernel),
134 system.coulomb,
135 *system.thermostat,
136 *system.box_geo,
137 unique_particles,
139#ifdef ESPRESSO_ROTATION
141#endif
142#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
144#endif
145#ifdef ESPRESSO_NPT
146 virial,
148#endif
149 aosoa,
151}
152
153// Single construction-and-launch site for SpecializedForcesKernel, shared by
154// the with- and without-coulomb branches of the dispatch below.
155template <bool HasCoulomb>
157 InteractionsNonBonded const &nonbonded_ias,
158 CellStructure::AoSoA_pack const &aosoa,
160 CuboidMinimumImage const &minimum_image, double const max_cutoff_sq
162 ,
165 ,
166 CoulombP3M const *p3m = nullptr
167#endif
168#endif
169) {
170 return [=, &nonbonded_ias, &aosoa](CellStructure::ListType const &verlet_list,
171 std::size_t const n) {
172 SpecializedForcesKernel<HasCoulomb> const kernel{nonbonded_ias,
173 aosoa,
175 verlet_list.counts,
176 verlet_list.neighbors,
177 minimum_image,
179#ifdef ESPRESSO_ELECTROSTATICS
180 ,
182#ifdef ESPRESSO_P3M
183 ,
184 p3m
185#endif
186#endif
187 };
188 Kokkos::parallel_for("specialized_nonbonded_pairs",
189 Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(
190 std::size_t{0}, n),
191 kernel);
192 };
193}
194
195// Build the compile-time-specialized Verlet pair loop when the active feature
196// set is covered by SpecializedForcesKernel: cuboid box, no NPT virial, no
197// dipolar or ELC kernel, only allowlisted (central-radial) pair potentials, no
198// Thole pair, and no particle with an exclusion. Returns an empty
199// ShortRangeVerletPairLoop otherwise, leaving cabana_short_range on the
200// generic ForcesKernel path. The specialized kernel is bitwise-identical to
201// the generic one on these systems.
203 System::System const &system,
205 [[maybe_unused]] auto const &elc_kernel, auto const &coulomb_kernel,
206 [[maybe_unused]] auto const &dipoles_kernel) {
207 if (system.box_geo->type() != BoxType::CUBOID)
208 return {};
209#ifdef ESPRESSO_NPT
210 if (virial != nullptr)
211 return {};
212#endif
213#ifdef ESPRESSO_DIPOLES
214 if (get_ptr(dipoles_kernel) != nullptr)
215 return {};
216#endif
217#ifdef ESPRESSO_ELECTROSTATICS
218 if (get_ptr(elc_kernel) != nullptr)
219 return {};
220#endif
221 auto const &nonbonded_ias = *system.nonbonded_ias;
222 // Allowlist over the aggregated pair-potential mask (O(1), maintained by
223 // recalc_maximal_cutoffs). Any type pair with a potential the specialized
224 // kernel does not compute -- Gay-Berne, DPD (in which case the DPD
225 // thermostat could act on the pair), or any future addition -- falls back
226 // to the generic kernel by default.
227 if ((nonbonded_ias.combined_active_pair_mask() &
229 return {};
230#ifdef ESPRESSO_THOLE
231 // Thole damping is not in the pair-potential mask; check its own aggregate.
232 if (nonbonded_ias.any_thole_configured())
233 return {};
234#endif
235 auto &cell_structure = *system.cell_structure;
236 auto const &aosoa = cell_structure.get_aosoa();
237#ifdef ESPRESSO_EXCLUSIONS
238 // The specialized kernel has no exclusion handling. The commit sweep (run by
239 // update_verlet_state earlier in this same force call) accumulates whether
240 // any packed particle carries an exclusion, so this is an O(1) read of the
241 // same population the old per-particle sweep covered (local + ghosts).
242 if (aosoa.has_any_exclusion())
243 return {};
244#endif
245
246 auto scatter_force = cell_structure.get_scatter_force();
247 auto const minimum_image = system.box_geo->cuboid_minimum_image();
248 auto const max_cutoff_sq = Utils::sqr(system.maximal_cutoff());
249
250#ifdef ESPRESSO_ELECTROSTATICS
251 if (auto const *coulomb_ptr = get_ptr(coulomb_kernel);
252 coulomb_ptr != nullptr) {
254 nonbonded_ias, aosoa, scatter_force, minimum_image, max_cutoff_sq,
257 ,
259#endif
260 );
261 }
262#else
263 static_cast<void>(coulomb_kernel);
264#endif
265
267 nonbonded_ias, aosoa, scatter_force, minimum_image, max_cutoff_sq);
268}
269
272
273 auto const &unique_particles = system.cell_structure->get_unique_particles();
274 auto &local_force = system.cell_structure->get_local_force();
275 auto scatter_force = system.cell_structure->get_scatter_force();
276 Kokkos::Experimental::contribute(local_force, scatter_force);
277#ifdef ESPRESSO_ROTATION
278 auto &local_torque = system.cell_structure->get_local_torque();
279 auto scatter_torque = system.cell_structure->get_scatter_torque();
280 Kokkos::Experimental::contribute(local_torque, scatter_torque);
281#endif
282#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
283 auto &local_dip_fld = system.cell_structure->get_local_dip_fld();
284 auto scatter_dip_fld = system.cell_structure->get_scatter_dip_fld();
285 Kokkos::Experimental::contribute(local_dip_fld, scatter_dip_fld);
286#endif
287#ifdef ESPRESSO_NPT
288 auto &local_virial = system.cell_structure->get_local_virial();
289 auto scatter_virial = system.cell_structure->get_scatter_virial();
290 Kokkos::Experimental::contribute(local_virial, scatter_virial);
291#endif
292
293 using execution_space = Kokkos::DefaultHostExecutionSpace;
294 Kokkos::RangePolicy<execution_space> policy(std::size_t{0},
295 unique_particles.size());
296 Kokkos::parallel_for("reduction", policy,
297 [&local_force,
299 &local_torque,
300#endif
302 &local_dip_fld,
303#endif
304 &unique_particles](std::size_t const i) {
305 Utils::Vector3d force{};
306#ifdef ESPRESSO_ROTATION
307 Utils::Vector3d torque{};
308#endif
309#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
310 Utils::Vector3d dip_fld{};
311#endif
312 force[0] += local_force(i, 0);
313 force[1] += local_force(i, 1);
314 force[2] += local_force(i, 2);
315#ifdef ESPRESSO_ROTATION
316 torque[0] += local_torque(i, 0);
317 torque[1] += local_torque(i, 1);
318 torque[2] += local_torque(i, 2);
319#endif
320#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
321 dip_fld[0] += local_dip_fld(i, 0);
322 dip_fld[1] += local_dip_fld(i, 1);
323 dip_fld[2] += local_dip_fld(i, 2);
324#endif
325 unique_particles.at(i)->force() += force;
326#ifdef ESPRESSO_ROTATION
327 unique_particles.at(i)->torque() += torque;
328#endif
329#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
330 unique_particles.at(i)->dip_fld() += dip_fld;
331#endif
332 });
333 Kokkos::fence();
334
335#ifdef ESPRESSO_NPT
336 if (virial) {
337 (*virial)[0] += local_virial(0);
338 (*virial)[1] += local_virial(1);
339 (*virial)[2] += local_virial(2);
340 }
341#endif
342}
343
345#ifdef ESPRESSO_CALIPER
347#endif
348#ifdef ESPRESSO_CUDA
349 {
350#ifdef ESPRESSO_CALIPER
351 CALI_MARK_BEGIN("copy_particles_to_GPU");
352#endif
353 gpu->update();
354#ifdef ESPRESSO_CALIPER
355 CALI_MARK_END("copy_particles_to_GPU");
356#endif
357 }
358#endif // ESPRESSO_CUDA
359
360#ifdef ESPRESSO_COLLISION_DETECTION
361 collision_detection->clear_queue();
362 auto const collision_detection_cutoff = collision_detection->cutoff();
363#else
365#endif
366 bond_breakage->clear_queue();
367 auto particles = cell_structure->local_particles();
368#ifdef ESPRESSO_NPT
369 if (propagation->used_propagations & PropagationMode::TRANS_LANGEVIN_NPT) {
370 // reset virial part of instantaneous pressure
371 npt_inst_pressure->p_vir = Utils::Vector3d{};
372 }
373#endif
374#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
375 if (dipoles.impl->solver.has_value()) {
376 reinit_dip_fld(*cell_structure);
377 }
378#endif
379
380 // Use combined function instead of two separate calls
381
382 auto const elc_kernel = coulomb.pair_force_elc_kernel();
383 auto const coulomb_kernel = coulomb.pair_force_kernel();
384 auto const dipoles_kernel = dipoles.pair_force_kernel();
385 auto const coulomb_u_kernel = coulomb.pair_energy_kernel();
386 auto *const virial = get_npt_virial();
387
388 // Factory instead of an eager criterion: construction fills an O(n_types^2)
389 // cutoff table, so it only runs where a criterion is actually consumed (the
390 // link-cell fallback and the collision-detection loop below).
391 auto const make_verlet_criterion = [&] {
392 return VerletCriterion<>{*this,
393 cell_structure->get_verlet_skin(),
394 get_interaction_range(),
395 coulomb.cutoff(),
396 dipoles.cutoff(),
398 };
399
401#ifdef ESPRESSO_ELECTROSTATICS
402 if (coulomb.impl->extension) {
403 update_icc_particles();
404 }
405#endif // ESPRESSO_ELECTROSTATICS
407#ifdef ESPRESSO_CALIPER
408 CALI_MARK_BEGIN("calc_long_range_forces");
409#endif
410#ifdef ESPRESSO_ELECTROSTATICS
411 coulomb.calc_long_range_force();
412#endif
413#ifdef ESPRESSO_DIPOLES
414 dipoles.calc_long_range_force();
415#endif
416#ifdef ESPRESSO_CALIPER
417 CALI_MARK_END("calc_long_range_forces");
418#endif
419
420#ifdef ESPRESSO_CALIPER
421 CALI_MARK_BEGIN("cabana_short_range");
422#endif
423 auto &bs = cell_structure->bond_state();
426 bonds_kernel_data, bs.pair_list, bs.pair_ids, get_ptr(coulomb_kernel)};
427 auto angle_bonds_kernel =
428 AngleBondsKernel{bonds_kernel_data, bs.angle_list, bs.angle_ids};
430 DihedralBondsKernel{bonds_kernel_data, bs.dihedral_list, bs.dihedral_ids};
431
433 create_cabana_neighbor_kernel(*this, virial, elc_kernel, coulomb_kernel,
434 dipoles_kernel, coulomb_u_kernel);
435
437 *this, virial, elc_kernel, coulomb_kernel, dipoles_kernel);
438
441 *cell_structure, get_interaction_range(),
442 bonded_ias->maximal_cutoff(), make_verlet_criterion,
443 propagation->integ_switch, specialized_pair_loop);
444
445 // Force and Torque reduction
447
448#ifdef ESPRESSO_COLLISION_DETECTION
449 auto collision_kernel = [&collision_detection = *collision_detection](
450 Particle const &p1, Particle const &p2,
451 Distance const &d) {
452 collision_detection.detect_collision(p1, p2, d.dist2);
453 };
454 if (not collision_detection->is_off()) {
456 cell_structure->non_bonded_loop(collision_kernel, verlet_criterion);
457 }
458#endif // ESPRESSO_COLLISION_DETECTION
459
460#ifdef ESPRESSO_CALIPER
461 CALI_MARK_END("cabana_short_range");
462#endif
463
464 constraints->add_forces(particles, get_sim_time());
465 oif_global->calculate_forces();
466
467 // Must be done here. Forces need to be ghost-communicated
468 immersed_boundaries->volume_conservation(*cell_structure);
469
470 if (thermostat->lb and (propagation->used_propagations &
472#ifdef ESPRESSO_CALIPER
473 CALI_MARK_BEGIN("lb_particle_coupling");
474#endif
475 lb_couple_particles();
476#ifdef ESPRESSO_CALIPER
477 CALI_MARK_END("lb_particle_coupling");
478#endif
479 }
480
481#ifdef ESPRESSO_CUDA
482 {
483#ifdef ESPRESSO_CALIPER
484 CALI_MARK_BEGIN("copy_forces_from_GPU");
485#endif
486 gpu->copy_forces_to_host(particles, this_node);
487
488#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
489 gpu->copy_dip_fld_to_host(particles, this_node);
490#endif
491
492#ifdef ESPRESSO_CALIPER
493 CALI_MARK_END("copy_forces_from_GPU");
494#endif
495 }
496#endif // ESPRESSO_CUDA
497
498#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
499 if (propagation->used_propagations &
503 }
504#endif
505#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
506 if (propagation->used_propagations &
509 }
510#endif
511
512 // Communication step: ghost forces
513 cell_structure->ghosts_reduce_forces();
514#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
515 if (dipoles.impl->solver.has_value()) {
516 cell_structure->ghosts_reduce_dipole_field();
517 }
518#endif
519
520 // should be pretty late, since it needs to zero out the total force
521 comfixed->apply(particles);
522
523 // Needs to be the last one to be effective
524 force_capping(*cell_structure, force_cap);
525
526 // mark that forces are now up-to-date
527 propagation->recalc_forces = false;
528}
Vector implementation and trait types for boost qvm interoperability.
This file contains everything related to the global cell structure / cell system.
double maximal_cutoff() const
Calculate the maximal cutoff of bonded interactions, required to determine the cell size for communic...
Describes a cell structure / cell system.
Kokkos::Experimental::ScatterView< double *[3], Kokkos::LayoutRight, memory_space > ScatterForce
void for_each_local_particle(Callable &&f, bool parallel=true) const
Run a kernel on all local particles.
Cuboid minimum-image fold parameters for hot pair loops.
Main system class.
void calculate_forces()
Calculate all forces.
Definition forces.cpp:344
Returns true if the particles are to be considered for short range interactions.
void vs_com_back_transfer_forces_and_torques(CellStructure &cell_structure)
Definition com.cpp:232
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
int this_node
The number of this node.
constexpr double inactive_cutoff
Special cutoff value for an inactive interaction.
Definition config.hpp:53
const T * get_ptr(std::optional< T > const &opt)
static BondsKernelData create_kokkos_bonds_kernel_data(System::System const &system)
Definition forces.cpp:92
static ShortRangeVerletPairLoop create_specialized_verlet_pair_loop(System::System const &system, Utils::Vector3d const *virial, auto const &elc_kernel, auto const &coulomb_kernel, auto const &dipoles_kernel)
Definition forces.cpp:202
static void reinit_dip_fld(CellStructure const &cell_structure)
Definition forces.cpp:85
static void force_capping(CellStructure &cell_structure, double force_cap)
Definition forces.cpp:71
static ForcesKernel create_cabana_neighbor_kernel(System::System const &system, Utils::Vector3d *virial, auto const &elc_kernel, auto const &coulomb_kernel, auto const &dipoles_kernel, auto const &coulomb_u_kernel)
Definition forces.cpp:109
static ShortRangeVerletPairLoop make_specialized_verlet_pair_loop(InteractionsNonBonded const &nonbonded_ias, CellStructure::AoSoA_pack const &aosoa, CellStructure::ScatterForce scatter_force, CuboidMinimumImage const &minimum_image, double const max_cutoff_sq, Coulomb::ShortRangeForceKernel::kernel_type const *coulomb_ptr=nullptr, CoulombP3M const *p3m=nullptr)
Definition forces.cpp:156
static void reduce_cabana_forces_and_torques(System::System const &system, Utils::Vector3d *virial)
Definition forces.cpp:270
CoulombP3M const * get_toplevel_p3m_solver(Coulomb::Solver const &coulomb)
The active P3M solver, or nullptr.
constexpr unsigned specialized_kernel_pair_mask
Pair potentials fully handled by SpecializedForcesKernel, i.e.
void init_forces_and_thermostat(System::System const &system)
Combined force initialization and Langevin noise application.
Force calculation.
ICC is a method that allows to take into account the influence of arbitrarily shaped dielectric inter...
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
Definition sqr.hpp:28
Various procedures concerning interactions between particles.
Exports for the NpT code.
void vs_relative_back_transfer_forces_and_torques(CellStructure &cell_structure)
Definition relative.cpp:164
This file contains all subroutines required to process rotational motion.
void cabana_short_range(auto const &pair_bonds_kernel, auto const &angle_bonds_kernel, auto const &dihedral_bonds_kernel, auto const &nonbonded_kernel, CellStructure &cell_structure, double pair_cutoff, double bond_cutoff, auto const &make_verlet_criterion, auto const integ_switch, ShortRangeVerletPairLoop const &verlet_pair_loop={})
std::function< void(CellStructure::ListType const &, std::size_t)> ShortRangeVerletPairLoop
void update_verlet_state(System::System const &system, double const collision_cut)
BondedInteractionsMap const & bonded_ias
P3M solver.
Definition p3m.hpp:55
Solver::ShortRangeForceKernel kernel_type
Distance vector and length handed to pair kernels.
BondedInteractionsMap const & bonded_ias
Struct holding all information for one particle.
Definition Particle.hpp:436
constexpr auto const & dip_fld() const
Definition Particle.hpp:590
constexpr auto const & force() const
Definition Particle.hpp:480
Own-the-loop specialization of the non-bonded pair kernel.