AutoPas  3.0.0
Loading...
Searching...
No Matches
VerletClusterListsRebuilder.h
Go to the documentation of this file.
1
7#pragma once
8
10#include "autopas/utils/Timer.h"
12#include "autopas/utils/inBox.h"
13
14namespace autopas::internal {
15
23template <class Particle_T>
25 public:
31
32 private:
33 size_t _clusterSize;
34 NeighborListsBuffer_T &_neighborListsBuffer;
35 std::vector<Particle_T> &_particlesToAdd;
37 double _interactionLengthSqr;
38 bool _newton3;
39
40 public:
51 VerletClusterListsRebuilder(ClusterTowerBlock2D<Particle_T> &towerBlock, std::vector<Particle_T> &particlesToAdd,
52 NeighborListsBuffer_T &neighborListsBuffer, size_t clusterSize,
53 double interactionLengthSqr, bool newton3)
54 : _clusterSize(clusterSize),
55 _neighborListsBuffer(neighborListsBuffer),
56 _particlesToAdd(particlesToAdd),
57 _towerBlock(towerBlock),
58 _interactionLengthSqr(interactionLengthSqr),
59 _newton3(newton3) {}
60
68 using namespace autopas::utils::ArrayMath::literals;
69 // get rid of dummies
70 for (auto &tower : _towerBlock) {
71 tower.deleteDummyParticles();
72 }
73
74 // count particles by accumulating tower sizes
75 const size_t numParticles = std::accumulate(_towerBlock.begin(), _towerBlock.end(), _particlesToAdd.size(),
76 [](auto acc, const auto &tower) {
77 // actually we want only the number of owned or halo particles
78 // but dummies were just deleted.
79 return acc + tower.getNumActualParticles();
80 });
81
82 // calculate new number of towers and their size
83 const auto boxSizeWithHalo = _towerBlock.getHaloBoxMax() - _towerBlock.getHaloBoxMin();
84 const auto numTowersOld = _towerBlock.size();
85 const auto [towerSideLength, numTowersPerDim] =
86 _towerBlock.estimateOptimalGridSideLength(numParticles, _clusterSize);
87 const auto numTowersNew = numTowersPerDim[0] * numTowersPerDim[1];
88
89 // collect all particles that are now not in the right tower anymore
90 std::vector<std::vector<Particle_T>> invalidParticles{};
91 // Reserve for:
92 // - _particlesToAdd (+1)
93 // - surplus towers (+max(0, numTowersNew - numTowersOld))
94 // - particles out of bounds of new towers (+numTowersNew)
95 invalidParticles.reserve(1 + std::max(0, static_cast<int>(numTowersNew) - static_cast<int>(numTowersOld)) +
96 numTowersNew);
97 // collect all particles that are not yet assigned to towers
98 invalidParticles.push_back(std::move(_particlesToAdd));
99 _particlesToAdd.clear();
100 // if we have less towers than before, collect all particles from the unused towers.
101 for (size_t i = numTowersNew; i < numTowersOld; ++i) {
102 invalidParticles.push_back(std::move(_towerBlock[i].particleVector()));
103 }
104
105 // resize to number of towers.
106 // Attention! This uses the dummy constructor so we still need to set the desired cluster size.
107 _towerBlock.resize(towerSideLength, numTowersPerDim);
108
109 // after resizing the towers we collect all the particles that are out of bounds
110 const auto collectedParticlesFromTowers = collectOutOfBoundsParticlesFromTowers();
111 invalidParticles.insert(invalidParticles.end(), collectedParticlesFromTowers.begin(),
112 collectedParticlesFromTowers.end());
113
114 // create more towers if needed and make an estimate for how many particles memory needs to be allocated
115 // Factor is more or less a random guess.
116 // Historically 2.7 used to be good but in other tests there was no significant difference to lower values.
117 const auto sizeEstimation =
118 static_cast<size_t>((static_cast<double>(numParticles) / static_cast<double>(numTowersNew)) * 1.2);
119 for (auto &tower : _towerBlock) {
120 // Set potentially new towers to the desired cluster size
121 tower.setClusterSize(_clusterSize);
122 tower.reserve(sizeEstimation);
123 }
124
125 sortParticlesIntoTowers(invalidParticles);
126
127 // estimate the number of clusters by particles divided by cluster size + one extra per tower.
128 _neighborListsBuffer.reserveNeighborLists(numParticles / _clusterSize + numTowersNew);
129 // generate clusters and count them
130 size_t numClusters = 0;
131 for (auto &tower : _towerBlock) {
132 numClusters += tower.generateClusters();
133 for (auto clusterIter = _newton3 ? tower.getClusters().begin() : tower.getFirstOwnedCluster();
134 clusterIter < (_newton3 ? tower.getClusters().end() : tower.getFirstTailHaloCluster()); ++clusterIter) {
135 // VCL stores the references to the lists in the clusters, therefore there is no need to create a
136 // cluster -> list lookup structure in the buffer structure
137 const auto listID = _neighborListsBuffer.getNewNeighborList();
138 clusterIter->setNeighborList(&(_neighborListsBuffer.template getNeighborListRef<false>(listID)));
139 }
140 }
141
142 return numClusters;
143 }
144
156
157 double dummyParticleDistance = _towerBlock.getInteractionLength() * 2;
158 double startDummiesX = 1000 * _towerBlock.getHaloBoxMax()[0];
159 for (size_t index = 0; index < _towerBlock.size(); index++) {
160 _towerBlock[index].setDummyValues(startDummiesX + static_cast<double>(index) * dummyParticleDistance,
161 dummyParticleDistance);
162 }
163 }
164
170 for (auto &tower : _towerBlock) {
171 tower.setDummyParticlesToLastActualParticle();
172 for (auto clusterIter = tower.getFirstOwnedCluster(); clusterIter < tower.getFirstTailHaloCluster();
173 ++clusterIter) {
174 clusterIter->clearNeighbors();
175 }
176 }
177 }
178
183 std::vector<std::vector<Particle_T>> collectAllParticlesFromTowers() {
184 std::vector<std::vector<Particle_T>> invalidParticles;
185 invalidParticles.resize(_towerBlock.size());
186 for (size_t towerIndex = 0; towerIndex < _towerBlock.size(); towerIndex++) {
187 invalidParticles[towerIndex] = _towerBlock[towerIndex].collectAllActualParticles();
188 _towerBlock[towerIndex].clear();
189 }
190 return invalidParticles;
191 }
192
199 std::vector<std::vector<Particle_T>> collectOutOfBoundsParticlesFromTowers() {
200 std::vector<std::vector<Particle_T>> outOfBoundsParticles;
201 outOfBoundsParticles.resize(_towerBlock.size());
202 for (size_t towerIndex = 0; towerIndex < _towerBlock.size(); towerIndex++) {
203 const auto &[towerBoxMin, towerBoxMax] = _towerBlock.getTowerBoundingBox(towerIndex);
204 outOfBoundsParticles[towerIndex] = _towerBlock[towerIndex].collectOutOfBoundsParticles(towerBoxMin, towerBoxMax);
205 }
206 return outOfBoundsParticles;
207 }
208
217 void sortParticlesIntoTowers(const std::vector<std::vector<Particle_T>> &particles2D) {
218 const auto numVectors = particles2D.size();
219 AUTOPAS_OPENMP(parallel for schedule(dynamic))
220 for (size_t index = 0; index < numVectors; index++) {
221 const std::vector<Particle_T> &vector = particles2D[index];
222 for (const auto &particle : vector) {
223 if (utils::inBox(particle.getR(), _towerBlock.getHaloBoxMin(), _towerBlock.getHaloBoxMax())) {
224 auto &tower = _towerBlock.getTowerAtPosition(particle.getR());
225 tower.addParticle(particle);
226 } else {
227 AutoPasLog(TRACE, "Not adding particle to VerletClusterLists container, because it is outside the halo:\n{}",
228 particle.toString());
229 }
230 }
231 }
232 }
233
238 const int maxTowerIndexX = _towerBlock.getTowersPerDim()[0] - 1;
239 const int maxTowerIndexY = _towerBlock.getTowersPerDim()[1] - 1;
240 const auto numTowersPerInteractionLength = _towerBlock.getNumTowersPerInteractionLength();
241 // for all towers
243 AUTOPAS_OPENMP(parallel for schedule(dynamic) collapse(2))
244 for (int towerIndexY = 0; towerIndexY <= maxTowerIndexY; towerIndexY++) {
245 for (int towerIndexX = 0; towerIndexX <= maxTowerIndexX; towerIndexX++) {
246 // calculate extent of interesting tower 2D indices
247 const int minX = std::max(towerIndexX - numTowersPerInteractionLength, 0);
248 const int minY = std::max(towerIndexY - numTowersPerInteractionLength, 0);
249 const int maxX = std::min(towerIndexX + numTowersPerInteractionLength, maxTowerIndexX);
250 const int maxY = std::min(towerIndexY + numTowersPerInteractionLength, maxTowerIndexY);
251
252 iterateNeighborTowers(towerIndexX, towerIndexY, minX, maxX, minY, maxY,
253 [this](auto &towerA, auto &towerB) { calculateNeighborsBetweenTowers(towerA, towerB); });
254 }
255 }
256 }
257
278 template <class FunType>
279 void iterateNeighborTowers(const int towerIndexX, const int towerIndexY, const int minNeighborIndexX,
280 const int maxNeighborIndexX, const int minNeighborIndexY, const int maxNeighborIndexY,
281 FunType function) {
282 auto &tower = _towerBlock.getTowerByIndex2D(towerIndexX, towerIndexY);
283 // for all neighbor towers
284 for (int neighborIndexY = minNeighborIndexY; neighborIndexY <= maxNeighborIndexY; neighborIndexY++) {
285 double distBetweenTowersY =
286 std::max(0, std::abs(towerIndexY - neighborIndexY) - 1) * _towerBlock.getTowerSideLength()[1];
287
288 for (int neighborIndexX = minNeighborIndexX; neighborIndexX <= maxNeighborIndexX; neighborIndexX++) {
289 if (_newton3 and not isForwardNeighbor(towerIndexX, towerIndexY, neighborIndexX, neighborIndexY)) {
290 continue;
291 }
292
293 double distBetweenTowersX =
294 std::max(0, std::abs(towerIndexX - neighborIndexX) - 1) * _towerBlock.getTowerSideLength()[0];
295
296 // calculate distance in xy-plane
297 auto distBetweenTowersXYsqr = distBetweenTowersX * distBetweenTowersX + distBetweenTowersY * distBetweenTowersY;
298 // skip if already longer than interactionLength
299 if (distBetweenTowersXYsqr <= _interactionLengthSqr) {
300 auto &neighborTower = _towerBlock.getTowerByIndex2D(neighborIndexX, neighborIndexY);
301
302 function(tower, neighborTower);
303 }
304 }
305 }
306 }
307
315 int get1DInteractionCellIndexForTower(const int towerIndexX, const int towerIndexY) {
316 const auto numTowersPerInteractionLength = _towerBlock.getNumTowersPerInteractionLength();
317 const int interactionCellTowerX = towerIndexX / numTowersPerInteractionLength;
318 const int interactionCellTowerY = towerIndexY / numTowersPerInteractionLength;
319
320 const int numInteractionCellsX = static_cast<int>(
321 std::ceil(_towerBlock.getTowersPerDim()[0] / static_cast<double>(numTowersPerInteractionLength)));
322
323 return interactionCellTowerX + numInteractionCellsX * interactionCellTowerY;
324 }
325
339 bool isForwardNeighbor(const int towerIndexX, const int towerIndexY, const int neighborIndexX,
340 const int neighborIndexY) {
341 const auto interactionCellTowerIndex1D = get1DInteractionCellIndexForTower(towerIndexX, towerIndexY);
342 const auto interactionCellNeighborIndex1D = get1DInteractionCellIndexForTower(neighborIndexX, neighborIndexY);
343
344 if (interactionCellNeighborIndex1D > interactionCellTowerIndex1D) {
345 return true;
346 } else if (interactionCellNeighborIndex1D < interactionCellTowerIndex1D) {
347 return false;
348 } // else if (interactionCellNeighborIndex1D == interactionCellTowerIndex1D) ...
349
350 const auto towerIndex1D = _towerBlock.towerIndex2DTo1D(towerIndexX, towerIndexY);
351 const auto neighborIndex1D = _towerBlock.towerIndex2DTo1D(neighborIndexX, neighborIndexY);
352
353 return neighborIndex1D >= towerIndex1D;
354 }
355
369
370 const auto interactionLengthFracOfDomainZ =
371 _towerBlock.getInteractionLength() / (_towerBlock.getHaloBoxMax()[2] - _towerBlock.getHaloBoxMin()[2]);
372 const bool isSameTower = (&towerA == &towerB);
373 // This heuristic seems to find a good middle ground between not too much memory allocated and no additional
374 // allocations when calling clusterA.addNeighbor(clusterB)
375 const auto neighborListReserveHeuristicFactor = (interactionLengthFracOfDomainZ * 2.1) / _clusterSize;
376 const auto isHaloCluster = [](const auto &clusterIter, const auto &tower) {
377 return clusterIter < tower.getFirstOwnedCluster() or clusterIter >= tower.getFirstTailHaloCluster();
378 };
379 // iterate over all clusters from tower A. In newton3 mode go over all of them, otherwise only owned.
380 for (auto clusterIterA = _newton3 ? towerA.getClusters().begin() : towerA.getFirstOwnedCluster();
381 clusterIterA < (_newton3 ? towerA.getClusters().end() : towerA.getFirstTailHaloCluster()); ++clusterIterA) {
382 if (not clusterIterA->empty()) {
383 clusterIterA->getNeighbors()->reserve((towerA.getNumActualParticles() + 8 * towerB.getNumActualParticles()) *
384 neighborListReserveHeuristicFactor);
385
386 // if we are within one tower depending on newton3 only look at forward neighbors
387 // clusterIterB can't be const because it will potentially be added as a non-const neighbor
388 for (auto clusterIterB = isSameTower and _newton3 ? clusterIterA + 1 : towerB.getClusters().begin();
389 clusterIterB < towerB.getClusters().end(); ++clusterIterB) {
390 // a cluster cannot be a neighbor to itself
391 if (&*clusterIterA == &*clusterIterB) {
392 continue;
393 }
394 // never do halo-halo interactions
395 if (isHaloCluster(clusterIterA, towerA) and isHaloCluster(clusterIterB, towerB)) {
396 continue;
397 }
398 if (not clusterIterB->empty()) {
399 const auto [aMin, aMax] = clusterIterA->getBoundingBox();
400 const auto [bMin, bMax] = clusterIterB->getBoundingBox();
401 const auto boxDistSquared = boxDistanceSquared(aMin, aMax, bMin, bMax);
402 if (boxDistSquared <= _interactionLengthSqr) {
403 clusterIterA->addNeighbor(*clusterIterB);
404 }
405 }
406 }
407 }
408 }
409 }
414 bool getNewton3() const { return _newton3; }
415};
416} // namespace autopas::internal
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
Class for manual memory management of neighbor lists.
Definition: NeighborListsBuffer.h:31
void reserveNeighborLists(size_t n)
Resize the internal buffer so that there are new spare lists.
Definition: NeighborListsBuffer.h:115
size_t getNewNeighborList()
Reserves a neighbor list for use.
Definition: NeighborListsBuffer.h:74
Class to manage the grid of towers for the Verlet Cluster Lists container.
Definition: ClusterTowerBlock2D.h:25
size_t towerIndex2DTo1D(const size_t x, const size_t y) const
Returns the 1D index for the given 2D-coordinates of a tower.
Definition: ClusterTowerBlock2D.h:282
const std::array< double, 3 > & getHaloBoxMin() const
Getter.
Definition: ClusterTowerBlock2D.h:332
const std::array< double, 3 > & getHaloBoxMax() const
Getter.
Definition: ClusterTowerBlock2D.h:337
size_t size() const
Return the number of towers.
Definition: ClusterTowerBlock2D.h:82
const std::array< double, 2 > & getTowerSideLength() const
Getter.
Definition: ClusterTowerBlock2D.h:357
std::vector< ClusterTower< Particle_T > >::iterator begin()
Start iterator over towers.
Definition: ClusterTowerBlock2D.h:47
std::vector< ClusterTower< Particle_T > >::iterator end()
End iterator over towers.
Definition: ClusterTowerBlock2D.h:57
void resize(const std::array< double, 2 > &towerSideLength, const std::array< size_t, 2 > &towersPerDim)
Resize the internal grid and storage.
Definition: ClusterTowerBlock2D.h:89
std::tuple< std::array< double, 3 >, std::array< double, 3 > > getTowerBoundingBox(size_t index1D) const
Calculates the low and high corner of a tower given by its index.
Definition: ClusterTowerBlock2D.h:176
const std::array< size_t, 2 > & getTowersPerDim() const
Getter.
Definition: ClusterTowerBlock2D.h:352
double getInteractionLength() const
Getter.
Definition: ClusterTowerBlock2D.h:312
std::tuple< std::array< double, 2 >, std::array< size_t, 2 > > estimateOptimalGridSideLength(size_t numParticles, size_t clusterSize) const
Estimates the optimal 2D tower grid side length.
Definition: ClusterTowerBlock2D.h:140
ClusterTower< Particle_T > & getTowerByIndex2D(const size_t x, const size_t y)
Returns a reference to the tower for the given tower grid coordinates.
Definition: ClusterTowerBlock2D.h:271
int getNumTowersPerInteractionLength() const
Getter.
Definition: ClusterTowerBlock2D.h:317
ClusterTower< Particle_T > & getTowerAtPosition(const std::array< double, 3 > &pos)
Return a reference to the tower at a given position in the simulation coordinate system (e....
Definition: ClusterTowerBlock2D.h:260
This class represents one tower for clusters in the VerletClusterLists container.
Definition: ClusterTower.h:43
const std::vector< autopas::internal::Cluster< Particle_T > >::iterator & getFirstTailHaloCluster() const
Returns an iterator to the first particle after the owned clusters, that contains no owned particles ...
Definition: ClusterTower.h:297
const std::vector< autopas::internal::Cluster< Particle_T > >::iterator & getFirstOwnedCluster() const
Returns an iterator to the first cluster that contains at least one owned particle.
Definition: ClusterTower.h:280
unsigned long getNumActualParticles() const
Get the number of all particles saved in the tower without tailing dummies that are used to fill up c...
Definition: ClusterTower.h:260
auto & getClusters()
Returns a reference to the std::vector holding the clusters of this container.
Definition: ClusterTower.h:274
This class represents a cluster in the VerletClusterLists container.
Definition: Cluster.h:26
Helper class for rebuilding the VerletClusterLists container.
Definition: VerletClusterListsRebuilder.h:24
void updateNeighborLists()
Updates the neighbor lists for all clusters.
Definition: VerletClusterListsRebuilder.h:237
void calculateNeighborsBetweenTowers(internal::ClusterTower< Particle_T > &towerA, internal::ClusterTower< Particle_T > &towerB)
Calculates for all clusters in the given tower:
Definition: VerletClusterListsRebuilder.h:366
std::vector< std::vector< Particle_T > > collectAllParticlesFromTowers()
Takes all particles from all towers and returns them.
Definition: VerletClusterListsRebuilder.h:183
void iterateNeighborTowers(const int towerIndexX, const int towerIndexY, const int minNeighborIndexX, const int maxNeighborIndexX, const int minNeighborIndexY, const int maxNeighborIndexY, FunType function)
For all clusters in a tower, given by it's x/y indices, find all neighbors in towers that are given b...
Definition: VerletClusterListsRebuilder.h:279
int get1DInteractionCellIndexForTower(const int towerIndexX, const int towerIndexY)
Returns the index of a imagined interaction cell with side length equal the interaction length that c...
Definition: VerletClusterListsRebuilder.h:315
bool isForwardNeighbor(const int towerIndexX, const int towerIndexY, const int neighborIndexX, const int neighborIndexY)
Decides if a given neighbor tower is a forward neighbor to a given tower.
Definition: VerletClusterListsRebuilder.h:339
std::vector< std::vector< Particle_T > > collectOutOfBoundsParticlesFromTowers()
Collect all particles that are stored in the wrong towers.
Definition: VerletClusterListsRebuilder.h:199
void rebuildNeighborListsAndFillClusters()
Rebuilds the neighbor lists and fills Clusters with dummies as described in ClusterTower::setDummyVal...
Definition: VerletClusterListsRebuilder.h:153
size_t rebuildTowersAndClusters()
Rebuilds the towers, sorts the particles into them and creates the clusters with a reference to an un...
Definition: VerletClusterListsRebuilder.h:67
void clearNeighborListsAndMoveDummiesIntoClusters()
Clears previously saved neighbors from clusters and sets the 3D positions of the dummy particles to i...
Definition: VerletClusterListsRebuilder.h:169
bool getNewton3() const
Getter.
Definition: VerletClusterListsRebuilder.h:414
void sortParticlesIntoTowers(const std::vector< std::vector< Particle_T > > &particles2D)
Sorts all passed particles in the appropriate clusters.
Definition: VerletClusterListsRebuilder.h:217
VerletClusterListsRebuilder(ClusterTowerBlock2D< Particle_T > &towerBlock, std::vector< Particle_T > &particlesToAdd, NeighborListsBuffer_T &neighborListsBuffer, size_t clusterSize, double interactionLengthSqr, bool newton3)
Constructs the builder from the cluster list.
Definition: VerletClusterListsRebuilder.h:51
This namespace is used for implementation specifics.
Definition: CellFunctor.h:14
double boxDistanceSquared(const std::array< T, SIZE > &aMin, const std::array< T, SIZE > &aMax, const std::array< T, SIZE > &bMin, const std::array< T, SIZE > &bMax)
Calculate the squared minimum distance between two boxes, which are aligned to the Cartesian grid.
Definition: ArrayMath.h:663
bool inBox(const std::array< T, 3 > &position, const std::array< T, 3 > &low, const std::array< T, 3 > &high)
Checks if position is inside of a box defined by low and high.
Definition: inBox.h:26