Skip to content

Commit 2fbea6e

Browse files
Refactor short range loop and cells (#3874)
Rewrite of #3787 Description of changes: - simplify short range loop - remove `for_each_pair.hpp ` and `verlet_ia.hpp` helper functions - delegate distance calculation and cell iteration to the `CellStructure` class - hide implementation details of `CellStructure` - make `local_cells()` private - encapsulate debug functions - remove global variables `n_verlet_updates` and `rebuild_verletlist`
2 parents 748e9d0 + 994e699 commit 2fbea6e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+483
-966
lines changed

src/core/AtomDecomposition.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ class AtomDecomposition : public ParticleDecomposition {
9090
Utils::Vector3d max_range() const override;
9191
/* Return true if minimum image convention is
9292
* needed for distance calculation. */
93-
bool minimum_image_distance() const override { return true; }
93+
boost::optional<BoxGeometry> minimum_image_distance() const override {
94+
return m_box;
95+
}
9496

9597
private:
9698
/**

src/core/BoxGeometry.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <bitset>
2727
#include <cassert>
28+
#include <cmath>
2829

2930
class BoxGeometry {
3031
public:
@@ -92,6 +93,9 @@ template <typename T> T get_mi_coord(T a, T b, T box_length, bool periodic) {
9293

9394
/**
9495
* @brief Get the minimum-image vector between two coordinates.
96+
*
97+
* @tparam T Floating point type.
98+
*
9599
* @param a Coordinate of the terminal point.
96100
* @param b Coordinate of the initial point.
97101
* @param box Box parameters (side lengths, periodicity).

src/core/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ set(EspressoCore_SRC
66
comfixed_global.cpp
77
communication.cpp
88
constraints.cpp
9-
debug.cpp
109
dpd.cpp
1110
energy.cpp
1211
errorhandling.cpp

src/core/CellStructure.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,52 @@
2626

2727
#include <utils/contains.hpp>
2828

29+
#include <stdexcept>
30+
31+
void CellStructure::check_particle_index() {
32+
auto const max_id = get_max_local_particle_id();
33+
34+
for (auto const &p : local_particles()) {
35+
auto const id = p.identity();
36+
37+
if (id < 0 || id > max_id) {
38+
throw std::runtime_error("Particle id out of bounds.");
39+
}
40+
41+
if (get_local_particle(id) != &p) {
42+
throw std::runtime_error("Invalid local particle index entry.");
43+
}
44+
}
45+
46+
/* checks: local particle id */
47+
int local_part_cnt = 0;
48+
for (int n = 0; n < get_max_local_particle_id() + 1; n++) {
49+
if (get_local_particle(n) != nullptr) {
50+
local_part_cnt++;
51+
if (get_local_particle(n)->p.identity != n) {
52+
throw std::runtime_error("local_particles part has corrupted id.");
53+
}
54+
}
55+
}
56+
57+
if (local_part_cnt != local_particles().size()) {
58+
throw std::runtime_error(
59+
std::to_string(local_particles().size()) + " parts in cells but " +
60+
std::to_string(local_part_cnt) + " parts in local_particles");
61+
}
62+
}
63+
64+
void CellStructure::check_particle_sorting() {
65+
for (auto cell : local_cells()) {
66+
for (auto const &p : cell->particles()) {
67+
if (particle_to_cell(p) != cell) {
68+
throw std::runtime_error("misplaced particle with id " +
69+
std::to_string(p.identity()));
70+
}
71+
}
72+
}
73+
}
74+
2975
Cell *CellStructure::particle_to_cell(const Particle &p) {
3076
return decomposition().particle_to_cell(p);
3177
}
@@ -166,6 +212,13 @@ void CellStructure::resort_particles(int global_flag) {
166212
for (auto d : diff) {
167213
boost::apply_visitor(UpdateParticleIndexVisitor{this}, d);
168214
}
215+
216+
m_rebuild_verlet_list = true;
217+
218+
#ifdef ADDITIONAL_CHECKS
219+
check_particle_index();
220+
check_particle_sorting();
221+
#endif
169222
}
170223

171224
void CellStructure::set_atom_decomposition(boost::mpi::communicator const &comm,
@@ -180,4 +233,4 @@ void CellStructure::set_domain_decomposition(
180233
set_particle_decomposition(
181234
std::make_unique<DomainDecomposition>(comm, range, box, local_geo));
182235
m_type = CELL_STRUCTURE_DOMDEC;
183-
}
236+
}

src/core/CellStructure.hpp

Lines changed: 164 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@
3030
#include "ParticleDecomposition.hpp"
3131
#include "ParticleList.hpp"
3232
#include "ParticleRange.hpp"
33+
#include "algorithm/link_cell.hpp"
3334
#include "bond_error.hpp"
3435
#include "ghosts.hpp"
3536

3637
#include <boost/algorithm/cxx11/any_of.hpp>
3738
#include <boost/container/static_vector.hpp>
39+
#include <boost/iterator/indirect_iterator.hpp>
3840
#include <boost/range/algorithm/find_if.hpp>
3941
#include <boost/range/algorithm/transform.hpp>
4042

@@ -80,6 +82,32 @@ inline ParticleRange particles(Utils::Span<Cell *> cells) {
8082
}
8183
} // namespace Cells
8284

