35#include "communication.hpp"
44#include "system/System.hpp"
51#ifdef ESPRESSO_CALIPER
55#include <boost/mpi/collectives/all_reduce.hpp>
72#include <unordered_set>
78 assert(
not m_pending_ghost_reduce.has_value() &&
79 "~CellStructure: ghost force reduction still in flight at destruction "
80 "— ghosts_reduce_forces_finish() was not called");
83 m_kokkos_handle.reset();
87 m_scatter_force.reset();
88 m_local_force.reset();
89#ifdef ESPRESSO_ROTATION
90 m_scatter_torque.reset();
91 m_local_torque.reset();
93#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
94 m_scatter_dip_fld.reset();
95 m_local_dip_fld.reset();
98 m_scatter_virial.reset();
99 m_local_virial.reset();
101 m_id_to_index.reset();
103 m_verlet_list_cabana.reset();
104 m_bond_state->clear();
105 m_rebuild_verlet_list_cabana =
true;
110 m_kokkos_handle = std::move(
handle);
111 m_bond_state = std::make_unique<LocalBondState>();
132 (4. / 3.) * std::numbers::pi * Utils::int_pow<3>(
pair_cutoff);
145#ifdef ESPRESSO_CALIPER
154#ifdef ESPRESSO_COLLISION_DETECTION
155 if (
system.has_collision_detection_enabled()) {
166 reset_torque_replicas_if_dirty();
167 reset_dip_fld_replicas_if_dirty();
171 m_scatter_force.emplace(
173#ifdef ESPRESSO_ROTATION
176 m_scatter_torque.emplace(
178 m_torque_replicas_dirty =
false;
180#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
183 m_scatter_dip_fld.emplace(
185 m_dip_fld_replicas_dirty =
false;
200 m_local_force = std::make_unique<ForceType>(
"local_force",
num_part);
201 m_scatter_force.emplace(
202 Kokkos::Experimental::create_scatter_view(*m_local_force));
203#ifdef ESPRESSO_ROTATION
204 m_local_torque = std::make_unique<ForceType>(
"local_torque",
num_part);
205 m_scatter_torque.emplace(
206 Kokkos::Experimental::create_scatter_view(*m_local_torque));
208#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
209 m_local_dip_fld = std::make_unique<ForceType>(
"local_dip_fld",
num_part);
210 m_scatter_dip_fld.emplace(
211 Kokkos::Experimental::create_scatter_view(*m_local_dip_fld));
213 m_id_to_index = std::make_unique<Kokkos::View<int *, memory_space>>(
219 m_aosoa = std::make_unique<AoSoA_pack>();
223 m_verlet_list_cabana =
227 if (
not m_local_virial) {
228 m_local_virial = std::make_unique<VirialType>(
"local_virial");
229 m_scatter_virial.emplace(
230 Kokkos::Experimental::create_scatter_view(*m_local_virial));
232 reset_virial_replicas_if_dirty();
239 m_scatter_force->reset();
242void CellStructure::reset_torque_replicas_if_dirty() {
243#ifdef ESPRESSO_ROTATION
244 if (m_torque_replicas_dirty) {
246 m_scatter_torque->reset();
247 m_torque_replicas_dirty =
false;
252void CellStructure::reset_dip_fld_replicas_if_dirty() {
253#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
254 if (m_dip_fld_replicas_dirty) {
256 m_scatter_dip_fld->reset();
257 m_dip_fld_replicas_dirty =
false;
262void CellStructure::reset_virial_replicas_if_dirty() {
264 if (m_virial_replicas_dirty) {
266 m_scatter_virial->reset();
267 m_virial_replicas_dirty =
false;
273#ifdef ESPRESSO_CALIPER
277 reset_torque_replicas_if_dirty();
278 reset_dip_fld_replicas_if_dirty();
279 reset_virial_replicas_if_dirty();
286 auto &pair_list = m_bond_state->pair_list;
287 auto &pair_ids = m_bond_state->pair_ids;
288 auto &angle_list = m_bond_state->angle_list;
289 auto &angle_ids = m_bond_state->angle_ids;
290 auto &dihedral_list = m_bond_state->dihedral_list;
291 auto &dihedral_ids = m_bond_state->dihedral_ids;
293 auto const partner_ids =
bond.partner_ids();
297 auto p_index = Kokkos::atomic_fetch_add(&pair_count, 1);
302 auto a_index = Kokkos::atomic_fetch_add(&angle_count, 1);
308 auto d_index = Kokkos::atomic_fetch_add(&dihedral_count, 1);
322#ifdef ESPRESSO_CALIPER
325 auto &unique_particles = m_unique_particles;
326 unique_particles.clear();
332 m_bond_state->reset_counts();
346 unique_particles[index] = &p;
348 counts.max_id = std::max(p.
id(), counts.max_id);
350 if (
not bond.partner_ids().empty()) {
351 auto const partner_ids =
bond.partner_ids();
352 if (partner_ids.size() == 1u) {
354 }
else if (partner_ids.size() == 2u) {
356 }
else if (partner_ids.size() == 3u) {
357 counts.dihedral += 1;
365 int dihedral_count = 0;
368 pair_count += counts.pair;
369 angle_count += counts.angle;
370 dihedral_count += counts.dihedral;
374 m_bond_state->allocate();
384 unique_particles.emplace_back(&p);
388 m_cached_max_local_particle_id =
max_id;
389 m_num_local_particles_cached = unique_particles.size();
399 auto const id = p.id();
402 throw std::runtime_error(
"Particle id out of bounds.");
406 throw std::runtime_error(
"Invalid local particle index entry.");
416 throw std::runtime_error(
"local_particles part has corrupted id.");
422 throw std::runtime_error(
430 for (
auto const &p : cell->particles()) {
431 if (particle_to_cell(p) != cell) {
432 throw std::runtime_error(
"misplaced particle with id " +
433 std::to_string(p.id()));
441 for (
auto it = bl.begin();
it != bl.end();) {
451 auto &
parts = cell->particles();
453 if (
it->id() == id) {
466 auto const sort_cell = particle_to_cell(p);
468 return std::addressof(
469 append_indexed_particle(
sort_cell->particles(), std::move(p)));
476 auto const sort_cell = particle_to_cell(p);
485 return std::addressof(
486 append_indexed_particle(cell->particles(), std::move(p)));
490 auto it = std::ranges::find_if(std::ranges::views::reverse(m_particle_index),
491 [](
auto const *p) {
return p !=
nullptr; });
493 return (
it != m_particle_index.rend()) ? (*it)->id() : -1;
497 return m_bond_state->pair_count;
500 return m_bond_state->angle_count;
503 return m_bond_state->dihedral_count;
509#ifdef ESPRESSO_COLLISION_DETECTION
520 cell->particles().clear();
523 m_particle_index.clear();
532 using namespace Cells;
555#ifdef ESPRESSO_CALIPER
565#ifdef ESPRESSO_CALIPER
576#ifdef ESPRESSO_CALIPER
585#ifdef ESPRESSO_CALIPER
599 assert(
not m_pending_ghost_reduce.has_value() &&
600 "ghosts_reduce_forces_start: a reduction is already in flight");
608#ifdef ESPRESSO_CALIPER
615 assert(m_pending_ghost_reduce.has_value() &&
616 "ghosts_reduce_forces_finish: no reduction is in flight");
624 m_pending_ghost_reduce.reset();
625#ifdef ESPRESSO_CALIPER
631#ifdef ESPRESSO_CALIPER
636 m_pending_ghost_reduce.reset();
638#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
645#ifdef ESPRESSO_BOND_CONSTRAINT
647#ifdef ESPRESSO_CALIPER
671#ifdef ESPRESSO_CALIPER
674 assert(
not m_pending_ghost_reduce.has_value() &&
675 "resort_particles: ghost force reduction is still in flight — "
676 "call ghosts_reduce_forces_finish() first");
679 std::vector<ParticleChange>
diff;
683 for (
auto d :
diff) {
684 std::visit(UpdateParticleIndexVisitor{
this}, d);
688 m_rebuild_verlet_list =
true;
689 m_rebuild_verlet_list_cabana =
true;
690 m_le_pos_offset_at_last_resort =
lebc.pos_offset;
692#ifdef ESPRESSO_ADDITIONAL_CHECKS
700 auto &local_geo = *
system.local_geo;
701 auto const &box_geo = *
system.box_geo;
702 set_particle_decomposition(
703 std::make_unique<AtomDecomposition>(
::comm_cart, box_geo));
705 local_geo.set_cell_structure_type(m_type);
706 system.on_cell_structure_change();
710 double range, std::optional<std::pair<int, int>> fully_connected_boundary) {
712 auto &local_geo = *
system.local_geo;
713 auto const &box_geo = *
system.box_geo;
714 set_particle_decomposition(std::make_unique<RegularDecomposition>(
717 local_geo.set_cell_structure_type(m_type);
718 system.on_cell_structure_change();
724 auto &local_geo = *
system.local_geo;
725 auto const &box_geo = *
system.box_geo;
726 set_particle_decomposition(std::make_unique<HybridDecomposition>(
728 [&
system]() {
return system.get_global_ghost_flags(); }, box_geo,
731 local_geo.set_cell_structure_type(m_type);
732 system.on_cell_structure_change();
737 m_verlet_skin = value;
738 m_verlet_skin_set =
true;
739 m_rebuild_verlet_list_cabana =
true;
745 auto const max_cut =
get_system().maximal_cutoff();
747 throw std::runtime_error(
748 "cannot automatically determine skin, please set it manually");
758#ifdef ESPRESSO_CALIPER
766 ::comm_cart, m_resort_particles, std::bit_or<unsigned>());
797 if ((p.pos() - p.pos_at_last_verlet_update()).norm2() >
lim) {
@ NSQUARE
Atom decomposition (N-square).
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
static cali_id_t ghost_reduce_async_attr()
unsigned map_data_parts(unsigned data_parts)
Map the data parts flags from cells to those used internally by the ghost communication.
static auto estimate_max_counts(double pair_cutoff, std::size_t number_of_unique_particles, double local_box_volume, std::size_t num_local_particles)
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_resolution_error(std::span< const int > partner_ids)
Zero-overhead Caliper guards for the inactive (no CALI_CONFIG) case.
bool espresso_cali_active() noexcept
Return true if Caliper is configured for this process.
#define ESPRESSO_CALI_MARK_FUNCTION
Guarded drop-in replacement for CALI_CXX_MARK_FUNCTION.
Atom decomposition cell system.
Describes a cell structure / cell system.
ParticleRange ghost_particles() const
Particle * get_local_particle(int id)
Get a local particle by id.
void set_kokkos_handle(std::shared_ptr< KokkosHandle > handle)
void check_particle_sorting() const
Check that particles are in the correct cell.
std::size_t count_local_particles() const
int get_local_angle_bond_numbers() const
void clear_resort_particles()
Set the resort level to sorted.
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 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.
int get_local_dihedral_bond_numbers() const
int get_cached_max_local_particle_id() const
CellStructure(BoxGeometry const &box)
auto & get_local_torque()
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 ghosts_reduce_forces()
Add forces and torques from ghost particles to real particles.
auto const & get_unique_particles() const
void rebuild_local_properties(double pair_cutoff)
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,...
auto resolve_bond_partners(std::span< const int > partner_ids)
Resolve ids to particles.
void ghosts_count()
Synchronize number of ghosts.
void set_resort_particles(Cells::Resort level)
Increase the local resort level at least to level.
void remove_particle(int id)
Remove a particle.
Particle * add_particle(Particle &&p)
Add a particle.
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 ghosts_reduce_dipole_field()
Add dipole fields from ghost particles to real particles.
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 & get_local_dip_fld()
void remove_all_particles()
Remove all particles from the cell system.
ParticleRange local_particles() const
void ghosts_reduce_rattle_correction()
Add rattle corrections from ghost particles to real particles.
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 clear_bond_properties()
void reset_local_properties()
virtual std::span< Cell *const > local_cells() const =0
Get pointer to local cells.
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
boost::mpi::communicator comm_cart
The communicator.
Ghost particles and particle exchange.
@ GHOSTTRANS_MOMENTUM
transfer ParticleMomentum
@ GHOSTTRANS_RATTLE
transfer ParticleRattle
@ GHOSTTRANS_QUAT
transfer orientation quaternion (pushed with position; runtime-conditional)
@ GHOSTTRANS_DIPFLD
transfer dipole field tracking data
@ GHOSTTRANS_PARTNUM
resize the receiver particle arrays to the size of the senders
@ GHOSTTRANS_POSITION
transfer ParticlePosition
@ GHOSTTRANS_PROPRTS
transfer ParticleProperties
@ GHOSTTRANS_FORCE
transfer ParticleForce
@ GHOSTTRANS_TORQUE
transfer torque (reduced with force; runtime-conditional)
ESPRESSO_ATTR_ALWAYS_INLINE void kokkos_deep_copy(auto const &exec_space, auto const &view, auto const &value)
Wrapper for Kokkos::deep_copy that skips fork/join when the number of threads is 1.
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
void halo_exchange_finish(GhostExchange &st)
Complete a halo exchange: run same-rank copies (overlapping the in-flight messages),...
void halo_exchange(HaloPlan const &plan, BoxGeometry const &box, unsigned data_parts, ExchangeOp op, ExchangeBuffers &bufs)
Blocking wrapper using a caller-owned buffer pool (no per-call alloc after warm-up).
GhostExchange halo_exchange_start(HaloPlan const &plan, BoxGeometry const &box, unsigned data_parts, ExchangeOp op, ExchangeBuffers &bufs)
Begin a halo exchange using a caller-owned buffer pool.
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
bool contains(Range &&rng, T const &value)
Check whether a range contains a value.
void enumerate_local_particles(CellStructure const &cs, Kernel &&kernel)
Run a kernel on all local particles with enumeration.
void clear_particle_node()
Invalidate particle_node.
Particles creation and deletion.
Exception indicating that a particle id could not be resolved.
Struct holding all information for one particle.
constexpr auto const & bonds() const
constexpr auto const & id() const
Apply a ParticleChange to a particle index.
void operator()(RemovedParticle rp) const
void operator()(ModifiedList mp) const