ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
CellStructure.hpp
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#pragma once
23
25
26#include "BoxGeometry.hpp"
27#include "LocalBox.hpp"
28#include "Particle.hpp"
29#include "ParticleList.hpp"
30#include "ParticleRange.hpp"
32#include "bond_error.hpp"
33#include "cell_system/Cell.hpp"
35#include "config/config.hpp"
37#include "ghosts.hpp"
38#include "system/Leaf.hpp"
39
40#include <utils/Vector.hpp>
41
42#include <boost/container/static_vector.hpp>
43#include <boost/iterator/indirect_iterator.hpp>
44#include <boost/range/algorithm/transform.hpp>
45
46#include <Cabana_Core.hpp>
47#include <Cabana_NeighborList.hpp>
48#include <Kokkos_Core.hpp>
49#include <Kokkos_ScatterView.hpp>
50
51#include <algorithm>
52#include <cassert>
53#include <concepts>
54#include <cstddef>
55#include <iterator>
56#include <memory>
57#include <optional>
58#include <set>
59#include <span>
60#include <stdexcept>
61#include <unordered_set>
62#include <utility>
63#include <vector>
64
65#ifdef ESPRESSO_CALIPER
66#include <caliper/cali.h>
67#endif
68
69// forward declarations
70struct KokkosHandle;
71struct LocalBondState;
72
73template <typename Callable>
74concept ParticleCallback = requires(Callable c, Particle &p) {
75 { c(p) } -> std::same_as<void>;
76};
77
78namespace Cells {
79enum Resort : unsigned {
82 RESORT_GLOBAL = 2u
83};
84
85/**
86 * @brief Flags to select particle parts for communication.
87 */
88enum DataPart : unsigned {
89 DATA_PART_NONE = 0u, /**< Nothing */
90 DATA_PART_PROPERTIES = 1u, /**< Particle::p */
91 DATA_PART_POSITION = 2u, /**< Particle::r */
92 DATA_PART_MOMENTUM = 8u, /**< Particle::m */
93 DATA_PART_FORCE = 16u, /**< Particle::f */
94#ifdef ESPRESSO_BOND_CONSTRAINT
95 DATA_PART_RATTLE = 32u, /**< Particle::rattle */
96#endif
97 DATA_PART_BONDS = 64u, /**< Particle::bonds */
98#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
99 DATA_PART_DIPFLD = 128u, /**< Particle::dip_fld */
100#endif
101};
102} // namespace Cells
103
104/**
105 * @brief Map the data parts flags from cells to those
106 * used internally by the ghost communication.
107 *
108 * @param data_parts data parts flags
109 * @return ghost communication flags
110 */
111unsigned map_data_parts(unsigned data_parts);
112
113namespace Cells {
114inline ParticleRange particles(std::span<Cell *const> cells) {
115 /* Find first non-empty cell */
116 auto first_non_empty = std::ranges::find_if(
117 cells, [](const Cell *c) { return not c->particles().empty(); });
118
119 return {CellParticleIterator(first_non_empty, cells.end()),
120 CellParticleIterator(cells.end())};
121}
122} // namespace Cells
123
124/**
125 * @brief Distance vector and length handed to pair kernels.
126 */
127struct Distance {
129 : vec21(vec21), dist2(vec21.norm2()) {}
130
132 double dist2;
133};
134
135namespace detail {
136// NOLINTNEXTLINE(bugprone-exception-escape)
137struct MinimalImageDistance {
138 BoxGeometry const box;
139
140 Distance operator()(Particle const &p1, Particle const &p2) const {
141 return Distance(box.get_mi_vector(p1.pos(), p2.pos()));
142 }
143};
144
145struct EuclidianDistance {
146 Distance operator()(Particle const &p1, Particle const &p2) const {
147 return Distance(p1.pos() - p2.pos());
148 }
149};
150} // namespace detail
151
152/** Describes a cell structure / cell system. Contains information
153 * about the communication of cell contents (particles, ghosts, ...)
154 * between different nodes and the relation between particle
155 * positions and the cell system. All other properties of the cell
156 * system which are not common between different cell systems have to
157 * be stored in separate structures.
158 */
159class CellStructure : public System::Leaf<CellStructure> {
160public:
161 static constexpr auto vector_length = 1;
162 using memory_space = Kokkos::HostSpace;
163 using execution_space = Kokkos::DefaultHostExecutionSpace;
164 struct AoSoA_pack;
165 using ForceType =
166 Kokkos::View<double *[3], Kokkos::LayoutRight, memory_space>;
167 using VirialType = Kokkos::View<double[3], Kokkos::LayoutRight, memory_space>;
169 Kokkos::Experimental::ScatterView<double *[3], Kokkos::LayoutRight,
172 Kokkos::Experimental::ScatterView<double[3], Kokkos::LayoutRight,
174 using ListAlgorithm = Cabana::HalfNeighborTag;
175 using ListType =
176 CustomVerletList<memory_space, ListAlgorithm, Cabana::VerletLayout2D,
177 Cabana::TeamVectorOpTag>;
178
179private:
180 /** The local id-to-particle index */
181 std::vector<Particle *> m_particle_index;
182 /** Implementation of the primary particle decomposition */
183 std::unique_ptr<ParticleDecomposition> m_decomposition;
184 /** Active type in m_decomposition */
186 /** One of @ref Cells::Resort, announces the level of resort needed.
187 */
188 unsigned m_resort_particles = Cells::RESORT_NONE;
189 bool m_verlet_skin_set = false;
190 bool m_rebuild_verlet_list = true;
191 bool m_rebuild_verlet_list_cabana = true;
192 std::vector<std::pair<Particle *, Particle *>> m_verlet_list;
193 double m_le_pos_offset_at_last_resort = 0.;
194 /** @brief Verlet list skin. */
195 double m_verlet_skin = 0.;
196 double m_verlet_reuse = 0.;
197 int m_cached_max_local_particle_id = 0;
198 std::size_t m_num_local_particles_cached = 0;
199 int m_max_id = 0;
200 std::unique_ptr<Kokkos::View<int *, memory_space>> m_id_to_index;
201 std::unique_ptr<ForceType> m_local_force;
202 std::optional<ScatterForce> m_scatter_force;
203#ifdef ESPRESSO_ROTATION
204 std::unique_ptr<ForceType> m_local_torque;
205 std::optional<ScatterForce> m_scatter_torque;
206#endif
207#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
208 std::unique_ptr<ForceType> m_local_dip_fld;
209 std::optional<ScatterForce> m_scatter_dip_fld;
210#endif
211#ifdef ESPRESSO_NPT
212 std::unique_ptr<VirialType> m_local_virial;
213 std::optional<ScatterVirial> m_scatter_virial;
214#endif
215 std::unique_ptr<LocalBondState> m_bond_state;
216 std::unique_ptr<ListType> m_verlet_list_cabana;
217 /** particle properties using individual Kokkos Views */
218 std::unique_ptr<AoSoA_pack> m_aosoa;
219 std::vector<Particle *> m_unique_particles;
220 std::shared_ptr<KokkosHandle> m_kokkos_handle;
221
222public:
223 CellStructure(BoxGeometry const &box);
224 virtual ~CellStructure();
225
226 bool use_verlet_list = true;
227
228 /**
229 * @brief Update local particle index.
230 *
231 * Update the entry for a particle in the local particle
232 * index.
233 *
234 * @param id Entry to update.
235 * @param p Pointer to the particle.
236 */
238 assert(id >= 0);
239 // cppcheck-suppress assertWithSideEffect
240 assert(not p or p->id() == id);
241
242 if (static_cast<unsigned int>(id) >= m_particle_index.size())
243 m_particle_index.resize(static_cast<unsigned int>(id + 1));
244
245 m_particle_index[static_cast<unsigned int>(id)] = p;
246 }
247
248 /**
249 * @brief Update local particle index.
250 *
251 * Update the entry for a particle in the local particle
252 * index.
253 *
254 * @param p Pointer to the particle.
255 */
257 update_particle_index(p.id(), std::addressof(p));
258 }
259
260 /**
261 * @brief Update local particle index.
262 *
263 * @param pl List of particles whose index entries should be updated.
264 */
266 for (auto &p : pl) {
267 update_particle_index(p.id(), std::addressof(p));
268 }
269 }
270
271 /**
272 * @brief Clear the particles index.
273 */
274 void clear_particle_index() { m_particle_index.clear(); }
275
276private:
277 /**
278 * @brief Append a particle to a list and update this
279 * particle index accordingly.
280 * @param pl List to add the particle to.
281 * @param p Particle to add.
282 */
283 Particle &append_indexed_particle(ParticleList &pl, Particle &&p) {
284 /* Check if cell may reallocate, in which case the index
285 * entries for all particles in this cell have to be
286 * updated. */
287 auto const may_reallocate = pl.size() >= pl.capacity();
288 auto &new_part = pl.insert(std::move(p));
289
290 if (may_reallocate)
292 else {
293 update_particle_index(new_part);
294 }
295
296 return new_part;
297 }
298
299public:
300 /**
301 * @brief Get a local particle by id.
302 *
303 * @param id Particle to get.
304 * @return Pointer to particle if it is local,
305 * nullptr otherwise.
306 */
308 assert(id >= 0);
309
310 if (static_cast<unsigned int>(id) >= m_particle_index.size())
311 return nullptr;
312
313 return m_particle_index[static_cast<unsigned int>(id)];
314 }
315
316 /** @overload */
317 const Particle *get_local_particle(int id) const {
318 assert(id >= 0);
319
320 if (static_cast<unsigned int>(id) >= m_particle_index.size())
321 return nullptr;
322
323 return m_particle_index[static_cast<unsigned int>(id)];
324 }
325
326 template <class InputRange, class OutputIterator>
328 std::ranges::transform(ids, out,
329 [this](int id) { return get_local_particle(id); });
330 }
331
332 CellStructureType decomposition_type() const { return m_type; }
333
334 /** Maximal cutoff supported by current cell system. */
336
337 /** Maximal pair range supported by current cell system. */
339
341 return Cells::particles(decomposition().local_cells());
342 }
343
345 return Cells::particles(decomposition().ghost_cells());
346 }
347
348 std::size_t count_local_particles() const {
349 std::size_t count = 0;
350 for (auto const &cell : m_decomposition->local_cells()) {
351 count += cell->particles().size();
352 }
353 return count;
354 }
355
356 /** @brief whether to use parallel version of @ref for_each_local_particle */
357 bool use_parallel_for_each_local_particle() const { return true; }
358
359 /**
360 * @brief Run a kernel on all local particles.
361 * The kernel is assumed to be thread-safe.
362 */
363 template <typename Callable>
364 void for_each_local_particle(Callable &&f, bool parallel = true) const {
366 parallel_for_each_particle_impl(decomposition().local_cells(), f);
367 return;
368 }
369 for (auto &p : local_particles()) {
370 f(p);
371 }
372 }
373
374 /**
375 * @brief Run a kernel on all ghost particles.
376 * The kernel is assumed to be thread-safe.
377 */
378 template <typename Callable>
380 for (auto &p : ghost_particles()) {
381 f(p);
382 }
383 }
384
385private:
386 /** Cell system dependent function to find the right cell for a
387 * particle.
388 * \param p Particle.
389 * \return pointer to cell where to put the particle, nullptr
390 * if the particle does not belong on this node.
391 */
392 Cell *particle_to_cell(const Particle &p) {
394 }
395 Cell const *particle_to_cell(const Particle &p) const {
397 }
398
399 template <typename Callable>
400 inline void parallel_for_each_particle_impl(std::span<Cell *const> cells,
401 Callable &f) const;
402
403public:
404 /**
405 * @brief Add a particle.
406 *
407 * Moves a particle into the cell system. This adds
408 * a particle to the local node, irrespective of where
409 * it belongs.
410 *
411 * @param p Particle to add.
412 * @return Pointer to the particle in the cell
413 * system.
414 */
416
417 /**
418 * @brief Add a particle.
419 *
420 * Moves a particle into the cell system, if it
421 * belongs to this node. Otherwise this does not
422 * have an effect and the particle is discarded.
423 * This can be used to add a particle without
424 * knowledge where it should be placed by calling
425 * the function on all nodes, it will then add
426 * the particle in exactly one place.
427 *
428 * @param p Particle to add.
429 * @return Pointer to particle if it is local, null
430 * otherwise.
431 */
433
434 /**
435 * @brief Remove a particle.
436 *
437 * Removes a particle and all bonds pointing
438 * to it. This is a collective call.
439 *
440 * @param id Id of particle to remove.
441 */
442 void remove_particle(int id);
443
444 /**
445 * @brief Get the maximal particle id on this node.
446 *
447 * This returns the highest particle id on
448 * this node, or -1 if there are no particles on this node.
449 */
450 int get_max_local_particle_id() const;
452 return m_cached_max_local_particle_id;
453 }
454 std::size_t get_num_local_particles_cached() const {
455 return m_num_local_particles_cached;
456 }
457 int get_local_pair_bond_numbers() const;
460 void set_local_bond_numbers(int p, int a, int d);
461#ifdef ESPRESSO_COLLISION_DETECTION
462 void clear_new_bonds();
463 void add_new_bond(int bond_id, std::vector<int> const &particle_ids);
464 void rebuild_bond_list();
465#endif // ESPRESSO_COLLISION_DETECTION
466
467 /**
468 * @brief Remove all particles from the cell system.
469 *
470 * This allows linear time removal of all particles from
471 * the system, removing each particle individually would
472 * be quadratic.
473 */
475
476 /**
477 * @brief Get the underlying particle decomposition.
478 *
479 * Should be used solely for informative purposes.
480 *
481 * @return The active particle decomposition.
482 */
484 return assert(m_decomposition), *m_decomposition;
485 }
486
487private:
489 return assert(m_decomposition), *m_decomposition;
490 }
491
492public:
493 /**
494 * @brief Increase the local resort level at least to @p level.
495 */
497 m_resort_particles |= level;
498 assert(m_resort_particles >= level);
499 }
500
501 /**
502 * @brief Get the currently scheduled resort level.
503 */
504 unsigned get_resort_particles() const { return m_resort_particles; }
505
506 /**
507 * @brief Set the resort level to sorted.
508 */
509 void clear_resort_particles() { m_resort_particles = Cells::RESORT_NONE; }
510
511 /**
512 * @brief Check whether a particle has moved further than half the skin
513 * since the last Verlet list update, thus requiring a resort.
514 * @param additional_offset Offset which is added to the distance the
515 * particle has travelled when comparing to half
516 * the Verlet skin (e.g., for Lees-Edwards BC).
517 * @return Whether a resort is needed.
518 */
519 bool
521
523 return m_le_pos_offset_at_last_resort;
524 }
525
526 /**
527 * @brief Synchronize number of ghosts.
528 */
529 void ghosts_count();
530
531 /**
532 * @brief Update ghost particles.
533 *
534 * Update ghost particles with data from the real particles.
535 *
536 * @param data_parts Particle parts to update, combination of @ref
537 * Cells::DataPart
538 */
539 void ghosts_update(unsigned data_parts);
540
541 /**
542 * @brief Update ghost particles, with particle resort if needed.
543 *
544 * Update ghost particles with data from the real particles.
545 * Resort particles if a resort is due.
546 *
547 * @param data_parts Particle parts to update, combination of @ref
548 * Cells::DataPart
549 */
551
552 /**
553 * @brief Add forces and torques from ghost particles to real particles.
554 */
556
557#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
558 /** Add dipole fields from ghost particles to real particles. */
560
561 /** Set dipole fields on all ghosts to zero. */
563 for_each_ghost_particle([](Particle &p) { p.dip_fld() = {}; });
564 }
565#endif
566
567 /** Set forces and torques on all ghosts to zero. */
570 }
571
572#ifdef ESPRESSO_BOND_CONSTRAINT
573 /**
574 * @brief Add rattle corrections from ghost particles to real particles.
575 */
577#endif
578
579 /**
580 * @brief Resort particles.
581 */
582 void resort_particles(bool global_flag);
583
584 /** @brief Whether the Verlet skin is set. */
585 auto is_verlet_skin_set() const { return m_verlet_skin_set; }
586
587 /** @brief Get the Verlet skin. */
588 auto get_verlet_skin() const { return m_verlet_skin; }
589
590 /** @brief Set the Verlet skin. */
591 void set_verlet_skin(double value);
592
593 /** @brief Set the Verlet skin using a heuristic. */
595
597 if (n_verlet_updates > 0) {
598 m_verlet_reuse = n_steps / static_cast<double>(n_verlet_updates);
599 } else {
600 m_verlet_reuse = 0.;
601 }
602 }
603
604 /** @brief Average number of integration steps the Verlet list was re-used */
605 auto get_verlet_reuse() const { return m_verlet_reuse; }
606
607 /**
608 * @brief Resolve ids to particles.
609 *
610 * @throws BondResolutionError if one of the ids
611 * was not found.
612 *
613 * @param partner_ids Ids to resolve.
614 * @return Vector of Particle pointers.
615 */
616 auto resolve_bond_partners(std::span<const int> partner_ids) {
617 boost::container::static_vector<Particle *, 4> partners;
618 get_local_particles(partner_ids, std::back_inserter(partners));
619
620 /* Check if id resolution failed for any partner */
621 if (std::ranges::find(partners, nullptr) != partners.end()) {
622 throw BondResolutionError{};
623 }
624
625 return partners;
626 }
627
628private:
629 /**
630 * @brief Execute kernel for every bond on particle.
631 * @tparam Handler Callable, which can be invoked with
632 * (Particle, int, std::span<Particle *>),
633 * returning a bool.
634 * @param p Particles for whom the bonds are evaluated.
635 * @param handler is called for every bond, and handed
636 * p, the bond id and a span with the bond
637 * partners as arguments. Its return value
638 * should indicate if the bond was broken.
639 */
640 template <class Handler>
641 void execute_bond_handler(Particle &p, Handler const &handler) {
642 for (const BondView bond : p.bonds()) {
643 auto const partner_ids = bond.partner_ids();
644
645 try {
646 auto partners = resolve_bond_partners(partner_ids);
647 auto const partners_span = std::span(partners.data(), partners.size());
648 auto const bond_broken = handler(p, bond.bond_id(), partners_span);
649 if (bond_broken) {
650 bond_broken_error(p.id(), partner_ids);
651 }
652 } catch (BondResolutionError const &) {
653 bond_resolution_error(partner_ids);
654 }
655 }
656 }
657
658 /**
659 * @brief Go through ghost cells and remove the ghost entries from the
660 * local particle index.
661 */
662 void invalidate_ghosts() {
663 for (auto const &p : ghost_particles()) {
664 if (get_local_particle(p.id()) == &p) {
665 update_particle_index(p.id(), nullptr);
666 }
667 }
668 }
669
670 /** @brief Set the particle decomposition, keeping the particles. */
671 void set_particle_decomposition(
672 std::unique_ptr<ParticleDecomposition> &&decomposition) {
674
675 /* Swap in new cell system */
676 std::swap(m_decomposition, decomposition);
677
678 /* Add particles to new system */
679 for (auto &p : Cells::particles(decomposition->local_cells())) {
680 add_particle(std::move(p));
681 }
682 }
683
684public:
685 /**
686 * @brief Set the particle decomposition to @ref AtomDecomposition.
687 */
689
690 /**
691 * @brief Set the particle decomposition to @ref RegularDecomposition.
692 *
693 * @param range Interaction range.
694 * @param fully_connected_boundary neighbor cell directions for Lees-Edwards.
695 */
697 double range,
698 std::optional<std::pair<int, int>> fully_connected_boundary);
699
700 /**
701 * @brief Set the particle decomposition to @ref HybridDecomposition.
702 *
703 * @param cutoff_regular Interaction cutoff_regular.
704 * @param n_square_types Particle types to put into n_square decomposition.
705 */
707 std::set<int> n_square_types);
708
709private:
710 /**
711 * @brief Run link_cell algorithm for local cells.
712 *
713 * @tparam Kernel Needs to be callable with (Particle, Particle, Distance).
714 * @param kernel Pair kernel functor.
715 */
716 template <class Kernel> void link_cell(Kernel kernel) {
719 auto const first = boost::make_indirect_iterator(local_cells_span.begin());
720 auto const last = boost::make_indirect_iterator(local_cells_span.end());
721
722 if (maybe_box) {
724 first, last,
725 [&kernel, df = detail::MinimalImageDistance{decomposition().box()}](
726 Particle &p1, Particle &p2) { kernel(p1, p2, df(p1, p2)); });
727 } else {
728 if (decomposition().box().type() != BoxType::CUBOID) {
729 throw std::runtime_error("Non-cuboid box type is not compatible with a "
730 "particle decomposition that relies on "
731 "EuclideanDistance for distance calculation.");
732 }
734 first, last,
735 [&kernel, df = detail::EuclidianDistance{}](
736 Particle &p1, Particle &p2) { kernel(p1, p2, df(p1, p2)); });
737 }
738 }
739
740public:
741 auto get_max_id() const { return m_max_id; }
742
743 void set_kokkos_handle(std::shared_ptr<KokkosHandle> handle);
747
748 auto &get_id_to_index() { return *m_id_to_index; }
749 auto &get_local_force() { return *m_local_force; }
750 auto get_scatter_force() { return *m_scatter_force; }
751#ifdef ESPRESSO_ROTATION
752 auto &get_local_torque() { return *m_local_torque; }
753 auto get_scatter_torque() { return *m_scatter_torque; }
754#endif
755#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
756 auto &get_local_dip_fld() { return *m_local_dip_fld; }
757 auto get_scatter_dip_fld() { return *m_scatter_dip_fld; }
758#endif
759#ifdef ESPRESSO_NPT
760 auto &get_local_virial() { return *m_local_virial; }
761 auto get_scatter_virial() { return *m_scatter_virial; }
762#endif
763
764 auto &get_aosoa() { return *m_aosoa; }
765 auto const &get_aosoa() const { return *m_aosoa; }
766 auto const &get_unique_particles() const { return m_unique_particles; }
767 auto const &get_verlet_list_cabana() const { return *m_verlet_list_cabana; }
768 auto &bond_state() { return *m_bond_state; }
769 auto const &bond_state() const { return *m_bond_state; }
772
774 return m_rebuild_verlet_list_cabana;
775 }
776
777 /**
778 * @brief Update bond storage(m_*_bond_list_kokkos and m_*_bond_id_kokkos).
779 * @param pair_count Index for pair bond storage.
780 * @param angle_count Index for angle bond storage.
781 * @param dihedral_count Index for dihedral bond storage.
782 * @param p Particle pointer.
783 */
784 void update_bond_storage(int &pair_count, int &angle_count,
785 int &dihedral_count, Particle const &p);
786
787 /**
788 * @brief Reset local properties of the Verlet list.
789 * @param cutoff Pair interaction cutoff.
790 * @return True if a rebuild is needed.
791 */
792 [[nodiscard]] auto prepare_verlet_list_cabana(double cutoff) {
793 auto const rebuild = is_verlet_list_cabana_rebuild_needed();
794 if (rebuild) {
795 // If we have to rebuild, we need to count the particles
796 set_index_map(); // parallelized index_map
797 // Create essential variables for MD
799 } else {
800 // If we do not rebuild we can use the saved map
802 }
803 return rebuild;
804 }
805
809 kernel(m_decomposition->local_cells(), m_decomposition->box(),
810 *m_verlet_list_cabana);
811 }
812 m_rebuild_verlet_list_cabana = false;
813 }
814
815 void set_index_map();
816
817 inline void cell_list_loop(auto &&kernel) {
818 kernel(m_decomposition->local_cells(), m_decomposition->box());
819 }
820
821private:
822 /** Non-bonded pair loop with verlet lists.
823 *
824 * @param pair_kernel Kernel to apply
825 * @param verlet_criterion Filter for verlet lists.
826 */
827 template <class PairKernel, class VerletCriterion>
828 void verlet_list_loop(PairKernel pair_kernel,
830 /* In this case the verlet list update is attached to
831 * the pair kernel, and the verlet list is rebuilt as
832 * we go. */
833 if (m_rebuild_verlet_list) {
834 m_verlet_list.clear();
835
836 link_cell([&](Particle &p1, Particle &p2, Distance const &d) {
837 if (verlet_criterion(p1, p2, d.dist2)) {
838 m_verlet_list.emplace_back(&p1, &p2);
839 pair_kernel(p1, p2, d);
840 }
841 });
842
843 m_rebuild_verlet_list = false;
844 m_rebuild_verlet_list_cabana = true;
845 } else {
847 /* In this case the pair kernel is just run over the verlet list. */
848 if (maybe_box) {
849 auto const distance_function =
850 detail::MinimalImageDistance{decomposition().box()};
851 for (auto const &[p1, p2] : m_verlet_list) {
853 }
854 } else {
855 auto const distance_function = detail::EuclidianDistance{};
856 for (auto const &[p1, p2] : m_verlet_list) {
858 }
859 }
860 }
861 }
862
863public:
864 /** Bonded pair loop.
865 * @param bond_kernel Kernel to apply
866 */
867 template <class BondKernel> void bond_loop(BondKernel const &bond_kernel) {
868 for (auto &p : local_particles()) {
869 execute_bond_handler(p, bond_kernel);
870 }
871 }
872
873 /** Non-bonded pair loop.
874 * @param pair_kernel Kernel to apply
875 */
876 template <class PairKernel> void non_bonded_loop(PairKernel pair_kernel) {
877 link_cell(pair_kernel);
878 }
879
880 /** Non-bonded pair loop with potential use
881 * of verlet lists.
882 * @param pair_kernel Kernel to apply
883 * @param verlet_criterion Filter for verlet lists.
884 */
885 template <class PairKernel, class VerletCriterion>
888 if (use_verlet_list) {
889 verlet_list_loop(pair_kernel, verlet_criterion);
890 } else {
891 /* No verlet lists, just run the kernel with pairs from the cells. */
892 link_cell(pair_kernel);
893 }
894 }
895
896 /**
897 * @brief Check that particle index is commensurate with particles.
898 *
899 * For each local particles is checked that has a correct entry
900 * in the particles index, and that there are no excess (non-existing)
901 * particles in the index.
902 */
903 void check_particle_index() const;
904
905 /**
906 * @brief Check that particles are in the correct cell.
907 *
908 * This checks for all local particles that the result
909 * of particles_to_cell is the cell the particles is
910 * actually in, e.g. that the particles are sorted according
911 * to particles_to_cell.
912 */
913 void check_particle_sorting() const;
914
915public:
916 /**
917 * @brief Find cell a particle is stored in.
918 *
919 * For local particles, this returns the cell they
920 * are stored in, otherwise nullptr is returned.
921 *
922 * @param p Particle to find cell for
923 * @return Cell for particle or nullptr.
924 */
927
928 if (p.is_ghost()) {
929 return nullptr;
930 }
931
932 return particle_to_cell(p);
933 }
934
935 /**
936 * @brief Run kernel on all particles inside local cell and its neighbors.
937 *
938 * @param p Particle to find cell for
939 * @param kernel Function with signature <tt>double(Particle const&,
940 * Particle const&, Utils::Vector3d const&)</tt>
941 * @return false if cell is not found, otherwise true
942 */
943 template <class Kernel>
945 Kernel &kernel) {
946 auto const cell = find_current_cell(p);
947
948 if (cell == nullptr) {
949 return false;
950 }
951
953
954 if (maybe_box) {
955 auto const distance_function =
956 detail::MinimalImageDistance{decomposition().box()};
957 short_range_neighbor_loop(p, cell, kernel, distance_function);
958 } else {
959 auto const distance_function = detail::EuclidianDistance{};
960 short_range_neighbor_loop(p, cell, kernel, distance_function);
961 }
962 return true;
963 }
964
965private:
966 template <class Kernel, class DistanceFunc>
967 void short_range_neighbor_loop(Particle const &p1, Cell *const cell,
968 Kernel &kernel, DistanceFunc const &df) {
969 /* Iterate over particles inside cell */
970 for (auto const &p2 : cell->particles()) {
971 if (p1.id() != p2.id()) {
972 auto const vec = df(p1, p2).vec21;
973 kernel(p1, p2, vec);
974 }
975 }
976 /* Iterate over all neighbors */
977 for (auto const neighbor : cell->neighbors().all()) {
978 /* Iterate over particles in neighbors */
979 if (neighbor != cell) {
980 for (auto const &p2 : neighbor->particles()) {
981 auto const vec = df(p1, p2).vec21;
982 kernel(p1, p2, vec);
983 }
984 }
985 }
986 }
987};
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.
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.
Definition BondList.hpp:44
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.
BoxType type() const
Describes a cell structure / cell system.
ParticleRange ghost_particles() const
auto & get_local_force()
void for_each_ghost_particle(Callable &&f) const
Run a kernel on all ghost particles.
Particle * get_local_particle(int id)
Get a local particle by id.
Kokkos::Experimental::ScatterView< double *[3], Kokkos::LayoutRight, memory_space > ScatterForce
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)
auto & get_id_to_index()
std::size_t count_local_particles() const
auto get_scatter_virial()
auto const & bond_state() const
virtual ~CellStructure()
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 get_max_id() const
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.
int get_local_pair_bond_numbers() const
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.
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.
void for_each_local_particle(Callable &&f, bool parallel=true) const
Run a kernel on all local 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
void ghosts_reset_dipole_field()
Set dipole fields on all ghosts to zero.
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 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)
void non_bonded_loop(PairKernel pair_kernel)
Non-bonded pair loop.
auto get_scatter_dip_fld()
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.
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_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 reset_local_force_and_torque()
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 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 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()
Definition Cell.hpp:96
auto & particles()
Particles.
Definition Cell.hpp:103
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
A range of particles.
Abstract class that represents a component of the system.
std::size_t capacity() const
Capacity of the container.
Definition Bag.hpp:104
T & insert(T const &v)
Insert an element into the container.
Definition Bag.hpp:147
std::size_t size() const
Number of elements in the container.
Definition Bag.hpp:90
Returns true if the particles are to be considered for short range interactions.
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
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...
Definition link_cell.hpp:32
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_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
@ DATA_PART_NONE
Nothing.
@ DATA_PART_RATTLE
Particle::rattle.
@ DATA_PART_POSITION
Particle::r.
ParticleRange particles(std::span< Cell *const > cells)
auto constexpr new_part
Exception indicating that a particle id could not be resolved.
Distance vector and length handed to pair kernels.
Utils::Vector3d vec21
Distance(Utils::Vector3d const &vec21)
Struct holding all information for one particle.
Definition Particle.hpp:436
constexpr auto const & dip_fld() const
Definition Particle.hpp:590
constexpr auto const & id() const
Definition Particle.hpp:455
constexpr auto const & force_and_torque() const
Definition Particle.hpp:482
constexpr bool is_ghost() const
Definition Particle.hpp:485