85+
/**
86+
* @brief Distance vector and length handed to pair kernels.
87+
*/
88+
struct Distance {
89+
explicit Distance(Utils::Vector3d const &vec21)
90+
: vec21(vec21), dist2(vec21.norm2()) {}
91+
92+
Utils::Vector3d vec21;
93+
double dist2;
94+
};
95+
namespace detail {
96+
struct MinimalImageDistance {
97+
const BoxGeometry box;
98+
99+
Distance operator()(Particle const &p1, Particle const &p2) const {
100+
return Distance(get_mi_vector(p1.r.p, p2.r.p, box));
101+
}
102+
};
103+
104+
struct EuclidianDistance {
105+
Distance operator()(Particle const &p1, Particle const &p2) const {
106+
return Distance(p1.r.p - p2.r.p);
107+
}
108+
};
109+
} // namespace detail
110+
83111
/** Describes a cell structure / cell system. Contains information
84112
* about the communication of cell contents (particles, ghosts, ...)
85113
* between different nodes and the relation between particle
@@ -103,6 +131,9 @@ struct CellStructure {
103131
public:
104132
bool use_verlet_list = true;
105133

134+
bool m_rebuild_verlet_list = true;
135+
std::vector<std::pair<Particle *, Particle *>> m_verlet_list;
136+
106137
/**
107138
* @brief Update local particle index.
108139
*
@@ -212,11 +243,14 @@ struct CellStructure {
212243
/** Maximal pair range supported by current cell system. */
213244
Utils::Vector3d max_range() const;
214245

215-
/** Return the global local_cells */
246+
private:
216247
Utils::Span<Cell *> local_cells();
248+
249+
public:
217250
ParticleRange local_particles();
218251
ParticleRange ghost_particles();
219252

253+
private:
220254
/** Cell system dependent function to find the right cell for a
221255
* particle.
222256
* \param p Particle.
@@ -225,6 +259,7 @@ struct CellStructure {
225259
*/
226260
Cell *particle_to_cell(const Particle &p);
227261

262+
public:
228263
/**
229264
* @brief Add a particle.
230265
*
@@ -293,7 +328,6 @@ struct CellStructure {
293328
return assert(m_decomposition), *m_decomposition;
294329
}
295330

296-
private:
297331
ParticleDecomposition &decomposition() {
298332
return assert(m_decomposition), *m_decomposition;
299333
}
@@ -327,6 +361,7 @@ struct CellStructure {
327361
* Cells::DataPart
328362
*/
329363
void ghosts_update(unsigned data_parts);
364+
330365
/**
331366
* @brief Add forces from ghost particles to real particles.
332367
*/
@@ -355,7 +390,6 @@ struct CellStructure {
355390
return partners;
356391
}
357392

