29#include "ParticleList.hpp"
39#include "system/Leaf.hpp"
43#include <boost/container/static_vector.hpp>
44#include <boost/iterator/indirect_iterator.hpp>
45#include <boost/range/algorithm/transform.hpp>
47#include <Cabana_Core.hpp>
48#include <Cabana_NeighborList.hpp>
49#include <Kokkos_Core.hpp>
50#include <Kokkos_ScatterView.hpp>
62#include <unordered_set>
66#ifdef ESPRESSO_CALIPER
67#include <caliper/cali.h>
74template <
typename Callable>
76 { c(p) } -> std::same_as<void>;
95#ifdef ESPRESSO_BOND_CONSTRAINT
99#ifdef ESPRESSO_ROTATION
103#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
121 auto first_non_empty = std::ranges::find_if(
122 cells, [](
const Cell *c) {
return not c->
particles().empty(); });
142struct MinimalImageDistance {
150struct EuclidianDistance {
171 Kokkos::View<double *[3], Kokkos::LayoutRight, memory_space>;
172 using VirialType = Kokkos::View<double[3], Kokkos::LayoutRight, memory_space>;
174 Kokkos::Experimental::ScatterView<
double *[3], Kokkos::LayoutRight,
177 Kokkos::Experimental::ScatterView<
double[3], Kokkos::LayoutRight,
182 Cabana::TeamVectorOpTag>;
186 std::vector<Particle *> m_particle_index;
188 std::unique_ptr<ParticleDecomposition> m_decomposition;
194 bool m_verlet_skin_set =
false;
195 bool m_rebuild_verlet_list =
true;
196 bool m_rebuild_verlet_list_cabana =
true;
197 std::vector<std::pair<Particle *, Particle *>> m_verlet_list;
198 double m_le_pos_offset_at_last_resort = 0.;
200 double m_verlet_skin = 0.;
201 double m_verlet_reuse = 0.;
202 int m_cached_max_local_particle_id = 0;
203 std::size_t m_num_local_particles_cached = 0;
205 std::unique_ptr<Kokkos::View<int *, memory_space>> m_id_to_index;
206 std::unique_ptr<ForceType> m_local_force;
207 std::optional<ScatterForce> m_scatter_force;
208#ifdef ESPRESSO_ROTATION
209 std::unique_ptr<ForceType> m_local_torque;
210 std::optional<ScatterForce> m_scatter_torque;
217 bool m_torque_replicas_dirty =
false;
219#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
220 std::unique_ptr<ForceType> m_local_dip_fld;
221 std::optional<ScatterForce> m_scatter_dip_fld;
223 bool m_dip_fld_replicas_dirty =
false;
226 std::unique_ptr<VirialType> m_local_virial;
227 std::optional<ScatterVirial> m_scatter_virial;
229 bool m_virial_replicas_dirty =
false;
231 std::unique_ptr<LocalBondState> m_bond_state;
232 std::unique_ptr<ListType> m_verlet_list_cabana;
255 mutable std::vector<Cell *> m_filtered_cells_scratch;
264 mutable std::optional<GhostComm::GhostExchange> m_pending_ghost_reduce;
266 std::unique_ptr<AoSoA_pack> m_aosoa;
267 std::vector<Particle *> m_unique_particles;
268 std::shared_ptr<KokkosHandle> m_kokkos_handle;
288 assert(not p or p->
id() ==
id);
290 if (
static_cast<unsigned int>(
id) >= m_particle_index.size())
291 m_particle_index.resize(
static_cast<unsigned int>(
id + 1));
293 m_particle_index[
static_cast<unsigned int>(id)] = p;
336 auto &new_part = pl.
insert(std::move(p));
358 if (
static_cast<unsigned int>(
id) >= m_particle_index.size())
361 return m_particle_index[
static_cast<unsigned int>(id)];
368 if (
static_cast<unsigned int>(
id) >= m_particle_index.size())
371 return m_particle_index[
static_cast<unsigned int>(id)];
374 template <
class InputRange,
class OutputIterator>
376 std::ranges::transform(ids, out,
397 std::size_t count = 0;
398 for (
auto const &cell : m_decomposition->local_cells()) {
399 count += cell->particles().size();
412 bool parallel =
true)
const {
414 parallel_for_each_particle_impl(
decomposition().local_cells(), f);
436 m_filtered_cells_scratch.clear();
437 for (
auto *c : all_cells) {
438 if (not c->is_boundary())
439 m_filtered_cells_scratch.push_back(c);
441 if (m_filtered_cells_scratch.empty())
443 std::span<Cell *const> span{m_filtered_cells_scratch};
445 parallel_for_each_particle_impl(span, f);
448 for (
auto &p : c->particles())
465 m_filtered_cells_scratch.clear();
466 for (
auto *c : all_cells) {
467 if (c->is_boundary())
468 m_filtered_cells_scratch.push_back(c);
470 if (m_filtered_cells_scratch.empty())
472 std::span<Cell *const> span{m_filtered_cells_scratch};
474 parallel_for_each_particle_impl(span, f);
477 for (
auto &p : c->particles())
506 inline void parallel_for_each_particle_impl(std::span<Cell *const> cells,
558 return m_cached_max_local_particle_id;
561 return m_num_local_particles_cached;
567#ifdef ESPRESSO_COLLISION_DETECTION
569 void add_new_bond(
int bond_id, std::vector<int>
const &particle_ids);
590 return assert(m_decomposition), *m_decomposition;
595 return assert(m_decomposition), *m_decomposition;
603 m_resort_particles |= level;
604 assert(m_resort_particles >= level);
629 return m_le_pos_offset_at_last_resort;
688 return m_pending_ghost_reduce.has_value();
691#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
706#ifdef ESPRESSO_BOND_CONSTRAINT
731 if (n_verlet_updates > 0) {
732 m_verlet_reuse = n_steps /
static_cast<double>(n_verlet_updates);
751 boost::container::static_vector<Particle *, 4> partners;
755 if (std::ranges::find(partners,
nullptr) != partners.end()) {
774 void execute_bond_handler(
Particle &p,
auto const &handler) {
775 for (
const BondView bond : p.bonds()) {
776 auto const partner_ids = bond.partner_ids();
779 auto const partners_span = std::span(partners.data(), partners.size());
780 auto const bond_broken = handler(p, bond.bond_id(), partners_span);
794 void invalidate_ghosts() {
803 void set_particle_decomposition(
805 assert(not m_pending_ghost_reduce.has_value() &&
806 "set_particle_decomposition: ghost force reduction is still in "
807 "flight — call ghosts_reduce_forces_finish() first");
833 std::optional<std::pair<int, int>> fully_connected_boundary);
842 std::set<int> n_square_types);
851 void link_cell(
auto kernel) {
854 auto const first = boost::make_indirect_iterator(local_cells_span.begin());
855 auto const last = boost::make_indirect_iterator(local_cells_span.end());
864 throw std::runtime_error(
"Non-cuboid box type is not compatible with a "
865 "particle decomposition that relies on "
866 "EuclideanDistance for distance calculation.");
870 [&kernel, df = detail::EuclidianDistance{}](
887#ifdef ESPRESSO_ROTATION
897#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
920 return m_rebuild_verlet_list_cabana;
931 int &dihedral_count,
Particle const &p);
954 if (rebuild_verlet_list) {
955 kernel(m_decomposition->local_cells(), m_decomposition->box(),
956 *m_verlet_list_cabana);
958 m_rebuild_verlet_list_cabana =
false;
964 kernel(m_decomposition->local_cells(), m_decomposition->box());
971 void reset_torque_replicas_if_dirty();
973 void reset_dip_fld_replicas_if_dirty();
975 void reset_virial_replicas_if_dirty();
982 template <
class PairKernel,
class VerletCriterion>
983 void verlet_list_loop(PairKernel pair_kernel,
988 if (m_rebuild_verlet_list) {
989 m_verlet_list.clear();
992 if (verlet_criterion(p1, p2, d.
dist2)) {
993 m_verlet_list.emplace_back(&p1, &p2);
994 pair_kernel(p1, p2, d);
998 m_rebuild_verlet_list =
false;
999 m_rebuild_verlet_list_cabana =
true;
1004 auto const distance_function =
1006 for (
auto const &[p1, p2] : m_verlet_list) {
1007 pair_kernel(*p1, *p2, distance_function(*p1, *p2));
1010 auto const distance_function = detail::EuclidianDistance{};
1011 for (
auto const &[p1, p2] : m_verlet_list) {
1012 pair_kernel(*p1, *p2, distance_function(*p1, *p2));
1022 template <
class BondKernel>
void bond_loop(BondKernel
const &bond_kernel) {
1024 execute_bond_handler(p, bond_kernel);
1032 link_cell(pair_kernel);
1040 template <
class PairKernel,
class VerletCriterion>
1044 verlet_list_loop(pair_kernel, verlet_criterion);
1047 link_cell(pair_kernel);
1087 return particle_to_cell(p);
1098 template <
class Kernel>
1103 if (cell ==
nullptr) {
1110 auto const distance_function =
1112 short_range_neighbor_loop(p, cell, kernel, distance_function);
1114 auto const distance_function = detail::EuclidianDistance{};
1115 short_range_neighbor_loop(p, cell, kernel, distance_function);
1121 template <
class Kernel,
class DistanceFunc>
1122 void short_range_neighbor_loop(
Particle const &p1,
Cell *
const cell,
1123 Kernel &kernel, DistanceFunc
const &df) {
1125 for (
auto const &p2 : cell->particles()) {
1126 if (p1.
id() != p2.
id()) {
1127 auto const vec = df(p1, p2).vec21;
1128 kernel(p1, p2, vec);
1132 for (
auto const neighbor : cell->neighbors().all()) {
1134 if (neighbor != cell) {
1135 for (
auto const &p2 : neighbor->
particles()) {
1136 auto const vec = df(p1, p2).vec21;
1137 kernel(p1, p2, vec);
ParticleIterator< std::span< Cell *const >::iterator > CellParticleIterator
CellStructureType
Cell structure topology.
@ NSQUARE
Atom decomposition (N-square).
unsigned map_data_parts(unsigned data_parts)
Map the data parts flags from cells to those used internally by the ghost communication.
Asynchronous, split-phase ghost-communication engine.
Vector implementation and trait types for boost qvm interoperability.
void bond_broken_error(int id, std::span< const int > partner_ids)
void bond_resolution_error(std::span< const int > partner_ids)
Immutable view on a bond.
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3< T > get_mi_vector(Utils::Vector3< T > const &a, Utils::Vector3< T > const &b) const noexcept
Get the minimum-image vector between two coordinates.
Describes a cell structure / cell system.
ParticleRange ghost_particles() const
Particle * get_local_particle(int id)
Get a local particle by id.
void for_each_local_particle(ParticleCallback auto &&f, bool parallel=true) const
Run a kernel on all local particles.
Kokkos::Experimental::ScatterView< double *[3], Kokkos::LayoutRight, memory_space > ScatterForce
void for_each_interior_particle(ParticleCallback auto &&f) const
Run a kernel on interior (non-boundary) local particles only.
void set_kokkos_handle(std::shared_ptr< KokkosHandle > handle)
void update_particle_index(ParticleList &pl)
Update local particle index.
void check_particle_sorting() const
Check that particles are in the correct cell.
void rebuild_verlet_list_cabana(auto &&kernel, bool rebuild_verlet_list)
std::size_t count_local_particles() const
auto get_scatter_virial()
auto const & bond_state() const
int get_local_angle_bond_numbers() const
void clear_resort_particles()
Set the resort level to sorted.
Cell * find_current_cell(const Particle &p)
Find cell a particle is stored in.
auto is_verlet_skin_set() const
Whether the Verlet skin is set.
void clear_local_properties()
ParticleDecomposition const & decomposition() const
Get the underlying particle decomposition.
void reset_local_force_buffers()
Zero the local force view and its scatter replicas.
int get_local_pair_bond_numbers() const
void ghosts_reduce_forces_start()
Begin the split-phase ghost force reduction (non-blocking).
void clear_particle_index()
Clear the particles index.
Kokkos::Experimental::ScatterView< double[3], Kokkos::LayoutRight, memory_space > ScatterVirial
auto prepare_verlet_list_cabana(double cutoff)
Reset local properties of the Verlet list.
void ghosts_reset_dipole_fields()
Set dipole fields on all ghosts to zero.
static constexpr auto vector_length
void update_ghosts_and_resort_particle(unsigned data_parts)
Update ghost particles, with particle resort if needed.
Particle * add_local_particle(Particle &&p)
Add a particle.
void set_verlet_skin_heuristic()
Set the Verlet skin using a heuristic.
void set_verlet_skin(double value)
Set the Verlet skin.
void ghosts_update(unsigned data_parts)
Update ghost particles.
auto get_le_pos_offset_at_last_resort() const
int get_local_dihedral_bond_numbers() const
Kokkos::HostSpace memory_space
int get_cached_max_local_particle_id() const
void get_local_particles(InputRange ids, OutputIterator out)
void update_verlet_stats(int n_steps, int n_verlet_updates)
Kokkos::View< double *[3], Kokkos::LayoutRight, memory_space > ForceType
auto & get_local_torque()
void ghosts_reset_forces()
Set forces and torques on all ghosts to zero.
Kokkos::View< double[3], Kokkos::LayoutRight, memory_space > VirialType
auto const & get_aosoa() const
auto & get_local_virial()
void update_particle_index(int id, Particle *p)
Update local particle index.
void set_local_bond_numbers(int p, int a, int d)
void mark_virial_replicas_dirty()
void ghosts_reduce_forces()
Add forces and torques from ghost particles to real particles.
auto const & get_unique_particles() const
unsigned get_resort_particles() const
Get the currently scheduled resort level.
auto get_verlet_reuse() const
Average number of integration steps the Verlet list was re-used.
void rebuild_local_properties(double pair_cutoff)
auto dip_fld_replicas_dirty() const
void non_bonded_loop(PairKernel pair_kernel)
Non-bonded pair loop.
bool has_pending_ghost_reduce() const
True when a split-phase force reduction is in flight.
auto get_scatter_dip_fld()
auto virial_replicas_dirty() const
Utils::Vector3d max_range() const
Maximal pair range supported by current cell system.
void add_new_bond(int bond_id, std::vector< int > const &particle_ids)
bool check_resort_required(Utils::Vector3d const &additional_offset={}) const
Check whether a particle has moved further than half the skin since the last Verlet list update,...
const Particle * get_local_particle(int id) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
auto resolve_bond_partners(std::span< const int > partner_ids)
Resolve ids to particles.
void bond_loop(BondKernel const &bond_kernel)
Bonded pair loop.
void ghosts_count()
Synchronize number of ghosts.
void set_resort_particles(Cells::Resort level)
Increase the local resort level at least to level.
void cell_list_loop(auto &&kernel)
void remove_particle(int id)
Remove a particle.
Particle * add_particle(Particle &&p)
Add a particle.
void for_each_boundary_particle(ParticleCallback auto &&f) const
Run a kernel on boundary local particles only.
auto torque_replicas_dirty() const
std::size_t get_num_local_particles_cached() const
Kokkos::DefaultHostExecutionSpace execution_space
void resort_particles(bool global_flag)
Resort particles.
void check_particle_index() const
Check that particle index is commensurate with particles.
void ghosts_reduce_forces_finish()
Complete the split-phase ghost force reduction.
void mark_dip_fld_replicas_dirty()
void ghosts_reduce_dipole_field()
Add dipole fields from ghost particles to real particles.
Cabana::HalfNeighborTag ListAlgorithm
auto get_scatter_torque()
auto get_verlet_skin() const
Get the Verlet skin.
void set_regular_decomposition(double range, std::optional< std::pair< int, int > > fully_connected_boundary)
Set the particle decomposition to RegularDecomposition.
void set_atom_decomposition()
Set the particle decomposition to AtomDecomposition.
auto const & get_verlet_list_cabana() const
bool run_on_particle_short_range_neighbors(Particle const &p, Kernel &kernel)
Run kernel on all particles inside local cell and its neighbors.
bool use_parallel_for_each_local_particle() const
whether to use parallel version of for_each_local_particle
auto & get_local_dip_fld()
void mark_torque_replicas_dirty()
Declare that a kernel scattering into the torque view is about to run.
void remove_all_particles()
Remove all particles from the cell system.
ParticleRange local_particles() const
void update_particle_index(Particle &p)
Update local particle index.
void ghosts_reduce_rattle_correction()
Add rattle corrections from ghost particles to real particles.
CellStructureType decomposition_type() const
auto is_verlet_list_cabana_rebuild_needed() const
void set_hybrid_decomposition(double cutoff_regular, std::set< int > n_square_types)
Set the particle decomposition to HybridDecomposition.
int get_max_local_particle_id() const
Get the maximal particle id on this node.
Utils::Vector3d max_cutoff() const
Maximal cutoff supported by current cell system.
void update_bond_storage(int &pair_count, int &angle_count, int &dihedral_count, Particle const &p)
Update bond storage(m_*_bond_list_kokkos and m_*_bond_id_kokkos).
void for_each_ghost_particle(ParticleCallback auto &&f) const
Run a kernel on all ghost particles.
void clear_bond_properties()
void non_bonded_loop(PairKernel pair_kernel, const VerletCriterion &verlet_criterion)
Non-bonded pair loop with potential use of verlet lists.
void reset_local_properties()
auto & particles()
Particles.
A distributed particle decomposition.
virtual Utils::Vector3d max_cutoff() const =0
Maximum supported cutoff.
virtual std::span< Cell *const > local_cells() const =0
Get pointer to local cells.
virtual Cell * particle_to_cell(Particle const &p)=0
Determine which cell a particle id belongs to.
virtual Utils::Vector3d max_range() const =0
Range in which calculations are performed.
virtual std::optional< BoxGeometry > minimum_image_distance() const =0
Return the box geometry needed for distance calculation if minimum image convention should be used ne...
virtual BoxGeometry const & box() const =0
Abstract class that represents a component of the system.
std::size_t capacity() const
Capacity of the container.
T & insert(T const &v)
Insert an element into the container.
std::size_t size() const
Number of elements in the container.
Returns true if the particles are to be considered for short range interactions.
Ghost particles and particle exchange.
void link_cell(CellIterator first, CellIterator last, PairKernel &&pair_kernel)
Iterates over all particles in the cell range, and over all pairs within the cells and with their nei...
DataPart
Flags to select particle parts for communication.
@ DATA_PART_MOMENTUM
Particle::m.
@ DATA_PART_DIPFLD
Particle::dip_fld.
@ DATA_PART_FORCE
Particle::f.
@ DATA_PART_TORQUE
torque (reduced with force)
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
@ DATA_PART_RATTLE
Particle::rattle.
@ DATA_PART_QUAT
orientation quaternion (pushed with position)
@ DATA_PART_POSITION
Particle::r.
ParticleRange particles(std::span< Cell *const > cells)
Exception indicating that a particle id could not be resolved.
Distance vector and length handed to pair kernels.
Distance(Utils::Vector3d const &vec21)
Persistent per-neighbor buffer pool for halo exchanges.
Struct holding all information for one particle.
constexpr auto const & dip_fld() const
constexpr auto const & pos() const
constexpr auto const & id() const
constexpr auto const & force_and_torque() const
constexpr bool is_ghost() const