AutoPas  3.0.0
Loading...
Searching...
No Matches
VerletClusterLists.h
Go to the documentation of this file.
1
7#pragma once
8
9#include <algorithm>
10#include <cmath>
11#include <iterator>
12
13#include "ClusterTowerBlock2D.h"
29#include "autopas/utils/Timer.h"
31#include "autopas/utils/inBox.h"
33
34namespace autopas {
35
55template <class Particle_T>
57 public:
61 struct ClusterRange {
73 size_t numClusters{};
74 };
75
89 VerletClusterLists(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax, double cutoff,
90 double skin, unsigned int rebuildFrequency, size_t clusterSize,
92 : ParticleContainerInterface<Particle_T>(skin),
93 _towerBlock{boxMin, boxMax, cutoff + skin},
94 _clusterSize{clusterSize},
95 _particlesToAdd(autopas_get_max_threads()),
96 _cutoff{cutoff},
97 _rebuildFrequency{rebuildFrequency},
98 _loadEstimator(loadEstimator) {
99 // always have at least one tower.
100 _towerBlock.addTower(_clusterSize);
101 }
102
104
105 [[nodiscard]] ContainerOption getContainerType() const override { return ContainerOption::verletClusterLists; }
106
112 // (Explicit) static cast required for Apple Clang (last tested version: 17.0.0)
113 switch (static_cast<LoadEstimatorOption::Value>(this->_loadEstimator)) {
115 return [&](const std::array<unsigned long, 3> &cellsPerDimension,
116 const std::array<unsigned long, 3> &lowerCorner, const std::array<unsigned long, 3> &upperCorner) {
117 // the neighborListLength function defined for verletListsCells in not compatible with this container.
118 unsigned long sum = 0;
119 for (unsigned long x = lowerCorner[0]; x <= upperCorner[0]; x++) {
120 for (unsigned long y = lowerCorner[1]; y <= upperCorner[1]; y++) {
121 unsigned long cellLoad = 0;
122 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
123 for (auto &cluster : tower.getClusters()) {
124 if (cluster.getNeighbors()) {
125 cellLoad += cluster.getNeighbors()->size();
126 }
127 }
128 sum += cellLoad;
129 }
130 }
131 return sum;
132 };
133 }
135 [[fallthrough]];
136 default: {
137 return
138 [&](const std::array<unsigned long, 3> &cellsPerDimension, const std::array<unsigned long, 3> &lowerCorner,
139 const std::array<unsigned long, 3> &upperCorner) { return 1; };
140 }
141 }
142 }
143
144 void computeInteractions(TraversalInterface *traversal) override {
145 if (_isValid == ValidityState::cellsAndListsValid) {
147 "VerletClusterLists::computeInteractions(): Trying to do a pairwise iteration, even though verlet lists are "
148 "not "
149 "valid.");
150 }
151 auto *traversalInterface = dynamic_cast<VCLTraversalInterface<Particle_T> *>(traversal);
152 if (traversalInterface) {
153 traversalInterface->setClusterLists(*this);
154 traversalInterface->setTowers(_towerBlock.getTowersRef());
155 } else {
157 "Trying to use a traversal of wrong type in VerletClusterLists::computeInteractions. TraversalID: {}",
158 traversal->getTraversalType());
159 }
160 if (auto *balancedTraversal = dynamic_cast<BalancedTraversal *>(traversal)) {
161 balancedTraversal->setLoadEstimator(getLoadEstimatorFunction());
162 }
163
164 traversal->initTraversal();
165 traversal->traverseParticles();
166 traversal->endTraversal();
167 }
168
169 void reserve(size_t numParticles, size_t numParticlesHaloEstimate) override {
170 const auto particlesPerTower = (numParticles + numParticlesHaloEstimate) / _towerBlock.size();
171 for (auto &tower : _towerBlock) {
172 tower.reserve(particlesPerTower);
173 }
174 }
175
181 void addParticleImpl(const Particle_T &p) override {
182 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
183 _particlesToAdd[autopas_get_thread_num()].push_back(p);
184 }
185
186 void addHaloParticleImpl(const Particle_T &haloParticle) override {
187 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
188 _particlesToAdd[autopas_get_thread_num()].push_back(haloParticle);
189 }
190
191 bool updateHaloParticle(const Particle_T &haloParticle) override {
192 using namespace autopas::utils::ArrayMath::literals;
193
194 const auto &haloPos = haloParticle.getR();
195 // this might be called from a parallel region so force this iterator to be sequential
196 for (auto it = getRegionIterator(haloPos - (this->getVerletSkin() / 2.), haloPos + (this->getVerletSkin() / 2.),
197 IteratorBehavior::halo | IteratorBehavior::forceSequential, nullptr);
198 it.isValid(); ++it) {
199 if (haloParticle.getID() == it->getID()) {
200 // don't simply copy haloParticle over iter. This would trigger a dataRace with other regionIterators that
201 // overlap with this region.
202 it->setR(haloPos);
203 it->setV(haloParticle.getV());
204 it->setF(haloParticle.getF());
205 return true;
206 }
207 }
208 return false;
209 }
210
211 void deleteHaloParticles() override {
212 // Step 1: Remove particles from _particlesToAdd
213 for (auto &particleVec : _particlesToAdd) {
214 for (size_t j = 0; j < particleVec.size();) {
215 if (particleVec[j].isHalo()) {
216 particleVec[j] = particleVec[particleVec.size() - 1];
217 particleVec.pop_back();
218 } else {
219 ++j;
220 }
221 }
222 }
223 // Step 2: Remove particles from _towers
224 bool deletedSomething = false;
225 AUTOPAS_OPENMP(parallel for reduction(|| : deletedSomething))
226 // Thanks to clang 13 this has to be a index based loop instead of a range based
227 for (size_t i = 0; i < _towerBlock.size(); ++i) {
228 auto &tower = _towerBlock[i];
229 const auto towerSize = tower.size();
230 auto numTailDummies = tower.getNumTailDummyParticles();
231 // iterate over all non-tail dummies. Avoid underflows.
232 for (size_t j = 0; numTailDummies < towerSize and j < towerSize - numTailDummies;) {
233 if (tower[j].isHalo()) {
234 // swap-"delete"
235 tower[j] = tower[towerSize - 1 - numTailDummies];
236 // Since we can't pop the moved particle here mark it for deletion.
237 internal::markParticleAsDeleted(tower[towerSize - 1 - numTailDummies]);
238 ++numTailDummies;
239 deletedSomething = true;
240 } else {
241 ++j;
242 }
243 }
244 // if anything was marked for deletion actually delete it now.
245 if (deletedSomething) {
246 tower.deleteDummyParticles();
247 }
248 }
249 if (deletedSomething) {
250 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
251 }
252 }
253
254 std::tuple<const Particle_T *, size_t, size_t> getParticle(size_t cellIndex, size_t particleIndex,
255 IteratorBehavior iteratorBehavior,
256 const std::array<double, 3> &boxMin,
257 const std::array<double, 3> &boxMax) const override {
258 return getParticleImpl<true>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax);
259 }
260 std::tuple<const Particle_T *, size_t, size_t> getParticle(size_t cellIndex, size_t particleIndex,
261 IteratorBehavior iteratorBehavior) const override {
262 // this is not a region iter hence we stretch the bounding box to the numeric max
263 constexpr std::array<double, 3> boxMin{std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(),
264 std::numeric_limits<double>::lowest()};
265
266 constexpr std::array<double, 3> boxMax{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(),
267 std::numeric_limits<double>::max()};
268 return getParticleImpl<false>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax);
269 }
270
282 template <bool regionIter>
283 std::tuple<const Particle_T *, size_t, size_t> getParticleImpl(size_t cellIndex, size_t particleIndex,
284 IteratorBehavior iteratorBehavior,
285 const std::array<double, 3> &boxMin,
286 const std::array<double, 3> &boxMax) const {
287 using namespace autopas::utils::ArrayMath::literals;
288
289 // in this context cell == tower
290 // catching the edge case that towers are not yet built -> Iterator jumps to additional vectors
291 if (_towerBlock.empty()) {
292 return {nullptr, 0, 0};
293 }
294
295 std::array<double, 3> boxMinWithSafetyMargin = boxMin;
296 std::array<double, 3> boxMaxWithSafetyMargin = boxMax;
297 if constexpr (regionIter) {
298 // We extend the search box for cells here since particles might have moved
299 boxMinWithSafetyMargin -= 0.5 * this->getVerletSkin();
300 boxMaxWithSafetyMargin += 0.5 * this->getVerletSkin();
301 }
302
303 // first and last relevant cell index
304 const auto [startCellIndex, endCellIndex] = [&]() -> std::tuple<size_t, size_t> {
305 if constexpr (regionIter) {
306 // We extend the search box for cells here since particles might have moved
307 return {_towerBlock.getTowerIndex1DAtPosition(boxMinWithSafetyMargin),
308 _towerBlock.getTowerIndex1DAtPosition(boxMaxWithSafetyMargin)};
309 } else {
310 if (not(iteratorBehavior & IteratorBehavior::halo)) {
311 // only potentially owned region
312 return {_towerBlock.getFirstOwnedTowerIndex(), _towerBlock.getLastOwnedTowerIndex()};
313 } else {
314 // whole range of cells
315 return {0, _towerBlock.size() - 1};
316 }
317 }
318 }();
319
320 // if we are at the start of an iteration ...
321 if (cellIndex == 0 and particleIndex == 0) {
322 cellIndex =
323 startCellIndex + ((iteratorBehavior & IteratorBehavior::forceSequential) ? 0 : autopas_get_thread_num());
324 }
325 // abort if the start index is already out of bounds
326 if (cellIndex >= _towerBlock.size()) {
327 return {nullptr, 0, 0};
328 }
329 // check the data behind the indices
330 if (particleIndex >= _towerBlock[cellIndex].getNumActualParticles() or
331 not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
332 _towerBlock[cellIndex][particleIndex], iteratorBehavior, boxMin, boxMax)) {
333 // either advance them to something interesting or invalidate them.
334 std::tie(cellIndex, particleIndex) =
335 advanceIteratorIndices<regionIter>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax,
336 boxMinWithSafetyMargin, boxMaxWithSafetyMargin, endCellIndex);
337 }
338 // shortcut if the given index doesn't exist
339 if (cellIndex > endCellIndex) {
340 return {nullptr, 0, 0};
341 }
342 const Particle_T *retPtr = &_towerBlock[cellIndex][particleIndex];
343
344 return {retPtr, cellIndex, particleIndex};
345 }
346
347 bool deleteParticle(Particle_T &particle) override {
348 // This function doesn't actually delete anything as it would mess up the clusters' references.
350 return false;
351 }
352
353 bool deleteParticle(size_t cellIndex, size_t particleIndex) override {
354 // This function doesn't actually delete anything as it would mess up the clusters' references.
355 internal::markParticleAsDeleted(_towerBlock[cellIndex][particleIndex]);
356 return false;
357 }
358
359 [[nodiscard]] std::vector<Particle_T> updateContainer(bool keepNeighborListsValid) override {
360 if (keepNeighborListsValid) {
362 }
363 // First delete all halo particles.
364 this->deleteHaloParticles();
365 // Delete dummy particles.
366 AUTOPAS_OPENMP(parallel for)
367 for (size_t i = 0ul; i < _towerBlock.size(); ++i) {
368 _towerBlock[i].deleteDummyParticles();
369 }
370
371 // next find invalid particles
372 std::vector<Particle_T> invalidParticles;
373
374 // custom openmp reduction to concatenate all local vectors to one at the end of a parallel region
375 AUTOPAS_OPENMP(declare reduction(
376 vecMergeParticle : std::vector<Particle_T> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end())))
377 AUTOPAS_OPENMP(parallel reduction(vecMergeParticle : invalidParticles)) {
378 for (auto iter = this->begin(IteratorBehavior::owned); iter.isValid(); ++iter) {
379 if (not utils::inBox(iter->getR(), this->getBoxMin(), this->getBoxMax())) {
380 invalidParticles.push_back(*iter);
382 }
383 }
384 }
385 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
386 return invalidParticles;
387 }
388
389 [[nodiscard]] TraversalSelectorInfo getTraversalSelectorInfo() const override {
390 using namespace autopas::utils::ArrayMath::literals;
391 // Here, the towers might not yet be built, hence do not use members like _towerBlock.getTowersPerDim
392 const auto boxSizeWithHalo = this->getHaloBoxMax() - this->getHaloBoxMin();
393 const auto [towerSideLength, towersPerDim] = _towerBlock.estimateOptimalGridSideLength(
394 this->getNumberOfParticles(IteratorBehavior::ownedOrHalo), _clusterSize);
395 const std::array<double, 3> towerSize = {towerSideLength[0], towerSideLength[1],
396 this->getHaloBoxMax()[2] - this->getHaloBoxMin()[2]};
397 const std::array<unsigned long, 3> towerDimensions = {towersPerDim[0], towersPerDim[1], 1};
398 return TraversalSelectorInfo(towerDimensions, this->getInteractionLength(), towerSize, _clusterSize);
399 }
400
405 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo,
406 typename ContainerIterator<Particle_T, true, false>::ParticleVecType *additionalVectors = nullptr) override {
407 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
408 // invalid, we do writing operations on _particlesToAdd and can not read from it without race conditions.
409 if (_isValid != ValidityState::invalid) {
410 // we call particlesToAddEmpty() as a sanity check to ensure there are actually no particles in _particlesToAdd if
411 // the status is not invalid
412 if (not particlesToAddEmpty(autopas_get_thread_num())) {
414 "VerletClusterLists::begin(): Error: particle container is valid, but _particlesToAdd isn't empty!");
415 }
416 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
417 // from LogicHandler.
418 return ContainerIterator<Particle_T, true, false>(*this, behavior, additionalVectors);
419 } else {
420 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
421 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
423 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
424 return ContainerIterator<Particle_T, true, false>(*this, behavior, &additionalVectorsToPass);
425 }
426 }
427
433 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo,
435 nullptr) const override {
436 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
437 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
438 if (_isValid != ValidityState::invalid) {
439 // we call particlesToAddEmpty() as a sanity check to ensire there are actually no particles in _particlesToAdd if
440 // the status is not invalid
441 if (not particlesToAddEmpty(autopas_get_thread_num())) {
443 "VerletClusterLists::begin() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
444 }
445 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
446 // from LogicHandler.
447 return ContainerIterator<Particle_T, false, false>(*this, behavior, additionalVectors);
448 } else {
449 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
450 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
452 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
453 return ContainerIterator<Particle_T, false, false>(*this, behavior, &additionalVectorsToPass);
454 }
455 }
456
460 template <typename Lambda>
461 void forEach(Lambda forEachLambda, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
462 for (auto &tower : _towerBlock) {
463 tower.forEach(forEachLambda, behavior);
464 }
465 for (auto &vector : this->_particlesToAdd) {
466 for (auto &particle : vector) {
467 if (behavior.contains(particle)) {
468 forEachLambda(particle);
469 }
470 }
471 }
472 }
473
479 template <typename Lambda>
480 void forEach(Lambda forEachLambda, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
481 if (_isValid != ValidityState::invalid) {
482 if (not particlesToAddEmpty()) {
484 "VerletClusterLists::forEach() const: Error: particle container is valid, but _particlesToAdd isn't "
485 "empty!");
486 }
487 }
488
489 // If the particles are sorted into the towers, we can simply use the iteration over towers.
490 for (auto &tower : _towerBlock) {
491 tower.forEach(forEachLambda, behavior);
492 }
493
494 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
495 if (_isValid == ValidityState::invalid) {
496 for (auto &particlesToAddPerThread : _particlesToAdd) {
497 for (auto &particle : particlesToAddPerThread) {
498 if (behavior.contains(particle)) {
499 forEachLambda(particle);
500 }
501 }
502 }
503 }
504 }
505
509 template <typename Lambda, typename A>
510 void reduce(Lambda reduceLambda, A &result, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
511 for (auto &tower : _towerBlock) {
512 tower.reduce(reduceLambda, result, behavior);
513 }
514 for (auto &vector : this->_particlesToAdd) {
515 for (auto &p : vector) {
516 if (behavior.contains(p)) {
517 reduceLambda(p, result);
518 }
519 }
520 }
521 }
522
528 template <typename Lambda, typename A>
529 void reduce(Lambda reduceLambda, A &result,
530 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
531 if (_isValid != ValidityState::invalid) {
532 if (not particlesToAddEmpty()) {
534 "VerletClusterLists::reduce() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
535 }
536 }
537
538 // If the particles are sorted into the towers, we can simply use the iteration over towers.
539 for (auto tower : _towerBlock) {
540 tower.reduce(reduceLambda, result, behavior);
541 }
542
543 if (_isValid == ValidityState::invalid) {
544 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
545 for (auto &particlesToAddPerThread : _particlesToAdd) {
546 for (auto &particle : particlesToAddPerThread) {
547 if (behavior.contains(particle)) {
548 reduceLambda(particle, result);
549 }
550 }
551 }
552 }
553 }
554
559 const std::array<double, 3> &lowerCorner, const std::array<double, 3> &higherCorner, IteratorBehavior behavior,
560 typename ContainerIterator<Particle_T, true, true>::ParticleVecType *additionalVectors = nullptr) override {
561 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
562 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
563 if (_isValid != ValidityState::invalid) {
564 // we call particlesToAddEmpty() as a sanity check to ensure there are actually no particles in _particlesToAdd
565 // if the status is not invalid
566 if (not particlesToAddEmpty(autopas_get_thread_num())) {
568 "VerletClusterLists::reduce() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
569 }
570 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
571 // from LogicHandler.
572 return ContainerIterator<Particle_T, true, true>(*this, behavior, additionalVectors, lowerCorner, higherCorner);
573 } else {
574 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
575 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
576 typename ContainerIterator<Particle_T, true, true>::ParticleVecType additionalVectorsToPass;
577 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
578 return ContainerIterator<Particle_T, true, true>(*this, behavior, &additionalVectorsToPass, lowerCorner,
579 higherCorner);
580 }
581 }
582
588 const std::array<double, 3> &lowerCorner, const std::array<double, 3> &higherCorner, IteratorBehavior behavior,
590 nullptr) const override {
591 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
592 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
593 if (_isValid != ValidityState::invalid) {
594 // we call particlesToAddEmpty() as a sanity check to ensire there are actually no particles in _particlesToAdd if
595 // the status is not invalid
596 if (not particlesToAddEmpty(autopas_get_thread_num())) {
598 "VerletClusterLists::getRegionIterator() const: Error: particle container is valid, but _particlesToAdd "
599 "isn't empty!");
600 }
601 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
602 // from LogicHandler.
603 return ContainerIterator<Particle_T, false, true>(*this, behavior, additionalVectors, lowerCorner, higherCorner);
604 } else {
605 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
606 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
608 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
609 return ContainerIterator<Particle_T, false, true>(*this, behavior, &additionalVectorsToPass, lowerCorner,
610 higherCorner);
611 }
612 }
613
617 template <typename Lambda>
618 void forEachInRegion(Lambda forEachLambda, const std::array<double, 3> &lowerCorner,
619 const std::array<double, 3> &higherCorner,
620 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
621 for (size_t i = 0; i < _towerBlock.size(); ++i) {
622 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
623 continue;
624 }
625 auto &tower = _towerBlock[i];
626 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
627 // particles can move over cell borders. Calculate the volume this cell's particles can be.
628 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
629 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
630 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
631 tower.forEach(forEachLambda, lowerCorner, higherCorner, behavior);
632 }
633 }
634 for (auto &vector : _particlesToAdd) {
635 for (auto &particle : vector) {
636 if (behavior.contains(particle)) {
637 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
638 forEachLambda(particle);
639 }
640 }
641 }
642 }
643 }
644
650 template <typename Lambda>
651 void forEachInRegion(Lambda forEachLambda, const std::array<double, 3> &lowerCorner,
652 const std::array<double, 3> &higherCorner,
653 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
654 if (_isValid != ValidityState::invalid) {
655 if (not particlesToAddEmpty()) {
657 "VerletClusterLists::forEachInRegion() const: Error: particle container is valid, but _particlesToAdd "
658 "isn't empty!");
659 }
660 }
661
662 // If the particles are sorted into the towers, we can simply use the iteration over towers.
663 for (size_t i = 0; i < _towerBlock.size(); ++i) {
664 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
665 continue;
666 }
667 auto &tower = _towerBlock[i];
668 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
669 // particles can move over cell borders. Calculate the volume this cell's particles can be.
670 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
671 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
672 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
673 tower.forEach(forEachLambda, lowerCorner, higherCorner, behavior);
674 }
675 }
676
677 if (_isValid == ValidityState::invalid) {
678 // If the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
679 for (auto &particlesToAddPerThread : _particlesToAdd) {
680 for (auto &particle : particlesToAddPerThread) {
681 if (behavior.contains(particle)) {
682 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
683 forEachLambda(particle);
684 }
685 }
686 }
687 }
688 }
689 }
690
694 template <typename Lambda, typename A>
695 void reduceInRegion(Lambda reduceLambda, A &result, const std::array<double, 3> &lowerCorner,
696 const std::array<double, 3> &higherCorner,
697 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
698 for (size_t i = 0; i < _towerBlock.size(); ++i) {
699 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
700 continue;
701 }
702 auto &tower = _towerBlock[i];
703 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
704 // particles can move over cell borders. Calculate the volume this cell's particles can be.
705 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
706 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
707 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
708 tower.reduce(reduceLambda, result, lowerCorner, higherCorner, behavior);
709 }
710 }
711 for (auto &vector : _particlesToAdd) {
712 for (auto &particle : vector) {
713 if (behavior.contains(particle)) {
714 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
715 reduceLambda(particle, result);
716 }
717 }
718 }
719 }
720 }
721
727 template <typename Lambda, typename A>
728 void reduceInRegion(Lambda reduceLambda, A &result, const std::array<double, 3> &lowerCorner,
729 const std::array<double, 3> &higherCorner,
730 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
731 if (_isValid != ValidityState::invalid) {
732 if (not particlesToAddEmpty()) {
734 "VerletClusterLists::reduceInRegion() const: Error: particle container is valid, but _particlesToAdd isn't "
735 "empty!");
736 }
737 }
738 // If the particles are sorted into the towers, we can simply use the iteration over towers.
739 for (size_t i = 0; i < _towerBlock.size(); ++i) {
740 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
741 continue;
742 }
743 auto &tower = _towerBlock[i];
744 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
745 // particles can move over cell borders. Calculate the volume this cell's particles can be.
746 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
747 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
748 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
749 tower.reduce(reduceLambda, result, lowerCorner, higherCorner, behavior);
750 }
751 }
752
753 if (_isValid == ValidityState::invalid) {
754 // If the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
755 for (auto &particlesToAddPerThread : _particlesToAdd) {
756 for (auto &particle : particlesToAddPerThread) {
757 if (behavior.contains(particle)) {
758 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
759 reduceLambda(particle, result);
760 }
761 }
762 }
763 }
764 }
765 }
766
767 void rebuildNeighborLists(TraversalInterface *traversal) override {
768 // the builder might have a different newton3 choice than the traversal. This typically only happens in unit tests
769 // when rebuildTowersAndClusters() was not called explicitly.
770 if (_isValid == ValidityState::invalid or traversal->getUseNewton3() != _builder->getNewton3()) {
771 // clear the lists buffer because clusters will be recreated
772 _neighborLists.clear();
774 }
775 _builder->rebuildNeighborListsAndFillClusters();
776
777 auto *clusterTraversalInterface = dynamic_cast<VCLTraversalInterface<Particle_T> *>(traversal);
778 if (clusterTraversalInterface) {
779 if (clusterTraversalInterface->needsStaticClusterThreadPartition()) {
781 }
782 } else {
784 "Trying to use a traversal of wrong type in VerletClusterLists::rebuildNeighborLists. TraversalID: {}",
785 traversal->getTraversalType());
786 }
787 }
788
796 template <bool inParallel, class LoopBody>
797 void traverseClusters(LoopBody &&loopBody) {
798 if (inParallel) {
799 traverseClustersParallel<LoopBody>(std::forward<LoopBody>(loopBody));
800 } else {
801 traverseClustersSequential<LoopBody>(std::forward<LoopBody>(loopBody));
802 }
803 }
804
809 [[nodiscard]] size_t size() const override {
810 size_t sum = std::accumulate(_towerBlock.begin(), _towerBlock.end(), 0,
811 [](size_t acc, const auto &tower) { return acc + tower.size(); });
812 sum = std::accumulate(_particlesToAdd.begin(), _particlesToAdd.end(), sum,
813 [](size_t acc, const auto &buffer) { return acc + buffer.size(); });
814 return sum;
815 }
816
820 [[nodiscard]] size_t getNumberOfParticles(IteratorBehavior behavior) const override {
821 // sum up all particles in towers that fulfill behavior
822 size_t sum = std::accumulate(_towerBlock.begin(), _towerBlock.end(), 0, [&behavior](size_t acc, const auto &tower) {
823 return acc + tower.getNumberOfParticles(behavior);
824 });
825
826 // Since we can not directly insert particles into towers without a rebuild of the whole data structure,
827 // _particlesToAdd is used to store all these particles temporarily until the next rebuild inserts them into the
828 // towers data structure. However, these particles already belong to the respective tower, so we have to count them
829 // as well.
830 sum = std::accumulate(
831 _particlesToAdd.begin(), _particlesToAdd.end(), sum, [&behavior](size_t acc, const auto &buffer) {
832 return acc +
833 (std::count_if(buffer.begin(), buffer.end(), [&behavior](auto p) { return behavior.contains(p); }));
834 });
835
836 return sum;
837 }
838
843 const auto &getClusterThreadPartition() const { return _clusterThreadPartition; }
844
849 auto getNumClusters() const { return _numClusters; }
850
855 auto getTowerSideLength() const { return _towerBlock.getTowerSideLength(); }
856
861 auto getTowersPerDimension() const { return _towerBlock.getTowersPerDim(); }
862
867 auto getClusterSize() const { return _clusterSize; }
868
873 auto getNumTowersPerInteractionLength() const { return _towerBlock.getNumTowersPerInteractionLength(); }
874
880 template <class Functor>
882 const auto numTowers = _towerBlock.size();
884 AUTOPAS_OPENMP(parallel for schedule(dynamic))
885 for (size_t index = 0; index < numTowers; index++) {
886 _towerBlock[index].loadSoA(functor);
887 }
888 }
889
895 template <class Functor>
897 const auto numTowers = _towerBlock.size();
899 AUTOPAS_OPENMP(parallel for schedule(dynamic))
900 for (size_t index = 0; index < numTowers; index++) {
901 _towerBlock[index].extractSoA(functor);
902 }
903 }
904
912 return _towerBlock.getTowerByIndex2D(x, y);
913 }
914
923
924 [[nodiscard]] const std::array<double, 3> &getBoxMax() const override { return _towerBlock.getBoxMax(); }
925
930 [[nodiscard]] const std::array<double, 3> &getHaloBoxMax() const { return _towerBlock.getHaloBoxMax(); }
931
932 [[nodiscard]] const std::array<double, 3> &getBoxMin() const override { return _towerBlock.getBoxMin(); }
933
938 [[nodiscard]] const std::array<double, 3> &getHaloBoxMin() const { return _towerBlock.getHaloBoxMin(); }
939
940 [[nodiscard]] double getCutoff() const override { return _cutoff; }
941
942 void setCutoff(double cutoff) override { _cutoff = cutoff; }
943
944 [[nodiscard]] double getVerletSkin() const override { return this->_skin; }
945
950 void setSkin(double skin) { this->_skin = skin; }
951
956 [[nodiscard]] unsigned int getRebuildFrequency() { return _rebuildFrequency; }
957
962 void setRebuildFrequency(unsigned int rebuildFrequency) { _rebuildFrequency = rebuildFrequency; }
963
964 [[nodiscard]] double getInteractionLength() const override { return _cutoff + this->getVerletSkin(); }
965
966 void deleteAllParticles() override {
967 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
968 std::for_each(_particlesToAdd.begin(), _particlesToAdd.end(), [](auto &buffer) { buffer.clear(); });
969 std::for_each(_towerBlock.begin(), _towerBlock.end(), [](auto &tower) { tower.clear(); });
970 }
971
977 return _neighborLists;
978 }
979
985 void rebuildTowersAndClusters(bool newton3) {
986 using namespace utils::ArrayMath::literals;
987 // collect all particles to add from across the thread buffers
988 typename decltype(_particlesToAdd)::value_type particlesToAdd;
989 const size_t numParticlesToAdd =
990 std::accumulate(_particlesToAdd.begin(), _particlesToAdd.end(), 0,
991 [](size_t acc, const auto &buffer) { return acc + buffer.size(); });
992 particlesToAdd.reserve(numParticlesToAdd);
993 std::for_each(_particlesToAdd.begin(), _particlesToAdd.end(), [&](auto &particlesBuffer) {
994 particlesToAdd.insert(particlesToAdd.end(), particlesBuffer.begin(), particlesBuffer.end());
995 particlesBuffer.clear();
996 });
997
998 const double interactionLength = _cutoff + this->_skin;
999 _builder = std::make_unique<internal::VerletClusterListsRebuilder<Particle_T>>(
1000 _towerBlock, particlesToAdd, _neighborLists, _clusterSize, interactionLength * interactionLength, newton3);
1001
1002 _numClusters = _builder->rebuildTowersAndClusters();
1003
1004 _isValid.store(ValidityState::cellsValidListsInvalid, std::memory_order::memory_order_relaxed);
1005 for (auto &tower : _towerBlock) {
1006 tower.setParticleDeletionObserver(this);
1007 }
1008 }
1009
1015 template <class LoopBody>
1016 void traverseClustersSequential(LoopBody &&loopBody) {
1017 for (size_t x = 0; x < _towerBlock.getTowersPerDim()[0]; x++) {
1018 for (size_t y = 0; y < _towerBlock.getTowersPerDim()[1]; y++) {
1019 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
1020 for (auto clusterIter = tower.getFirstOwnedCluster(); clusterIter < tower.getFirstTailHaloCluster();
1021 ++clusterIter) {
1022 loopBody(*clusterIter);
1023 }
1024 }
1025 }
1026 }
1027
1037 template <class LoopBody>
1038 void traverseClustersParallel(LoopBody &&loopBody) {
1039 const auto towersPerDimX = _towerBlock.getTowersPerDim()[0];
1040 const auto towersPerDimY = _towerBlock.getTowersPerDim()[1];
1042 AUTOPAS_OPENMP(parallel for schedule(dynamic) collapse(2))
1043 for (size_t x = 0; x < towersPerDimX; x++) {
1044 for (size_t y = 0; y < towersPerDimY; y++) {
1045 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
1046
1047 for (auto clusterIter = tower.getFirstOwnedCluster(); clusterIter < tower.getFirstTailHaloCluster();
1048 ++clusterIter) {
1049 loopBody(*clusterIter);
1050 }
1051 }
1052 }
1053 }
1054
1055 protected:
1061 size_t numClusterPairs = 0;
1062 this->template traverseClusters<false>(
1063 [&numClusterPairs](auto &cluster) { numClusterPairs += cluster.getNeighbors()->size(); });
1064
1065 constexpr int minNumClusterPairsPerThread = 1000;
1066 auto numThreads =
1067 std::clamp(static_cast<int>(numClusterPairs / minNumClusterPairsPerThread), 1, autopas_get_max_threads());
1068
1069 size_t numClusterPairsPerThread =
1070 std::max(static_cast<unsigned long>(std::ceil(static_cast<double>(numClusterPairs) / numThreads)), 1ul);
1071 if (numClusterPairsPerThread * numThreads < numClusterPairs) {
1073 "VerletClusterLists::calculateClusterThreadPartition(): numClusterPairsPerThread ({}) * numThreads ({})={} "
1074 "should always "
1075 "be at least the amount of Cluster Pairs ({})!",
1076 numClusterPairsPerThread, numThreads, numClusterPairsPerThread * numThreads, numClusterPairs);
1077 }
1078 fillClusterRanges(numClusterPairsPerThread, numThreads);
1079 }
1080
1087 void fillClusterRanges(size_t numClusterPairsPerThread, int numThreads) {
1088 if (numClusterPairsPerThread < 1) {
1090 "VerletClusterLists::fillClusterRanges(): numClusterPairsPerThread({}) is less than one, this is not "
1091 "supported "
1092 "and will lead to errors!",
1093 numClusterPairsPerThread);
1094 }
1095 _clusterThreadPartition.resize(numThreads);
1096
1097 size_t currentThread = 0;
1098 size_t currentNumClustersToAdd = 0;
1099 size_t numClusterPairsTotal = 0;
1100 bool threadIsInitialized = false;
1101 // Iterate over the clusters of all towers
1102 for (size_t currentTowerIndex = 0; currentTowerIndex < _towerBlock.size(); currentTowerIndex++) {
1103 auto &currentTower = _towerBlock[currentTowerIndex];
1104 const auto firstOwnedClusterIndex =
1105 std::distance(currentTower.getClusters().begin(), currentTower.getFirstOwnedCluster());
1106 const auto firstTailHaloClusterIndex =
1107 std::distance(currentTower.getClusters().begin(), currentTower.getFirstTailHaloCluster());
1108 for (size_t currentClusterInTower = firstOwnedClusterIndex; currentClusterInTower < firstTailHaloClusterIndex;
1109 ++currentClusterInTower) {
1110 auto &currentCluster = currentTower.getCluster(currentClusterInTower);
1111
1112 // If on a new thread, start with the clusters for this thread here.
1113 if (not threadIsInitialized) {
1114 _clusterThreadPartition[currentThread] = {currentTowerIndex, currentClusterInTower, 0};
1115 threadIsInitialized = true;
1116 }
1117
1118 currentNumClustersToAdd++;
1119 numClusterPairsTotal += currentCluster.getNeighbors()->size();
1120
1121 // If the thread is finished, write number of clusters and start new thread.
1122 if (numClusterPairsTotal >= numClusterPairsPerThread * (currentThread + 1)) {
1123 // Add the number of clusters for the finished thread.
1124 _clusterThreadPartition[currentThread].numClusters += currentNumClustersToAdd;
1125 currentNumClustersToAdd = 0;
1126 // Go to next thread!
1127 currentThread++;
1128 // if we are already at the end of all threads, go back to last thread!
1129 // this is a safety precaution and should not really matter.
1130 if (currentThread >= numThreads) {
1131 --currentThread;
1132 threadIsInitialized = true;
1133 } else {
1134 threadIsInitialized = false;
1135 }
1136 }
1137 }
1138 }
1139 if (not threadIsInitialized) {
1140 _clusterThreadPartition[currentThread] = {0, 0, 0};
1141 }
1142 // Make sure the last cluster range contains the rest of the clusters, even if there is not the perfect number left.
1143 if (currentNumClustersToAdd != 0) {
1144 _clusterThreadPartition[currentThread].numClusters += currentNumClustersToAdd;
1145 }
1146 // Theoretically, some threads may still remain. This ensures that their numClusters are set to 0.
1147 while (++currentThread < numThreads) {
1148 _clusterThreadPartition[currentThread] = {0, 0, 0};
1149 }
1150 }
1151
1157 void notifyParticleDeleted() override {
1158 // this is potentially called from a threaded environment, so we have to make this atomic here!
1159 _isValid.store(ValidityState::invalid, std::memory_order::memory_order_relaxed);
1160 }
1161
1162 private:
1177 template <bool regionIter>
1178 [[nodiscard]] std::tuple<size_t, size_t> advanceIteratorIndices(
1179 size_t cellIndex, size_t particleIndex, IteratorBehavior iteratorBehavior, const std::array<double, 3> &boxMin,
1180 const std::array<double, 3> &boxMax, const std::array<double, 3> &boxMinWithSafetyMargin,
1181 const std::array<double, 3> &boxMaxWithSafetyMargin, size_t endCellIndex) const {
1182 // Finding the indices for the next particle
1183 const size_t stride = (iteratorBehavior & IteratorBehavior::forceSequential) ? 1 : autopas_get_num_threads();
1184
1185 // helper function to determine if the cell can even contain particles of interest to the iterator
1186 auto towerIsRelevant = [&]() -> bool {
1187 // special case: Towers are not yet built, then the tower acting as buffer is always relevant.
1188 if (_towerBlock.size() == 1) {
1189 return true;
1190 }
1191 bool isRelevant = true;
1192 if constexpr (regionIter) {
1193 // is the cell in the region?
1194 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(cellIndex);
1195 isRelevant =
1196 utils::boxesOverlap(towerLowCorner, towerHighCorner, boxMinWithSafetyMargin, boxMaxWithSafetyMargin);
1197 }
1198 return isRelevant;
1199 };
1200
1201 do {
1202 // advance to the next particle
1203 ++particleIndex;
1204 // If this breaches the end of a cell, find the next non-empty cell and reset particleIndex.
1205
1206 // If cell has wrong type, or there are no more particles in this cell jump to the next
1207 while (not towerIsRelevant() or particleIndex >= _towerBlock[cellIndex].getNumActualParticles()) {
1208 cellIndex += stride;
1209 particleIndex = 0;
1210
1211 // If we notice that there is nothing else to look at set invalid values, so we get a nullptr next time and
1212 // break.
1213 if (cellIndex > endCellIndex) {
1214 return {std::numeric_limits<size_t>::max(), particleIndex};
1215 }
1216 }
1217 } while (not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
1218 _towerBlock[cellIndex][particleIndex], iteratorBehavior, boxMin, boxMax));
1219
1220 // the indices returned at this point should always be valid
1221 return {cellIndex, particleIndex};
1222 }
1223
1225
1229 autopas::LoadEstimatorOption _loadEstimator;
1230
1234 size_t _clusterSize;
1235
1239 size_t _numClusters{0};
1240
1246 mutable std::vector<std::vector<Particle_T>> _particlesToAdd;
1247
1253 [[nodiscard]] bool particlesToAddEmpty(int bufferID = -1) const {
1254 if (bufferID == -1) {
1255 for (auto &threadBuffer : _particlesToAdd) {
1256 if (not threadBuffer.empty()) {
1257 return false;
1258 }
1259 }
1260 return true;
1261 } else {
1262 return _particlesToAdd[bufferID].empty();
1263 }
1264 }
1265
1274 template <class VecVec>
1275 void appendBuffersHelper(VecVec *additionalVectors, VecVec &outVec) const {
1276 if (additionalVectors) {
1277 outVec.reserve(_particlesToAdd.size() + additionalVectors->size());
1278 outVec.insert(outVec.end(), additionalVectors->begin(), additionalVectors->end());
1279 } else {
1280 outVec.reserve(_particlesToAdd.size());
1281 }
1282 for (auto &vec : _particlesToAdd) {
1283 outVec.push_back(&vec);
1284 }
1285 }
1286
1290 std::vector<ClusterRange> _clusterThreadPartition;
1291
1295 double _cutoff{};
1296
1300 unsigned int _rebuildFrequency{};
1304 enum class ValidityState : unsigned char {
1305 invalid = 0, // nothing is valid.
1306 cellsValidListsInvalid = 1, // only the cell structure is valid, but the lists are not.
1307 cellsAndListsValid = 2 // the cells and lists are valid
1308 };
1309
1313 std::atomic<ValidityState> _isValid{ValidityState::invalid};
1314
1318 std::unique_ptr<internal::VerletClusterListsRebuilder<Particle_T>> _builder;
1319
1324};
1325
1326} // namespace autopas
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
Base class for traversals utilising load balancing.
Definition: BalancedTraversal.h:19
std::function< unsigned long(const std::array< unsigned long, 3 > &, const std::array< unsigned long, 3 > &, const std::array< unsigned long, 3 > &)> EstimatorFunction
Type signature for load estimators.
Definition: BalancedTraversal.h:26
Public iterator class that iterates over a particle container and additional vectors (which are typic...
Definition: ContainerIterator.h:93
std::conditional_t< modifiable, std::vector< std::vector< Particle_T > * >, std::vector< std::vector< Particle_T > const * > > ParticleVecType
Type of the additional vector collection.
Definition: ContainerIterator.h:106
Functor base class.
Definition: Functor.h:40
Class representing the load estimator choices.
Definition: LoadEstimatorOption.h:18
Value
Possible choices for the load estimation algorithm.
Definition: LoadEstimatorOption.h:23
@ neighborListLength
Sum of neighbor list lengths.
Definition: LoadEstimatorOption.h:35
@ none
No load estimator.
Definition: LoadEstimatorOption.h:27
Class for manual memory management of neighbor lists.
Definition: NeighborListsBuffer.h:31
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:37
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
virtual void endTraversal()=0
Finalizes the traversal.
virtual TraversalOption getTraversalType() const =0
Return a enum representing the name of the traversal class.
virtual void traverseParticles()=0
Traverse the particles by pairs, triplets etc.
virtual void initTraversal()=0
Initializes the traversal.
bool getUseNewton3() const
Return whether the traversal uses newton 3.
Definition: TraversalInterface.h:63
Info for traversals of a specific container.
Definition: TraversalSelectorInfo.h:14
Interface for traversals of the VerletClusterLists container.
Definition: VCLTraversalInterface.h:20
virtual void setClusterLists(VerletClusterLists< Particle_T > &verletClusterLists)
Sets the cluster list for the traversal to iterate over.
Definition: VCLTraversalInterface.h:31
Particles are divided into clusters.
Definition: VerletClusterLists.h:56
TraversalSelectorInfo getTraversalSelectorInfo() const override
Generates a traversal selector info for this container.
Definition: VerletClusterLists.h:389
void reduce(Lambda reduceLambda, A &result, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo)
Reduce properties of particles as defined by a lambda function.
Definition: VerletClusterLists.h:510
ContainerOption getContainerType() const override
Get the ContainerType.
Definition: VerletClusterLists.h:105
void traverseClusters(LoopBody &&loopBody)
Helper method to iterate over all clusters.
Definition: VerletClusterLists.h:797
void deleteHaloParticles() override
Deletes all halo particles.
Definition: VerletClusterLists.h:211
void forEach(Lambda forEachLambda, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo) const
Execute code on all particles in this container as defined by a lambda function.
Definition: VerletClusterLists.h:480
size_t size() const override
Get the number of all particles stored in this container (owned + halo + dummy).
Definition: VerletClusterLists.h:809
auto getClusterSize() const
Returns the number of particles in each cluster.
Definition: VerletClusterLists.h:867
void rebuildNeighborLists(TraversalInterface *traversal) override
Rebuilds the neighbor lists for the next traversals.
Definition: VerletClusterLists.h:767
CellType getParticleCellTypeEnum() const override
Get the ParticleCell type as an Enum.
Definition: VerletClusterLists.h:103
BalancedTraversal::EstimatorFunction getLoadEstimatorFunction()
Generates the load estimation function depending on _loadEstimator.
Definition: VerletClusterLists.h:111
void setSkin(double skin)
Set the verlet skin length for the container.
Definition: VerletClusterLists.h:950
void addHaloParticleImpl(const Particle_T &haloParticle) override
Adds a particle to the container that lies in the halo region of the container.
Definition: VerletClusterLists.h:186
auto getTowersPerDimension() const
Returns the number of grids per dimension on the container.
Definition: VerletClusterLists.h:861
void forEach(Lambda forEachLambda, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo)
Execute code on all particles in this container as defined by a lambda function.
Definition: VerletClusterLists.h:461
const std::array< double, 3 > & getHaloBoxMax() const
Get the upper corner of the halo box.
Definition: VerletClusterLists.h:930
std::tuple< const Particle_T *, size_t, size_t > getParticleImpl(size_t cellIndex, size_t particleIndex, IteratorBehavior iteratorBehavior, const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax) const
Container specific implementation for getParticle.
Definition: VerletClusterLists.h:283
void addParticleImpl(const Particle_T &p) override
Adds the given particle to the container.
Definition: VerletClusterLists.h:181
void reduceInRegion(Lambda reduceLambda, A &result, const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo)
Execute code on all particles in this container in a certain region as defined by a lambda function.
Definition: VerletClusterLists.h:695
void traverseClustersSequential(LoopBody &&loopBody)
Helper method to sequentially iterate over all owned clusters.
Definition: VerletClusterLists.h:1016
ContainerIterator< Particle_T, false, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior, typename ContainerIterator< Particle_T, false, true >::ParticleVecType *additionalVectors=nullptr) const override
Iterate over all particles in a specified region for(auto iter = container.getRegionIterator(lowCorne...
Definition: VerletClusterLists.h:587
void computeInteractions(TraversalInterface *traversal) override
Iterates over all particle multiples (e.g.
Definition: VerletClusterLists.h:144
double getInteractionLength() const override
Return the interaction length (cutoff+skin) of the container.
Definition: VerletClusterLists.h:964
bool deleteParticle(size_t cellIndex, size_t particleIndex) override
Deletes the particle at the given index positions as long as this does not compromise the validity of...
Definition: VerletClusterLists.h:353
double getVerletSkin() const override
Return the verletSkin of the container verletSkin.
Definition: VerletClusterLists.h:944
void loadParticlesIntoSoAs(Functor *functor)
Loads all particles of the container in their correct SoA and generates the SoAViews for the clusters...
Definition: VerletClusterLists.h:881
std::tuple< const Particle_T *, size_t, size_t > getParticle(size_t cellIndex, size_t particleIndex, IteratorBehavior iteratorBehavior, const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax) const override
Fetch the pointer to a particle, identified via a cell and particle index.
Definition: VerletClusterLists.h:254
void setRebuildFrequency(unsigned int rebuildFrequency)
Set the rebuild Frequency value for the container.
Definition: VerletClusterLists.h:962
internal::ClusterTowerBlock2D< Particle_T > & getTowerBlock()
Getter for the cell block.
Definition: VerletClusterLists.h:922
auto getNumTowersPerInteractionLength() const
Returns the towers per interaction length.
Definition: VerletClusterLists.h:873
const std::array< double, 3 > & getBoxMin() const override
Get the lower corner of the container without halo.
Definition: VerletClusterLists.h:932
void deleteAllParticles() override
Deletes all particles.
Definition: VerletClusterLists.h:966
void fillClusterRanges(size_t numClusterPairsPerThread, int numThreads)
Fills in the cluster ranges of the cluster thread partition.
Definition: VerletClusterLists.h:1087
ContainerIterator< Particle_T, true, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior, typename ContainerIterator< Particle_T, true, true >::ParticleVecType *additionalVectors=nullptr) override
Iterate over all particles in a specified region for(auto iter = container.getRegionIterator(lowCorne...
Definition: VerletClusterLists.h:558
ContainerIterator< Particle_T, true, false > begin(IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo, typename ContainerIterator< Particle_T, true, false >::ParticleVecType *additionalVectors=nullptr) override
Iterate over all particles using for(auto iter = container.begin(); iter.isValid(); ++iter) .
Definition: VerletClusterLists.h:404
VerletClusterLists(const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax, double cutoff, double skin, unsigned int rebuildFrequency, size_t clusterSize, LoadEstimatorOption loadEstimator=LoadEstimatorOption::none)
Constructor of the VerletClusterLists class.
Definition: VerletClusterLists.h:89
void reduce(Lambda reduceLambda, A &result, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo) const
Reduce properties of particles as defined by a lambda function.
Definition: VerletClusterLists.h:529
size_t getNumberOfParticles(IteratorBehavior behavior) const override
Get the number of particles with respect to the specified IteratorBehavior.
Definition: VerletClusterLists.h:820
ContainerIterator< Particle_T, false, false > begin(IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo, typename ContainerIterator< Particle_T, false, false >::ParticleVecType *additionalVectors=nullptr) const override
Iterate over all particles using for(auto iter = container.begin(); iter.isValid(); ++iter) .
Definition: VerletClusterLists.h:432
void forEachInRegion(Lambda forEachLambda, const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo) const
Execute code on all particles in this container in a certain region as defined by a lambda function.
Definition: VerletClusterLists.h:651
unsigned int getRebuildFrequency()
Get the rebuild Frequency value for the container.
Definition: VerletClusterLists.h:956
void traverseClustersParallel(LoopBody &&loopBody)
Helper method to iterate over all clusters in parallel.
Definition: VerletClusterLists.h:1038
const std::array< double, 3 > & getHaloBoxMin() const
Get the lower corner of the halo box.
Definition: VerletClusterLists.h:938
const internal::VerletClusterListsRebuilder< Particle_T >::NeighborListsBuffer_T & getNeighborLists() const
Get the neighbor lists buffer object.
Definition: VerletClusterLists.h:976
double getCutoff() const override
Return the cutoff of the container.
Definition: VerletClusterLists.h:940
internal::ClusterTower< Particle_T > & getTowerByIndex(size_t x, size_t y)
Returns a reference to the tower for the given tower grid coordinates.
Definition: VerletClusterLists.h:911
auto getTowerSideLength() const
Returns the grid side length of the grids in the container.
Definition: VerletClusterLists.h:855
void reduceInRegion(Lambda reduceLambda, A &result, const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo) const
Execute code on all particles in this container in a certain region as defined by a lambda function.
Definition: VerletClusterLists.h:728
const std::array< double, 3 > & getBoxMax() const override
Get the upper corner of the container without halo.
Definition: VerletClusterLists.h:924
void reserve(size_t numParticles, size_t numParticlesHaloEstimate) override
Reserve memory for a given number of particles in the container and logic layers.
Definition: VerletClusterLists.h:169
void setCutoff(double cutoff) override
Set the cutoff of the container.
Definition: VerletClusterLists.h:942
bool deleteParticle(Particle_T &particle) override
Deletes the given particle as long as this does not compromise the validity of the container.
Definition: VerletClusterLists.h:347
std::tuple< const Particle_T *, size_t, size_t > getParticle(size_t cellIndex, size_t particleIndex, IteratorBehavior iteratorBehavior) const override
Fetch the pointer to a particle, identified via a cell and particle index.
Definition: VerletClusterLists.h:260
auto getNumClusters() const
Returns the number of clusters in this container.
Definition: VerletClusterLists.h:849
std::vector< Particle_T > updateContainer(bool keepNeighborListsValid) override
Updates the container.
Definition: VerletClusterLists.h:359
void rebuildTowersAndClusters(bool newton3)
Initializes a new VerletClusterListsRebuilder and uses it to rebuild the towers and the clusters.
Definition: VerletClusterLists.h:985
void calculateClusterThreadPartition()
Calculates a cluster thread partition that aims to give each thread about the same amount of cluster ...
Definition: VerletClusterLists.h:1060
void extractParticlesFromSoAs(Functor *functor)
Extracts all SoAs of the container into the particles.
Definition: VerletClusterLists.h:896
void forEachInRegion(Lambda forEachLambda, const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo)
Execute code on all particles in this container in a certain region as defined by a lambda function.
Definition: VerletClusterLists.h:618
bool updateHaloParticle(const Particle_T &haloParticle) override
Update a halo particle of the container with the given haloParticle.
Definition: VerletClusterLists.h:191
void notifyParticleDeleted() override
If a particle is deleted, we want _isValid to be set to invalid, as the tower structure is invalidate...
Definition: VerletClusterLists.h:1157
const auto & getClusterThreadPartition() const
Returns the cluster-thread-partition.
Definition: VerletClusterLists.h:843
Class to manage the grid of towers for the Verlet Cluster Lists container.
Definition: ClusterTowerBlock2D.h:25
This class represents one tower for clusters in the VerletClusterLists container.
Definition: ClusterTower.h:43
Class that is notified when a particle is deleted.
Definition: ParticleDeletedObserver.h:15
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
std::vector< typename ContainerType::ParticleType > collectParticlesAndMarkNonOwnedAsDummy(ContainerType &container)
Collects leaving particles and marks halo particles as dummy.
Definition: LeavingParticleCollector.h:85
void markParticleAsDeleted(Particle_T &p)
Marks a particle as deleted.
Definition: markParticleAsDeleted.h:23
void deleteParticle(ParticleIterator &iterator)
Function to access private iterator.deleteCurrentParticle() via friend.
Definition: ContainerIterator.h:35
constexpr std::array< T, SIZE > subScalar(const std::array< T, SIZE > &a, T s)
Subtracts a scalar s from each element of array a and returns the result.
Definition: ArrayMath.h:164
constexpr std::array< T, SIZE > addScalar(const std::array< T, SIZE > &a, T s)
Adds a scalar s to each element of array a and returns the result.
Definition: ArrayMath.h:147
bool boxesOverlap(const std::array< T, 3 > &boxALow, const std::array< T, 3 > &boxAHigh, const std::array< T, 3 > &boxBLow, const std::array< T, 3 > &boxBHigh)
Checks if two boxes have overlap.
Definition: inBox.h:67
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
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:32
CellType
The ParticleCell Type as an Enum.
Definition: ParticleCell.h:19
@ ClusterTower
ClusterTower : Tower for the 2D tower structure of VerletClusterLists.
int autopas_get_num_threads()
Dummy for omp_get_num_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:138
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132
Defines a cluster range used in the static cluster-thread-partition.
Definition: VerletClusterLists.h:61
size_t startIndexInTower
The index of the first cluster in its tower.
Definition: VerletClusterLists.h:69
size_t startTowerIndex
The index of the tower that contains the first cluster.
Definition: VerletClusterLists.h:65
size_t numClusters
The number of clusters in the range.
Definition: VerletClusterLists.h:73