358-
public:
359393
/**
360394
* @brief Execute kernel for every bond on particle.
361395
* @tparam Handler Callable, which can be invoked with
@@ -387,9 +421,10 @@ struct CellStructure {
387421
}
388422
}
389423

390-
private:
391-
/** Go through ghost cells and remove the ghost entries from the
392-
local particle index. */
424+
/**
425+
* @brief Go through ghost cells and remove the ghost entries from the
426+
* local particle index.
427+
*/
393428
void invalidate_ghosts() {
394429
for (auto const &p : ghost_particles()) {
395430
if (get_local_particle(p.identity()) == &p) {
@@ -443,11 +478,130 @@ struct CellStructure {
443478
double range, BoxGeometry const &box,
444479
LocalBox<double> const &local_geo);
445480

481+
public:
482+
template <class BondKernel> void bond_loop(BondKernel const &bond_kernel) {
483+
for (auto &p : local_particles()) {
484+
execute_bond_handler(p, bond_kernel);
485+
}
486+
}
487+
488+
private:
446489
/**
447-
* @brief Return true if minimum image convention is
448-
* needed for distance calculation. */
449-
bool minimum_image_distance() const {
450-
return m_decomposition->minimum_image_distance();
490+
* @brief Run link_cell algorithm for local cells.
491+
*
492+
* @tparam Kernel Needs to be callable with (Particle, Particle, Distance).
493+
* @param kernel Pair kernel functor.
494+
*/
495+
template <class Kernel> void link_cell(Kernel kernel) {
496+
auto const maybe_box = decomposition().minimum_image_distance();
497+
auto const first = boost::make_indirect_iterator(local_cells().begin());
498+
auto const last = boost::make_indirect_iterator(local_cells().end());
499+
500+
if (maybe_box) {
501+
Algorithm::link_cell(
502+
first, last,
503+
[&kernel, df = detail::MinimalImageDistance{*maybe_box}](
504+
Particle &p1, Particle &p2) { kernel(p1, p2, df(p1, p2)); });
505+
} else {
506+
Algorithm::link_cell(
507+
first, last,
508+
[&kernel, df = detail::EuclidianDistance{}](
509+
Particle &p1, Particle &p2) { kernel(p1, p2, df(p1, p2)); });
510+
}
511+
}
512+
513+
public:
514+
/** Non-bonded pair loop with potential use
515+
* of verlet lists.
516+
* @param pair_kernel Kernel to apply
517+
*/
518+
template <class PairKernel> void non_bonded_loop(PairKernel pair_kernel) {
519+
link_cell(pair_kernel);
520+
}
521+
522+
/** Non-bonded pair loop with potential use
523+
* of verlet lists.
524+
* @param pair_kernel Kernel to apply
525+
* @param verlet_criterion Filter for verlet lists.
526+
*/
527+
template <class PairKernel, class VerletCriterion>
528+
void non_bonded_loop(PairKernel &&pair_kernel,
529+
const VerletCriterion &verlet_criterion) {
530+
/* In this case the verlet list update is attached to
531+
* the pair kernel, and the verlet list is rebuilt as
532+
* we go. */
533+
if (use_verlet_list && m_rebuild_verlet_list) {
534+
m_verlet_list.clear();
535+
536+
link_cell([&pair_kernel, &verlet_criterion,
537+
this](Particle &p1, Particle &p2, Distance const &d) {
538+
if (verlet_criterion(p1, p2, d)) {
539+
m_verlet_list.emplace_back(&p1, &p2);
540+
pair_kernel(p1, p2, d);
541+
}
542+
});
543+
544+
m_rebuild_verlet_list = false;
545+
} else if (use_verlet_list && not m_rebuild_verlet_list) {
546+
auto const maybe_box = decomposition().minimum_image_distance();
547+
/* In this case the pair kernel is just run over the verlet list. */
548+
if (maybe_box) {
549+
auto const distance_function = detail::MinimalImageDistance{*maybe_box};
550+
for (auto &pair : m_verlet_list) {
551+
pair_kernel(*pair.first, *pair.second,
552+
distance_function(*pair.first, *pair.second));
553+
}
554+
} else {
555+
auto const distance_function = detail::EuclidianDistance{};
556+
for (auto &pair : m_verlet_list) {
557+
pair_kernel(*pair.first, *pair.second,
558+
distance_function(*pair.first, *pair.second));
559+
}
560+
}
561+
} else {
562+
/* No verlet lists, just run the kernel with pairs from the cells. */
563+
link_cell(pair_kernel);
564+
}
565+
}
566+
567+
private:
568+
/**
569+
* @brief Check that particle index is commensurate with particles.
570+
*
571+
* For each local particles is checked that has a correct entry
572+
* in the particles index, and that there are no excess (non-existing)
573+
* particles in the index.
574+
*/
575+
void check_particle_index();
576+
577+
/**
578+
* @brief Check that particles are in the correct cell.
579+
*
580+
* This checks for all local particles that the result
581+
* of particles_to_cell is the cell the particles is
582+
* actually in, e.g. that the particles are sorted according
583+
* to particles_to_cell.
584+
*/
585+
void check_particle_sorting();
586+
587+
public:
588+
/**
589+
* @brief Find cell a particle is stored in.
590+
*
591+
* For local particles, this returns the cell they
592+
* are stored in, otherwise nullptr is returned.
593+
*
594+
* @param p Particle to find cell for
595+
* @return Cell for particle or nullptr.
596+
*/
597+
Cell *find_current_cell(const Particle &p) {
598+
assert(not get_resort_particles());
599+
600+
if (p.l.ghost) {
601+
return nullptr;
602+
}
603+
604+
return particle_to_cell(p);
451605
}
452606
};
453607

src/core/DomainDecomposition.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ void DomainDecomposition::resort(bool global,
224224
}
225225
}
226226
}
227+
227228
void DomainDecomposition::mark_cells() {
228229
int cnt_c = 0;
229230

src/core/DomainDecomposition.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ struct DomainDecomposition : public ParticleDecomposition {
8181
const BoxGeometry &box_geo,
8282
const LocalBox<double> &local_geo);
8383

84-
public:
8584
GhostCommunicator const &exchange_ghosts_comm() const override {
8685
return m_exchange_ghosts_comm;
8786
}
@@ -100,10 +99,13 @@ struct DomainDecomposition : public ParticleDecomposition {
10099
return position_to_cell(p.r.p);
101100
}
102101

103-
bool minimum_image_distance() const override { return false; }
104102
void resort(bool global, std::vector<ParticleChange> &diff) override;
105103
Utils::Vector3d max_range() const override;
106104

105+
boost::optional<BoxGeometry> minimum_image_distance() const override {
106+
return {};
107+
}
108+
107109
private:
108110
/** Fill local_cells list and ghost_cells list for use with domain
109111
* decomposition.

0 commit comments

Comments
 (0)