ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
forces_cabana.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025-2026 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#pragma once
21
22#include <config/config.hpp>
23
24#include "aosoa_pack.hpp"
25#include "forces_inline.hpp"
27
28#include <utils/Vector.hpp>
29
30#include <Cabana_Core.hpp>
31#include <Kokkos_ScatterView.hpp>
32
33#include <cstddef>
34#include <memory>
35#include <optional>
36#include <variant>
37#include <vector>
38
48 std::vector<Particle *> const &unique_particles;
50#ifdef ESPRESSO_ROTATION
52#endif
53#ifdef ESPRESSO_NPT
56#endif
58#ifdef ESPRESSO_P3M
60#endif
62
64 BondedInteractionsMap const &bonded_ias_,
65 InteractionsNonBonded const &nonbonded_ias_,
69 Coulomb::ShortRangeEnergyKernel::kernel_type const *coulomb_u_kernel_,
70 Coulomb::Solver const &coulomb_,
71 Thermostat::Thermostat const &thermostat_, BoxGeometry const &box_geo_,
72 std::vector<Particle *> const &unique_particles_,
73 CellStructure::ScatterForce local_force_,
74#ifdef ESPRESSO_ROTATION
75 CellStructure::ScatterForce local_torque_,
76#endif
77#ifdef ESPRESSO_NPT
78 Utils::Vector3d *const global_virial_,
79 CellStructure::ScatterVirial local_virial_,
80#endif
81 CellStructure::AoSoA_pack const &aosoa_, double system_max_cutoff_)
82 : bonded_ias(bonded_ias_), nonbonded_ias(nonbonded_ias_),
83 coulomb_kernel(coulomb_kernel_), dipoles_kernel(dipoles_kernel_),
84 elc_kernel(elc_kernel_), coulomb_u_kernel(coulomb_u_kernel_),
85 thermostat(thermostat_), box_geo(box_geo_),
86 unique_particles(unique_particles_),
87 local_force(std::move(local_force_)),
88#ifdef ESPRESSO_ROTATION
89 local_torque(std::move(local_torque_)),
90#endif
91#ifdef ESPRESSO_NPT
92 global_virial(global_virial_), local_virial(std::move(local_virial_)),
93#endif
94 aosoa(aosoa_), system_max_cutoff_sq(Utils::sqr(system_max_cutoff_)) {
95#ifdef ESPRESSO_P3M
96 p3m = nullptr;
97 if (auto &solver = coulomb_.impl->solver; solver.has_value()) {
98 if (std::holds_alternative<std::shared_ptr<CoulombP3M>>(*solver)) {
99 p3m = std::get<std::shared_ptr<CoulombP3M>>(*solver).get();
100 }
101 }
102#endif
103 }
104
105#ifdef ESPRESSO_NPT
106 ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION bool npt_active() const {
107 return global_virial != nullptr;
108 }
109#endif
110
111 ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION void
112 operator()(std::size_t i, std::size_t j) const {
113
114 // calc distance (component-wise, avoids constructing pos1/pos2 Vector3d
115 // on the hot early-exit path; pos1/pos2 are built lazily below only
116 // where kernels actually require them)
117 auto const d = box_geo.get_mi_vector(
118 aosoa.position(i, 0), aosoa.position(i, 1), aosoa.position(i, 2),
119 aosoa.position(j, 0), aosoa.position(j, 1), aosoa.position(j, 2));
120 auto const dist_sq = d.norm2();
121
122 // Early exit if distance > maximal global cutoff
123 if (dist_sq > system_max_cutoff_sq)
124 return;
125 auto const dist = std::sqrt(dist_sq);
126 auto const &ia_params =
128
129 ParticleForce pf{};
130
131 // Determine which data needs to be loaded based on active algorithms
132#if defined(ESPRESSO_EXCLUSIONS) or defined(ESPRESSO_THOLE)
133 bool need_particle_pointers = false;
134#ifdef ESPRESSO_EXCLUSIONS
135 need_particle_pointers |= aosoa.has_exclusion(i) or aosoa.has_exclusion(j);
136#endif
137#ifdef ESPRESSO_THOLE
138 need_particle_pointers |=
139 thole_active(ia_params, coulomb_kernel != nullptr);
140#endif
141
142 Particle const *p1_ptr = nullptr;
143 Particle const *p2_ptr = nullptr;
144 if (need_particle_pointers) {
145 p1_ptr = unique_particles.at(i);
146 p2_ptr = unique_particles.at(j);
147 }
148#endif
149
150 /***********************************************/
151 /* non-bonded pair potentials */
152 /***********************************************/
153
154 if (dist <= ia_params.max_cut) {
155#ifdef ESPRESSO_EXCLUSIONS
156 bool skip_non_bonded = false;
157 if (aosoa.has_exclusion(i) or aosoa.has_exclusion(j)) {
158 skip_non_bonded = not do_nonbonded(*p1_ptr, *p2_ptr);
159 }
160#else
161 constexpr bool skip_non_bonded = false;
162#endif
163 if (not skip_non_bonded) {
164 pf.f += calc_central_radial_force(ia_params, d, dist);
165
166 // Only call Thole force kernel if active
167#ifdef ESPRESSO_THOLE
168 if (thole_active(ia_params, coulomb_kernel != nullptr)) {
169 pf.f += thole_pair_force(*p1_ptr, *p2_ptr, ia_params, d, dist,
171 }
172#endif
173 // Only call Gay-Berne force kernel if active
174#ifdef ESPRESSO_GAY_BERNE
175 if (gay_berne_active(dist, ia_params)) {
176 auto const dir1 = aosoa.get_vector_at(aosoa.director, i);
177 auto const dir2 = aosoa.get_vector_at(aosoa.director, j);
178 pf += gb_pair_force(dir1, dir2, ia_params, d, dist);
179 }
180#endif
181 } // not skip_non_bonded
182 } // not dist > ia_params.max_cut
183
184 /*********************************************************************/
185 /* everything before this contributes to the virial pressure in NpT, */
186 /* but nothing afterwards, since the contribution to pressure from */
187 /* electrostatic is calculated by energy */
188 /*********************************************************************/
189#ifdef ESPRESSO_NPT
190 Utils::Vector3d virial{};
191 if (npt_active()) {
192 virial = hadamard_product(pf.f, d);
193 }
194#endif // ESPRESSO_NPT
195
196 /***********************************************/
197 /* thermostat */
198 /***********************************************/
199
200 /* The inter dpd force should not be part of the virial */
201#ifdef ESPRESSO_DPD
202 if (dpd_active(ia_params, thermostat.thermo_switch)) {
203 auto const pos1 = aosoa.get_vector_at(aosoa.position, i);
204 auto const pos2 = aosoa.get_vector_at(aosoa.position, j);
205 auto const vel1 = aosoa.get_vector_at(aosoa.velocity, i);
206 auto const vel2 = aosoa.get_vector_at(aosoa.velocity, j);
207 auto const force =
208 dpd_pair_force(pos1, vel1, aosoa.id(i), pos2, vel2, aosoa.id(j),
209 *thermostat.dpd, box_geo, ia_params, d, dist, dist_sq);
210 pf += force;
211 }
212#endif // ESPRESSO_DPD
213
214#ifdef ESPRESSO_ELECTROSTATICS
215 Utils::Vector3d f1_asym{};
216 Utils::Vector3d f2_asym{};
217 // real-space electrostatic charge-charge interaction
218 if (coulomb_kernel != nullptr) {
219 if ((aosoa.charge(i) != 0.) and (aosoa.charge(j) != 0.)) {
220 auto const q1q2 = aosoa.charge(i) * aosoa.charge(j);
221#ifdef ESPRESSO_P3M
222 if (p3m) [[likely]] {
223 pf.f += p3m->pair_force(q1q2, d, dist);
224 } else
225#endif
226 {
227 pf.f += (*coulomb_kernel)(q1q2, d, dist);
228 }
229 if (elc_kernel) {
230 auto const pos1 = aosoa.get_vector_at(aosoa.position, i);
231 auto const pos2 = aosoa.get_vector_at(aosoa.position, j);
232 (*elc_kernel)(pos1, pos2, f1_asym, f2_asym, q1q2);
233 }
234#ifdef ESPRESSO_NPT
235 if (npt_active()) {
236 auto const pos1 = aosoa.get_vector_at(aosoa.position, i);
237 auto const pos2 = aosoa.get_vector_at(aosoa.position, j);
238 virial[0] += (*coulomb_u_kernel)(pos1, pos2, q1q2, d, dist);
239 }
240#endif // ESPRESSO_NPT
241 }
242 }
243#endif // ESPRESSO_ELECTROSTATICS
244
245 // Only call dipole force kernel if active
246#ifdef ESPRESSO_DIPOLES
247 if (dipoles_kernel != nullptr) {
248 auto const d1d2 = aosoa.dipm(i) * aosoa.dipm(j);
249 if (d1d2 != 0.) {
250 auto const dir1 = aosoa.get_vector_at(aosoa.director, i);
251 auto const dir2 = aosoa.get_vector_at(aosoa.director, j);
252 pf += (*dipoles_kernel)(d1d2, aosoa.dipm(i) * dir1,
253 aosoa.dipm(j) * dir2, d, dist, dist_sq);
254 }
255 }
256#endif // ESPRESSO_DIPOLES
257
258 auto opf = calc_opposing_force(pf, d);
259#ifdef ESPRESSO_ELECTROSTATICS
260 pf.f += f1_asym;
261 opf.f += f2_asym;
262#endif // ESPRESSO_ELECTROSTATICS
263
264 auto access_force = local_force.access();
265
266 access_force(i, 0) += pf.f[0];
267 access_force(i, 1) += pf.f[1];
268 access_force(i, 2) += pf.f[2];
269#ifdef ESPRESSO_ROTATION
270 auto access_torque = local_torque.access();
271 access_torque(i, 0) += pf.torque[0];
272 access_torque(i, 1) += pf.torque[1];
273 access_torque(i, 2) += pf.torque[2];
274#endif
275
276 access_force(j, 0) += opf.f[0];
277 access_force(j, 1) += opf.f[1];
278 access_force(j, 2) += opf.f[2];
279#ifdef ESPRESSO_ROTATION
280 access_torque(j, 0) += opf.torque[0];
281 access_torque(j, 1) += opf.torque[1];
282 access_torque(j, 2) += opf.torque[2];
283#endif
284#ifdef ESPRESSO_NPT
285 if (npt_active()) {
286 auto access_virial = local_virial.access();
287 access_virial(0) += virial[0];
288 access_virial(1) += virial[1];
289 access_virial(2) += virial[2];
290 }
291#endif
292 }
293};
Vector implementation and trait types for boost qvm interoperability.
#define ESPRESSO_ATTR_ALWAYS_INLINE
container for bonded interactions.
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3< T > get_mi_vector(Utils::Vector3< T > const &a, Utils::Vector3< T > const &b) const
Get the minimum-image vector between two coordinates.
Kokkos::Experimental::ScatterView< double[3], Kokkos::LayoutRight > ScatterVirial
Kokkos::Experimental::ScatterView< double *[3], Kokkos::LayoutRight > ScatterForce
auto & get_ia_param(int i, int j)
Get interaction parameters between particle types i and j.
std::shared_ptr< DPDThermostat > dpd
int thermo_switch
Bitmask of currently active thermostats.
Utils::Vector3d dpd_pair_force(Utils::Vector3d const &p1_position, Utils::Vector3d const &p1_velocity, int p1_id, Utils::Vector3d const &p2_position, Utils::Vector3d const &p2_velocity, int p2_id, DPDThermostat const &dpd, BoxGeometry const &box_geo, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist, double dist2)
Definition dpd.cpp:75
bool do_nonbonded(Particle const &p1, Particle const &p2)
Determine if the non-bonded interactions between p1 and p2 should be calculated.
Force calculation.
ParticleForce calc_opposing_force(ParticleForce const &pf, Utils::Vector3d const &d)
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3d calc_central_radial_force(IA_parameters const &ia_params, Utils::Vector3d const &d, double const dist)
ParticleForce gb_pair_force(Utils::Vector3d const &ui, Utils::Vector3d const &uj, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist)
Calculate Gay-Berne force and torques.
Definition gay_berne.hpp:49
STL namespace.
ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION bool gay_berne_active(double dist, IA_parameters const &ia_params)
KOKKOS_INLINE_FUNCTION bool thole_active(IA_parameters const &ia_params, bool has_coulomb_kernel)
ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION bool dpd_active(IA_parameters const &ia_params, int thermo_switch)
bool has_exclusion(std::size_t i) const
PositionViewType position
Utils::Vector< T, N > get_vector_at(Kokkos::View< T *[N], array_layout, Kokkos::HostSpace > const &view, std::size_t i) const
DirectorViewType director
VelocityViewType velocity
P3M solver.
Definition p3m.hpp:55
Utils::Vector3d pair_force(double q1q2, Utils::Vector3d const &d, double dist) const
Calculate real-space contribution of p3m Coulomb pair forces.
Definition p3m.hpp:145
Solver::ShortRangeEnergyKernel kernel_type
Solver::ShortRangeForceCorrectionsKernel kernel_type
Solver::ShortRangeForceKernel kernel_type
std::unique_ptr< Implementation > impl
Pointer-to-implementation.
Solver::ShortRangeForceKernel kernel_type
std::vector< Particle * > const & unique_particles
CellStructure::ScatterForce local_force
Coulomb::ShortRangeEnergyKernel::kernel_type const *const coulomb_u_kernel
ForcesKernel(BondedInteractionsMap const &bonded_ias_, InteractionsNonBonded const &nonbonded_ias_, Coulomb::ShortRangeForceKernel::kernel_type const *coulomb_kernel_, Dipoles::ShortRangeForceKernel::kernel_type const *dipoles_kernel_, Coulomb::ShortRangeForceCorrectionsKernel::kernel_type const *elc_kernel_, Coulomb::ShortRangeEnergyKernel::kernel_type const *coulomb_u_kernel_, Coulomb::Solver const &coulomb_, Thermostat::Thermostat const &thermostat_, BoxGeometry const &box_geo_, std::vector< Particle * > const &unique_particles_, CellStructure::ScatterForce local_force_, CellStructure::ScatterForce local_torque_, Utils::Vector3d *const global_virial_, CellStructure::ScatterVirial local_virial_, CellStructure::AoSoA_pack const &aosoa_, double system_max_cutoff_)
Coulomb::ShortRangeForceCorrectionsKernel::kernel_type const * elc_kernel
ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION bool npt_active() const
CellStructure::ScatterVirial local_virial
BoxGeometry const & box_geo
double system_max_cutoff_sq
Dipoles::ShortRangeForceKernel::kernel_type const *const dipoles_kernel
CellStructure::AoSoA_pack const & aosoa
CellStructure::ScatterForce local_torque
InteractionsNonBonded const & nonbonded_ias
Utils::Vector3d *const global_virial
ESPRESSO_ATTR_ALWAYS_INLINE KOKKOS_INLINE_FUNCTION void operator()(std::size_t i, std::size_t j) const
BondedInteractionsMap const & bonded_ias
CoulombP3M const * p3m
Coulomb::ShortRangeForceKernel::kernel_type const *const coulomb_kernel
Thermostat::Thermostat const & thermostat
Force information on a particle.
Definition Particle.hpp:330
Struct holding all information for one particle.
Definition Particle.hpp:435
Utils::Vector3d thole_pair_force(Particle const &p1, Particle const &p2, IA_parameters const &ia_params, Utils::Vector3d const &d, double dist, BondedInteractionsMap const &bonded_ias, Coulomb::ShortRangeForceKernel::kernel_type const *kernel)
Calculate Thole force.
Definition thole.hpp:45