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 using ParticleType = Particle_T;
66
70 struct ClusterRange {
82 size_t numClusters{};
83 };
84
97 VerletClusterLists(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax, double cutoff,
98 double skin, size_t clusterSize, LoadEstimatorOption loadEstimator = LoadEstimatorOption::none)
99 : ParticleContainerInterface<Particle_T>(skin),
100 _towerBlock{boxMin, boxMax, cutoff + skin},
101 _clusterSize{clusterSize},
102 _particlesToAdd(autopas_get_max_threads()),
103 _cutoff{cutoff},
104 _loadEstimator(loadEstimator) {
105 // always have at least one tower.
106 _towerBlock.addTower(_clusterSize);
107 }
108
109 [[nodiscard]] ContainerOption getContainerType() const override { return ContainerOption::verletClusterLists; }
110
116 // (Explicit) static cast required for Apple Clang (last tested version: 17.0.0)
117 switch (static_cast<LoadEstimatorOption::Value>(this->_loadEstimator)) {
119 return [&](const std::array<unsigned long, 3> &cellsPerDimension,
120 const std::array<unsigned long, 3> &lowerCorner, const std::array<unsigned long, 3> &upperCorner) {
121 // the neighborListLength function defined for verletListsCells in not compatible with this container.
122 unsigned long sum = 0;
123 for (unsigned long x = lowerCorner[0]; x <= upperCorner[0]; x++) {
124 for (unsigned long y = lowerCorner[1]; y <= upperCorner[1]; y++) {
125 unsigned long cellLoad = 0;
126 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
127 for (auto &cluster : tower.getClusters()) {
128 if (cluster.getNeighbors()) {
129 cellLoad += cluster.getNeighbors()->size();
130 }
131 }
132 sum += cellLoad;
133 }
134 }
135 return sum;
136 };
137 }
139 [[fallthrough]];
140 default: {
141 return
142 [&](const std::array<unsigned long, 3> &cellsPerDimension, const std::array<unsigned long, 3> &lowerCorner,
143 const std::array<unsigned long, 3> &upperCorner) { return 1; };
144 }
145 }
146 }
147
148 void computeInteractions(TraversalInterface *traversal) override {
149 if (_isValid == ValidityState::cellsAndListsValid) {
151 "VerletClusterLists::computeInteractions(): Trying to do a pairwise iteration, even though verlet lists are "
152 "not valid.");
153 }
154 auto *traversalInterface = dynamic_cast<VCLTraversalInterface<Particle_T> *>(traversal);
155 if (traversalInterface) {
156 traversalInterface->setClusterLists(*this);
157 traversalInterface->setTowers(_towerBlock.getTowersRef());
158 } else {
160 "Trying to use a traversal of wrong type in VerletClusterLists::computeInteractions. TraversalID: {}",
161 traversal->getTraversalType());
162 }
163 if (auto *balancedTraversal = dynamic_cast<BalancedTraversal *>(traversal)) {
164 balancedTraversal->setLoadEstimator(getLoadEstimatorFunction());
165 }
166
167 traversal->initTraversal();
168 traversal->traverseParticles();
169 traversal->endTraversal();
170 }
171
176 void setAoSSortingThresholds(std::shared_ptr<const SortingThresholdInfoInterface> aosSortingThresholds) override {}
177
182 void setSoASortingThresholds(std::shared_ptr<const SortingThresholdInfoInterface> soaSortingThresholds) override {}
183
184 void reserve(size_t numParticles, size_t numParticlesHaloEstimate) override {
185 const auto particlesPerTower = (numParticles + numParticlesHaloEstimate) / _towerBlock.size();
186 for (auto &tower : _towerBlock) {
187 tower.reserve(particlesPerTower);
188 }
189 }
190
196 void addParticleImpl(const Particle_T &p) override {
197 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
198 _particlesToAdd[autopas_get_thread_num()].push_back(p);
199 }
200
201 void addHaloParticleImpl(const Particle_T &haloParticle) override {
202 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
203 _particlesToAdd[autopas_get_thread_num()].push_back(haloParticle);
204 }
205
206 bool updateHaloParticle(const Particle_T &haloParticle) override {
207 using namespace autopas::utils::ArrayMath::literals;
208
209 const auto &haloPos = haloParticle.getR();
210 // this might be called from a parallel region so force this iterator to be sequential
211 for (auto it = getRegionIterator(haloPos - (this->getVerletSkin() / 2.), haloPos + (this->getVerletSkin() / 2.),
212 IteratorBehavior::halo | IteratorBehavior::forceSequential, std::nullopt);
213 it.isValid(); ++it) {
214 if (haloParticle.getID() == it->getID()) {
215 // don't simply copy haloParticle over iter. This would trigger a dataRace with other regionIterators that
216 // overlap with this region.
217 it->setR(haloPos);
218 it->setV(haloParticle.getV());
219 it->setF(haloParticle.getF());
220 return true;
221 }
222 }
223 return false;
224 }
225
226 void deleteHaloParticles() override {
227 // Step 1: Remove particles from _particlesToAdd
228 for (auto &particleVec : _particlesToAdd) {
229 for (size_t j = 0; j < particleVec.size();) {
230 if (particleVec[j].isHalo()) {
231 particleVec[j] = particleVec[particleVec.size() - 1];
232 particleVec.pop_back();
233 } else {
234 ++j;
235 }
236 }
237 }
238 // Step 2: Remove particles from _towers
239 bool deletedSomething = false;
240 AUTOPAS_OPENMP(parallel for reduction(|| : deletedSomething))
241 // Thanks to clang 13 this has to be a index based loop instead of a range based
242 for (size_t i = 0; i < _towerBlock.size(); ++i) {
243 auto &tower = _towerBlock[i];
244 const auto towerSize = tower.size();
245 auto numTailDummies = tower.getNumTailDummyParticles();
246 // iterate over all non-tail dummies. Avoid underflows.
247 for (size_t j = 0; numTailDummies < towerSize and j < towerSize - numTailDummies;) {
248 if (tower[j].isHalo()) {
249 // swap-"delete"
250 tower[j] = tower[towerSize - 1 - numTailDummies];
251 // Since we can't pop the moved particle here mark it for deletion.
252 internal::markParticleAsDeleted(tower[towerSize - 1 - numTailDummies]);
253 ++numTailDummies;
254 deletedSomething = true;
255 } else {
256 ++j;
257 }
258 }
259 // if anything was marked for deletion actually delete it now.
260 if (deletedSomething) {
261 tower.deleteDummyParticles();
262 }
263 }
264 if (deletedSomething) {
265 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
266 }
267 }
268
269 std::tuple<const Particle_T *, size_t, size_t> getParticle(size_t cellIndex, size_t particleIndex,
270 IteratorBehavior iteratorBehavior,
271 const std::array<double, 3> &boxMin,
272 const std::array<double, 3> &boxMax) const override {
273 return getParticleImpl<true>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax);
274 }
275 std::tuple<const Particle_T *, size_t, size_t> getParticle(size_t cellIndex, size_t particleIndex,
276 IteratorBehavior iteratorBehavior) const override {
277 // this is not a region iter hence we stretch the bounding box to the numeric max
278 constexpr std::array<double, 3> boxMin{std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(),
279 std::numeric_limits<double>::lowest()};
280
281 constexpr std::array<double, 3> boxMax{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(),
282 std::numeric_limits<double>::max()};
283 return getParticleImpl<false>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax);
284 }
285
297 template <bool regionIter>
298 std::tuple<const Particle_T *, size_t, size_t> getParticleImpl(size_t cellIndex, size_t particleIndex,
299 IteratorBehavior iteratorBehavior,
300 const std::array<double, 3> &boxMin,
301 const std::array<double, 3> &boxMax) const {
302 using namespace autopas::utils::ArrayMath::literals;
303
304 // in this context cell == tower
305 // catching the edge case that towers are not yet built -> Iterator jumps to additional vectors
306 if (_towerBlock.empty()) {
307 return {nullptr, 0, 0};
308 }
309
310 std::array<double, 3> boxMinWithSafetyMargin = boxMin;
311 std::array<double, 3> boxMaxWithSafetyMargin = boxMax;
312 if constexpr (regionIter) {
313 // We extend the search box for cells here since particles might have moved
314 boxMinWithSafetyMargin -= 0.5 * this->getVerletSkin();
315 boxMaxWithSafetyMargin += 0.5 * this->getVerletSkin();
316 }
317
318 // first and last relevant cell index
319 const auto [startCellIndex, endCellIndex] = [&]() -> std::tuple<size_t, size_t> {
320 if constexpr (regionIter) {
321 // We extend the search box for cells here since particles might have moved
322 return {_towerBlock.getTowerIndex1DAtPosition(boxMinWithSafetyMargin),
323 _towerBlock.getTowerIndex1DAtPosition(boxMaxWithSafetyMargin)};
324 } else {
325 if (not(iteratorBehavior & IteratorBehavior::halo)) {
326 // only potentially owned region
327 return {_towerBlock.getFirstOwnedTowerIndex(), _towerBlock.getLastOwnedTowerIndex()};
328 } else {
329 // whole range of cells
330 return {0, _towerBlock.size() - 1};
331 }
332 }
333 }();
334
335 // if we are at the start of an iteration ...
336 if (cellIndex == 0 and particleIndex == 0) {
337 cellIndex =
338 startCellIndex + ((iteratorBehavior & IteratorBehavior::forceSequential) ? 0 : autopas_get_thread_num());
339 }
340 // abort if the start index is already out of bounds
341 if (cellIndex >= _towerBlock.size()) {
342 return {nullptr, 0, 0};
343 }
344 // check the data behind the indices
345 if (particleIndex >= _towerBlock[cellIndex].getNumActualParticles() or
346 not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
347 _towerBlock[cellIndex][particleIndex], iteratorBehavior, boxMin, boxMax)) {
348 // either advance them to something interesting or invalidate them.
349 std::tie(cellIndex, particleIndex) =
350 advanceIteratorIndices<regionIter>(cellIndex, particleIndex, iteratorBehavior, boxMin, boxMax,
351 boxMinWithSafetyMargin, boxMaxWithSafetyMargin, endCellIndex);
352 }
353 // shortcut if the given index doesn't exist
354 if (cellIndex > endCellIndex) {
355 return {nullptr, 0, 0};
356 }
357 const Particle_T *retPtr = &_towerBlock[cellIndex][particleIndex];
358
359 return {retPtr, cellIndex, particleIndex};
360 }
361
362 bool deleteParticle(Particle_T &particle) override {
363 // This function doesn't actually delete anything as it would mess up the clusters' references.
365 return false;
366 }
367
368 bool deleteParticle(size_t cellIndex, size_t particleIndex) override {
369 // This function doesn't actually delete anything as it would mess up the clusters' references.
370 internal::markParticleAsDeleted(_towerBlock[cellIndex][particleIndex]);
371 return false;
372 }
373
374 [[nodiscard]] std::vector<Particle_T> updateContainer(bool keepNeighborListsValid) override {
375 if (keepNeighborListsValid) {
377 }
378 // First delete all halo particles.
379 this->deleteHaloParticles();
380 // Delete dummy particles.
381 AUTOPAS_OPENMP(parallel for)
382 for (size_t i = 0ul; i < _towerBlock.size(); ++i) {
383 _towerBlock[i].deleteDummyParticles();
384 }
385 // Delete dummy particles also from the _particlesToAdd vector
386 AUTOPAS_OPENMP(parallel for)
387 for (size_t i = 0ul; i < _particlesToAdd.size(); ++i) {
388 _particlesToAdd[i].erase(std::remove_if(_particlesToAdd[i].begin(), _particlesToAdd[i].end(),
389 [](const auto &p) { return p.isDummy(); }),
390 _particlesToAdd[i].end());
391 }
392
393 // next find invalid particles
394 std::vector<Particle_T> invalidParticles;
395
396 // custom openmp reduction to concatenate all local vectors to one at the end of a parallel region
397 AUTOPAS_OPENMP(declare reduction(
398 vecMergeParticle : std::vector<Particle_T> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end())))
399 AUTOPAS_OPENMP(parallel reduction(vecMergeParticle : invalidParticles)) {
400 for (auto iter = this->begin(IteratorBehavior::owned); iter.isValid(); ++iter) {
401 if (not utils::inBox(iter->getR(), this->getBoxMin(), this->getBoxMax())) {
402 invalidParticles.push_back(*iter);
404 }
405 }
406 }
407 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
408 return invalidParticles;
409 }
410
411 [[nodiscard]] TraversalSelectorInfo getTraversalSelectorInfo() const override {
412 using namespace autopas::utils::ArrayMath::literals;
413 // Here, the towers might not yet be built, hence do not use members like _towerBlock.getTowersPerDim
414 const auto boxSizeWithHalo = this->getHaloBoxMax() - this->getHaloBoxMin();
415 const auto [towerSideLength, towersPerDim] = _towerBlock.estimateOptimalGridSideLength(
416 this->getNumberOfParticles(IteratorBehavior::ownedOrHalo), _clusterSize);
417 const std::array<double, 3> towerSize = {towerSideLength[0], towerSideLength[1],
418 this->getHaloBoxMax()[2] - this->getHaloBoxMin()[2]};
419 const std::array<unsigned long, 3> towerDimensions = {towersPerDim[0], towersPerDim[1], 1};
420 return TraversalSelectorInfo(towerDimensions, this->getInteractionLength(), towerSize, _clusterSize);
421 }
422
427 IteratorBehavior behavior = IteratorBehavior::ownedOrHalo,
429 std::nullopt) override {
430 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
431 // invalid, we do writing operations on _particlesToAdd and can not read from it without race conditions.
432 if (_isValid != ValidityState::invalid) {
433 // we call particlesToAddEmpty() as a sanity check to ensure there are actually no particles in _particlesToAdd if
434 // the status is not invalid
435 if (not particlesToAddEmpty(autopas_get_thread_num())) {
437 "VerletClusterLists::begin(): Error: particle container is valid, but _particlesToAdd isn't empty!");
438 }
439 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
440 // from LogicHandler.
441 return ContainerIterator<Particle_T, true, false>(*this, behavior, additionalVectors);
442 } else {
443 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
444 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
446 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
447 return ContainerIterator<Particle_T, true, false>(*this, behavior, std::ref(additionalVectorsToPass));
448 }
449 }
450
456 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo,
458 std::nullopt) const override {
459 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
460 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
461 if (_isValid != ValidityState::invalid) {
462 // we call particlesToAddEmpty() as a sanity check to ensire there are actually no particles in _particlesToAdd if
463 // the status is not invalid
464 if (not particlesToAddEmpty(autopas_get_thread_num())) {
466 "VerletClusterLists::begin() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
467 }
468 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
469 // from LogicHandler.
470 return ContainerIterator<Particle_T, false, false>(*this, behavior, additionalVectors);
471 } else {
472 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
473 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
475 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
476 return ContainerIterator<Particle_T, false, false>(*this, behavior, std::ref(additionalVectorsToPass));
477 }
478 }
479
483 template <typename Lambda>
484 void forEach(Lambda forEachLambda, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
485 for (auto &tower : _towerBlock) {
486 tower.forEach(forEachLambda, behavior);
487 }
488 for (auto &vector : this->_particlesToAdd) {
489 for (auto &particle : vector) {
490 if (behavior.contains(particle)) {
491 forEachLambda(particle);
492 }
493 }
494 }
495 }
496
502 template <typename Lambda>
503 void forEach(Lambda forEachLambda, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
504 if (_isValid != ValidityState::invalid) {
505 if (not particlesToAddEmpty()) {
507 "VerletClusterLists::forEach() const: Error: particle container is valid, but _particlesToAdd isn't "
508 "empty!");
509 }
510 }
511
512 // If the particles are sorted into the towers, we can simply use the iteration over towers.
513 for (auto &tower : _towerBlock) {
514 tower.forEach(forEachLambda, behavior);
515 }
516
517 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
518 if (_isValid == ValidityState::invalid) {
519 for (auto &particlesToAddPerThread : _particlesToAdd) {
520 for (auto &particle : particlesToAddPerThread) {
521 if (behavior.contains(particle)) {
522 forEachLambda(particle);
523 }
524 }
525 }
526 }
527 }
528
532 template <typename Lambda, typename A>
533 void reduce(Lambda reduceLambda, A &result, IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
534 for (auto &tower : _towerBlock) {
535 tower.reduce(reduceLambda, result, behavior);
536 }
537 for (auto &vector : this->_particlesToAdd) {
538 for (auto &p : vector) {
539 if (behavior.contains(p)) {
540 reduceLambda(p, result);
541 }
542 }
543 }
544 }
545
551 template <typename Lambda, typename A>
552 void reduce(Lambda reduceLambda, A &result,
553 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
554 if (_isValid != ValidityState::invalid) {
555 if (not particlesToAddEmpty()) {
557 "VerletClusterLists::reduce() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
558 }
559 }
560
561 // If the particles are sorted into the towers, we can simply use the iteration over towers.
562 for (auto tower : _towerBlock) {
563 tower.reduce(reduceLambda, result, behavior);
564 }
565
566 if (_isValid == ValidityState::invalid) {
567 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
568 for (auto &particlesToAddPerThread : _particlesToAdd) {
569 for (auto &particle : particlesToAddPerThread) {
570 if (behavior.contains(particle)) {
571 reduceLambda(particle, result);
572 }
573 }
574 }
575 }
576 }
577
582 const std::array<double, 3> &lowerCorner, const std::array<double, 3> &higherCorner, IteratorBehavior behavior,
584 std::nullopt) override {
585 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
586 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
587 if (_isValid != ValidityState::invalid) {
588 // we call particlesToAddEmpty() as a sanity check to ensure there are actually no particles in _particlesToAdd
589 // if the status is not invalid
590 if (not particlesToAddEmpty(autopas_get_thread_num())) {
592 "VerletClusterLists::reduce() const: Error: particle container is valid, but _particlesToAdd isn't empty!");
593 }
594 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
595 // from LogicHandler.
596 return ContainerIterator<Particle_T, true, true>(*this, behavior, additionalVectors, lowerCorner, higherCorner);
597 } else {
598 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
599 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
600 typename ContainerIterator<Particle_T, true, true>::ParticleVecType additionalVectorsToPass;
601 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
602 return ContainerIterator<Particle_T, true, true>(*this, behavior, std::ref(additionalVectorsToPass), lowerCorner,
603 higherCorner);
604 }
605 }
606
612 const std::array<double, 3> &lowerCorner, const std::array<double, 3> &higherCorner, IteratorBehavior behavior,
614 std::nullopt) const override {
615 // Note: particlesToAddEmpty() can only be called if the container status is not invalid. If the status is set to
616 // invalid, we do writing operations on _particlesToAdd and can not read from from it without race conditions.
617 if (_isValid != ValidityState::invalid) {
618 // we call particlesToAddEmpty() as a sanity check to ensire there are actually no particles in _particlesToAdd if
619 // the status is not invalid
620 if (not particlesToAddEmpty(autopas_get_thread_num())) {
622 "VerletClusterLists::getRegionIterator() const: Error: particle container is valid, but _particlesToAdd "
623 "isn't empty!");
624 }
625 // If the particles are sorted into the towers, we can simply use the iteration over towers + additionalVectors
626 // from LogicHandler.
627 return ContainerIterator<Particle_T, false, true>(*this, behavior, additionalVectors, lowerCorner, higherCorner);
628 } else {
629 // if the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
630 // store all pointers in a temporary which is passed to the ParticleIterator constructor.
632 appendBuffersHelper(additionalVectors, additionalVectorsToPass);
633 return ContainerIterator<Particle_T, false, true>(*this, behavior, std::ref(additionalVectorsToPass), lowerCorner,
634 higherCorner);
635 }
636 }
637
641 template <typename Lambda>
642 void forEachInRegion(Lambda forEachLambda, const std::array<double, 3> &lowerCorner,
643 const std::array<double, 3> &higherCorner,
644 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
645 for (size_t i = 0; i < _towerBlock.size(); ++i) {
646 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
647 continue;
648 }
649 auto &tower = _towerBlock[i];
650 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
651 // particles can move over cell borders. Calculate the volume this cell's particles can be.
652 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
653 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
654 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
655 tower.forEach(forEachLambda, lowerCorner, higherCorner, behavior);
656 }
657 }
658 for (auto &vector : _particlesToAdd) {
659 for (auto &particle : vector) {
660 if (behavior.contains(particle)) {
661 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
662 forEachLambda(particle);
663 }
664 }
665 }
666 }
667 }
668
674 template <typename Lambda>
675 void forEachInRegion(Lambda forEachLambda, const std::array<double, 3> &lowerCorner,
676 const std::array<double, 3> &higherCorner,
677 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
678 if (_isValid != ValidityState::invalid) {
679 if (not particlesToAddEmpty()) {
681 "VerletClusterLists::forEachInRegion() const: Error: particle container is valid, but _particlesToAdd "
682 "isn't empty!");
683 }
684 }
685
686 // If the particles are sorted into the towers, we can simply use the iteration over towers.
687 for (size_t i = 0; i < _towerBlock.size(); ++i) {
688 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
689 continue;
690 }
691 auto &tower = _towerBlock[i];
692 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
693 // particles can move over cell borders. Calculate the volume this cell's particles can be.
694 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
695 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
696 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
697 tower.forEach(forEachLambda, lowerCorner, higherCorner, behavior);
698 }
699 }
700
701 if (_isValid == ValidityState::invalid) {
702 // If the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
703 for (auto &particlesToAddPerThread : _particlesToAdd) {
704 for (auto &particle : particlesToAddPerThread) {
705 if (behavior.contains(particle)) {
706 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
707 forEachLambda(particle);
708 }
709 }
710 }
711 }
712 }
713 }
714
718 template <typename Lambda, typename A>
719 void reduceInRegion(Lambda reduceLambda, A &result, const std::array<double, 3> &lowerCorner,
720 const std::array<double, 3> &higherCorner,
721 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) {
722 for (size_t i = 0; i < _towerBlock.size(); ++i) {
723 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
724 continue;
725 }
726 auto &tower = _towerBlock[i];
727 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
728 // particles can move over cell borders. Calculate the volume this cell's particles can be.
729 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
730 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
731 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
732 tower.reduce(reduceLambda, result, lowerCorner, higherCorner, behavior);
733 }
734 }
735 for (auto &vector : _particlesToAdd) {
736 for (auto &particle : vector) {
737 if (behavior.contains(particle)) {
738 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
739 reduceLambda(particle, result);
740 }
741 }
742 }
743 }
744 }
745
751 template <typename Lambda, typename A>
752 void reduceInRegion(Lambda reduceLambda, A &result, const std::array<double, 3> &lowerCorner,
753 const std::array<double, 3> &higherCorner,
754 IteratorBehavior behavior = autopas::IteratorBehavior::ownedOrHalo) const {
755 if (_isValid != ValidityState::invalid) {
756 if (not particlesToAddEmpty()) {
758 "VerletClusterLists::reduceInRegion() const: Error: particle container is valid, but _particlesToAdd isn't "
759 "empty!");
760 }
761 }
762 // If the particles are sorted into the towers, we can simply use the iteration over towers.
763 for (size_t i = 0; i < _towerBlock.size(); ++i) {
764 if (_towerBlock.ignoreCellForIteration(i, behavior)) {
765 continue;
766 }
767 auto &tower = _towerBlock[i];
768 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(i);
769 // particles can move over cell borders. Calculate the volume this cell's particles can be.
770 const auto towerLowCornerSkin = utils::ArrayMath::subScalar(towerLowCorner, this->getVerletSkin() * 0.5);
771 const auto towerHighCornerSkin = utils::ArrayMath::addScalar(towerHighCorner, this->getVerletSkin() * 0.5);
772 if (utils::boxesOverlap(towerLowCornerSkin, towerHighCornerSkin, lowerCorner, higherCorner)) {
773 tower.reduce(reduceLambda, result, lowerCorner, higherCorner, behavior);
774 }
775 }
776
777 if (_isValid == ValidityState::invalid) {
778 // If the particles are not sorted into the towers, we have to also iterate over _particlesToAdd.
779 for (auto &particlesToAddPerThread : _particlesToAdd) {
780 for (auto &particle : particlesToAddPerThread) {
781 if (behavior.contains(particle)) {
782 if (utils::inBox(particle.getR(), lowerCorner, higherCorner)) {
783 reduceLambda(particle, result);
784 }
785 }
786 }
787 }
788 }
789 }
790
791 void rebuildNeighborLists(TraversalInterface *traversal) override {
792 // The builder might have a different newton3 choice than the traversal. This typically only happens in unit tests
793 // when rebuildTowersAndClusters() was not called explicitly.
794 if (_isValid == ValidityState::invalid or traversal->getUseNewton3() != _builder->getNewton3()) {
795 // clear the lists buffer because clusters will be recreated
796 _neighborLists.clear();
798 }
799 _builder->rebuildNeighborListsAndFillClusters();
800
801 auto *clusterTraversalInterface = dynamic_cast<VCLTraversalInterface<Particle_T> *>(traversal);
802 if (clusterTraversalInterface) {
803 if (clusterTraversalInterface->needsStaticClusterThreadPartition()) {
805 }
806 } else {
808 "Trying to use a traversal of wrong type in VerletClusterLists::rebuildNeighborLists. TraversalID: {}",
809 traversal->getTraversalType());
810 }
811 }
812
820 template <bool inParallel, class LoopBody>
821 void traverseClusters(LoopBody &&loopBody) {
822 if (inParallel) {
823 traverseClustersParallel<LoopBody>(std::forward<LoopBody>(loopBody));
824 } else {
825 traverseClustersSequential<LoopBody>(std::forward<LoopBody>(loopBody));
826 }
827 }
828
833 [[nodiscard]] size_t size() const override {
834 size_t sum = std::accumulate(_towerBlock.begin(), _towerBlock.end(), 0,
835 [](size_t acc, const auto &tower) { return acc + tower.size(); });
836 sum = std::accumulate(_particlesToAdd.begin(), _particlesToAdd.end(), sum,
837 [](size_t acc, const auto &buffer) { return acc + buffer.size(); });
838 return sum;
839 }
840
844 [[nodiscard]] size_t getNumberOfParticles(IteratorBehavior behavior) const override {
845 // sum up all particles in towers that fulfill behavior
846 size_t sum = std::accumulate(_towerBlock.begin(), _towerBlock.end(), 0, [&behavior](size_t acc, const auto &tower) {
847 return acc + tower.getNumberOfParticles(behavior);
848 });
849
850 // Since we can not directly insert particles into towers without a rebuild of the whole data structure,
851 // _particlesToAdd is used to store all these particles temporarily until the next rebuild inserts them into the
852 // towers data structure. However, these particles already belong to the respective tower, so we have to count them
853 // as well.
854 sum = std::accumulate(
855 _particlesToAdd.begin(), _particlesToAdd.end(), sum, [&behavior](size_t acc, const auto &buffer) {
856 return acc +
857 (std::count_if(buffer.begin(), buffer.end(), [&behavior](auto p) { return behavior.contains(p); }));
858 });
859
860 return sum;
861 }
862
867 const auto &getClusterThreadPartition() const { return _clusterThreadPartition; }
868
873 auto getNumClusters() const { return _numClusters; }
874
879 auto getTowerSideLength() const { return _towerBlock.getTowerSideLength(); }
880
885 auto getTowersPerDimension() const { return _towerBlock.getTowersPerDim(); }
886
891 auto getClusterSize() const { return _clusterSize; }
892
897 auto getNumTowersPerInteractionLength() const { return _towerBlock.getNumTowersPerInteractionLength(); }
898
904 template <class Functor>
906 const auto numTowers = _towerBlock.size();
908 AUTOPAS_OPENMP(parallel for schedule(dynamic))
909 for (size_t index = 0; index < numTowers; index++) {
910 _towerBlock[index].loadSoA(functor);
911 }
912 }
913
919 template <class Functor>
921 const auto numTowers = _towerBlock.size();
923 AUTOPAS_OPENMP(parallel for schedule(dynamic))
924 for (size_t index = 0; index < numTowers; index++) {
925 _towerBlock[index].extractSoA(functor);
926 }
927 }
928
936 return _towerBlock.getTowerByIndex2D(x, y);
937 }
938
947
948 [[nodiscard]] const std::array<double, 3> &getBoxMax() const override { return _towerBlock.getBoxMax(); }
949
954 [[nodiscard]] const std::array<double, 3> &getHaloBoxMax() const { return _towerBlock.getHaloBoxMax(); }
955
956 [[nodiscard]] const std::array<double, 3> &getBoxMin() const override { return _towerBlock.getBoxMin(); }
957
962 [[nodiscard]] const std::array<double, 3> &getHaloBoxMin() const { return _towerBlock.getHaloBoxMin(); }
963
964 [[nodiscard]] double getCutoff() const override { return _cutoff; }
965
966 void setCutoff(double cutoff) override { _cutoff = cutoff; }
967
968 [[nodiscard]] double getVerletSkin() const override { return this->_skin; }
969
974 void setSkin(double skin) { this->_skin = skin; }
975
976 [[nodiscard]] double getInteractionLength() const override { return _cutoff + this->getVerletSkin(); }
977
978 void deleteAllParticles() override {
979 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
980 std::for_each(_particlesToAdd.begin(), _particlesToAdd.end(), [](auto &buffer) { buffer.clear(); });
981 std::for_each(_towerBlock.begin(), _towerBlock.end(), [](auto &tower) { tower.clear(); });
982 }
983
989 return _neighborLists;
990 }
991
997 void rebuildTowersAndClusters(bool newton3) {
998 using namespace utils::ArrayMath::literals;
999 // collect all particles to add from across the thread buffers
1000 typename decltype(_particlesToAdd)::value_type particlesToAdd;
1001 const size_t numParticlesToAdd =
1002 std::accumulate(_particlesToAdd.begin(), _particlesToAdd.end(), 0,
1003 [](size_t acc, const auto &buffer) { return acc + buffer.size(); });
1004 particlesToAdd.reserve(numParticlesToAdd);
1005 std::for_each(_particlesToAdd.begin(), _particlesToAdd.end(), [&](auto &particlesBuffer) {
1006 particlesToAdd.insert(particlesToAdd.end(), particlesBuffer.begin(), particlesBuffer.end());
1007 particlesBuffer.clear();
1008 });
1009
1010 const double interactionLength = _cutoff + this->_skin;
1011 _builder = std::make_unique<internal::VerletClusterListsRebuilder<Particle_T>>(
1012 _towerBlock, particlesToAdd, _neighborLists, _clusterSize, interactionLength * interactionLength, newton3);
1013
1014 _numClusters = _builder->rebuildTowersAndClusters();
1015
1016 _isValid.store(ValidityState::cellsValidListsInvalid, std::memory_order_relaxed);
1017 for (auto &tower : _towerBlock) {
1018 tower.setParticleDeletionObserver(this);
1019 }
1020 }
1021
1027 template <class LoopBody>
1028 void traverseClustersSequential(LoopBody &&loopBody) {
1029 for (size_t x = 0; x < _towerBlock.getTowersPerDim()[0]; x++) {
1030 for (size_t y = 0; y < _towerBlock.getTowersPerDim()[1]; y++) {
1031 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
1032 for (auto clusterIter = tower.getFirstOwnedCluster(); clusterIter < tower.getFirstTailHaloCluster();
1033 ++clusterIter) {
1034 loopBody(*clusterIter);
1035 }
1036 }
1037 }
1038 }
1039
1049 template <class LoopBody>
1050 void traverseClustersParallel(LoopBody &&loopBody) {
1051 const auto towersPerDimX = _towerBlock.getTowersPerDim()[0];
1052 const auto towersPerDimY = _towerBlock.getTowersPerDim()[1];
1054 AUTOPAS_OPENMP(parallel for schedule(dynamic) collapse(2))
1055 for (size_t x = 0; x < towersPerDimX; x++) {
1056 for (size_t y = 0; y < towersPerDimY; y++) {
1057 auto &tower = _towerBlock.getTowerByIndex2D(x, y);
1058
1059 for (auto clusterIter = tower.getFirstOwnedCluster(); clusterIter < tower.getFirstTailHaloCluster();
1060 ++clusterIter) {
1061 loopBody(*clusterIter);
1062 }
1063 }
1064 }
1065 }
1066
1067 protected:
1073 size_t numClusterPairs = 0;
1074 this->template traverseClusters<false>(
1075 [&numClusterPairs](auto &cluster) { numClusterPairs += cluster.getNeighbors()->size(); });
1076
1077 constexpr int minNumClusterPairsPerThread = 1000;
1078 auto numThreads =
1079 std::clamp(static_cast<int>(numClusterPairs / minNumClusterPairsPerThread), 1, autopas_get_max_threads());
1080
1081 size_t numClusterPairsPerThread =
1082 std::max(static_cast<unsigned long>(std::ceil(static_cast<double>(numClusterPairs) / numThreads)), 1ul);
1083 if (numClusterPairsPerThread * numThreads < numClusterPairs) {
1085 "VerletClusterLists::calculateClusterThreadPartition(): numClusterPairsPerThread ({}) * numThreads ({})={} "
1086 "should always "
1087 "be at least the amount of Cluster Pairs ({})!",
1088 numClusterPairsPerThread, numThreads, numClusterPairsPerThread * numThreads, numClusterPairs);
1089 }
1090 fillClusterRanges(numClusterPairsPerThread, numThreads);
1091 }
1092
1099 void fillClusterRanges(size_t numClusterPairsPerThread, int numThreads) {
1100 if (numClusterPairsPerThread < 1) {
1102 "VerletClusterLists::fillClusterRanges(): numClusterPairsPerThread({}) is less than one, this is not "
1103 "supported "
1104 "and will lead to errors!",
1105 numClusterPairsPerThread);
1106 }
1107 _clusterThreadPartition.resize(numThreads);
1108
1109 size_t currentThread = 0;
1110 size_t currentNumClustersToAdd = 0;
1111 size_t numClusterPairsTotal = 0;
1112 bool threadIsInitialized = false;
1113 // Iterate over the clusters of all towers
1114 for (size_t currentTowerIndex = 0; currentTowerIndex < _towerBlock.size(); currentTowerIndex++) {
1115 auto &currentTower = _towerBlock[currentTowerIndex];
1116 const auto firstOwnedClusterIndex =
1117 std::distance(currentTower.getClusters().begin(), currentTower.getFirstOwnedCluster());
1118 const auto firstTailHaloClusterIndex =
1119 std::distance(currentTower.getClusters().begin(), currentTower.getFirstTailHaloCluster());
1120 for (size_t currentClusterInTower = firstOwnedClusterIndex; currentClusterInTower < firstTailHaloClusterIndex;
1121 ++currentClusterInTower) {
1122 auto &currentCluster = currentTower.getCluster(currentClusterInTower);
1123
1124 // If on a new thread, start with the clusters for this thread here.
1125 if (not threadIsInitialized) {
1126 _clusterThreadPartition[currentThread] = {currentTowerIndex, currentClusterInTower, 0};
1127 threadIsInitialized = true;
1128 }
1129
1130 currentNumClustersToAdd++;
1131 numClusterPairsTotal += currentCluster.getNeighbors()->size();
1132
1133 // If the thread is finished, write number of clusters and start new thread.
1134 if (numClusterPairsTotal >= numClusterPairsPerThread * (currentThread + 1)) {
1135 // Add the number of clusters for the finished thread.
1136 _clusterThreadPartition[currentThread].numClusters += currentNumClustersToAdd;
1137 currentNumClustersToAdd = 0;
1138 // Go to next thread!
1139 currentThread++;
1140 // if we are already at the end of all threads, go back to last thread!
1141 // this is a safety precaution and should not really matter.
1142 if (currentThread >= numThreads) {
1143 --currentThread;
1144 threadIsInitialized = true;
1145 } else {
1146 threadIsInitialized = false;
1147 }
1148 }
1149 }
1150 }
1151 if (not threadIsInitialized) {
1152 _clusterThreadPartition[currentThread] = {0, 0, 0};
1153 }
1154 // Make sure the last cluster range contains the rest of the clusters, even if there is not the perfect number left.
1155 if (currentNumClustersToAdd != 0) {
1156 _clusterThreadPartition[currentThread].numClusters += currentNumClustersToAdd;
1157 }
1158 // Theoretically, some threads may still remain. This ensures that their numClusters are set to 0.
1159 while (++currentThread < numThreads) {
1160 _clusterThreadPartition[currentThread] = {0, 0, 0};
1161 }
1162 }
1163
1169 void notifyParticleDeleted() override {
1170 // this is potentially called from a threaded environment, so we have to make this atomic here!
1171 _isValid.store(ValidityState::invalid, std::memory_order_relaxed);
1172 }
1173
1174 private:
1189 template <bool regionIter>
1190 [[nodiscard]] std::tuple<size_t, size_t> advanceIteratorIndices(
1191 size_t cellIndex, size_t particleIndex, IteratorBehavior iteratorBehavior, const std::array<double, 3> &boxMin,
1192 const std::array<double, 3> &boxMax, const std::array<double, 3> &boxMinWithSafetyMargin,
1193 const std::array<double, 3> &boxMaxWithSafetyMargin, size_t endCellIndex) const {
1194 // Finding the indices for the next particle
1195 const size_t stride = (iteratorBehavior & IteratorBehavior::forceSequential) ? 1 : autopas_get_num_threads();
1196
1197 // helper function to determine if the cell can even contain particles of interest to the iterator
1198 auto towerIsRelevant = [&]() -> bool {
1199 // special case: Towers are not yet built, then the tower acting as buffer is always relevant.
1200 if (_towerBlock.size() == 1) {
1201 return true;
1202 }
1203 bool isRelevant = true;
1204 if constexpr (regionIter) {
1205 // is the cell in the region?
1206 const auto [towerLowCorner, towerHighCorner] = _towerBlock.getTowerBoundingBox(cellIndex);
1207 isRelevant =
1208 utils::boxesOverlap(towerLowCorner, towerHighCorner, boxMinWithSafetyMargin, boxMaxWithSafetyMargin);
1209 }
1210 return isRelevant;
1211 };
1212
1213 do {
1214 // advance to the next particle
1215 ++particleIndex;
1216 // If this breaches the end of a cell, find the next non-empty cell and reset particleIndex.
1217
1218 // If cell has wrong type, or there are no more particles in this cell jump to the next
1219 while (not towerIsRelevant() or particleIndex >= _towerBlock[cellIndex].getNumActualParticles()) {
1220 cellIndex += stride;
1221 particleIndex = 0;
1222
1223 // If we notice that there is nothing else to look at set invalid values, so we get a nullptr next time and
1224 // break.
1225 if (cellIndex > endCellIndex) {
1226 return {std::numeric_limits<size_t>::max(), particleIndex};
1227 }
1228 }
1229 } while (not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
1230 _towerBlock[cellIndex][particleIndex], iteratorBehavior, boxMin, boxMax));
1231
1232 // the indices returned at this point should always be valid
1233 return {cellIndex, particleIndex};
1234 }
1235
1237
1241 autopas::LoadEstimatorOption _loadEstimator;
1242
1246 size_t _clusterSize;
1247
1251 size_t _numClusters{0};
1252
1258 mutable std::vector<std::vector<Particle_T>> _particlesToAdd;
1259
1265 [[nodiscard]] bool particlesToAddEmpty(int bufferID = -1) const {
1266 if (bufferID == -1) {
1267 for (auto &threadBuffer : _particlesToAdd) {
1268 if (not threadBuffer.empty()) {
1269 return false;
1270 }
1271 }
1272 return true;
1273 } else {
1274 return _particlesToAdd[bufferID].empty();
1275 }
1276 }
1277
1286 template <class VecVec>
1287 void appendBuffersHelper(utils::optRef<VecVec> additionalVectors, VecVec &outVec) const {
1288 if (additionalVectors.has_value()) {
1289 outVec.reserve(_particlesToAdd.size() + additionalVectors->get().size());
1290 outVec.insert(outVec.end(), additionalVectors->get().begin(), additionalVectors->get().end());
1291 } else {
1292 outVec.reserve(_particlesToAdd.size());
1293 }
1294 for (auto &vec : _particlesToAdd) {
1295 outVec.push_back(&vec);
1296 }
1297 }
1298
1302 std::vector<ClusterRange> _clusterThreadPartition;
1303
1307 double _cutoff{};
1308
1312 enum class ValidityState : unsigned char {
1313 invalid = 0, // nothing is valid.
1314 cellsValidListsInvalid = 1, // only the cell structure is valid, but the lists are not.
1315 cellsAndListsValid = 2 // the cells and lists are valid
1316 };
1317
1321 std::atomic<ValidityState> _isValid{ValidityState::invalid};
1322
1326 std::unique_ptr<internal::VerletClusterListsRebuilder<Particle_T>> _builder;
1327
1332};
1333
1334} // 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:95
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:108
Functor base class.
Definition: Functor.h:41
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:40
constexpr bool end() const
Dummy to make range-based for loops work.
Definition: ParticleContainerInterface.h:248
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:65
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:411
void reduce(Lambda reduceLambda, A &result, IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo)
Reduce properties of particles as defined by a lambda function.
Definition: VerletClusterLists.h:533
ContainerOption getContainerType() const override
Get the ContainerType.
Definition: VerletClusterLists.h:109
void traverseClusters(LoopBody &&loopBody)
Helper method to iterate over all clusters.
Definition: VerletClusterLists.h:821
void deleteHaloParticles() override
Deletes all halo particles.
Definition: VerletClusterLists.h:226
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:503
size_t size() const override
Get the number of all particles stored in this container (owned + halo + dummy).
Definition: VerletClusterLists.h:833
auto getClusterSize() const
Returns the number of particles in each cluster.
Definition: VerletClusterLists.h:891
void rebuildNeighborLists(TraversalInterface *traversal) override
Rebuilds the neighbor lists for the next traversals.
Definition: VerletClusterLists.h:791
ContainerIterator< Particle_T, true, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior, utils::optRef< typename ContainerIterator< Particle_T, true, true >::ParticleVecType > additionalVectors=std::nullopt) override
Iterate over all particles in a specified region for(auto iter = container.getRegionIterator(lowCorne...
Definition: VerletClusterLists.h:581
BalancedTraversal::EstimatorFunction getLoadEstimatorFunction()
Generates the load estimation function depending on _loadEstimator.
Definition: VerletClusterLists.h:115
void setSkin(double skin)
Set the verlet skin length for the container.
Definition: VerletClusterLists.h:974
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:201
auto getTowersPerDimension() const
Returns the number of grids per dimension on the container.
Definition: VerletClusterLists.h:885
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:484
const std::array< double, 3 > & getHaloBoxMax() const
Get the upper corner of the halo box.
Definition: VerletClusterLists.h:954
ContainerIterator< Particle_T, true, false > begin(IteratorBehavior behavior=IteratorBehavior::ownedOrHalo, utils::optRef< typename ContainerIterator< Particle_T, true, false >::ParticleVecType > additionalVectors=std::nullopt) override
Iterate over all particles using for(auto iter = container.begin(); iter.isValid(); ++iter) .
Definition: VerletClusterLists.h:426
void setSoASortingThresholds(std::shared_ptr< const SortingThresholdInfoInterface > soaSortingThresholds) override
Set the SoA sorting-threshold for traversals that use the CellFunctor.
Definition: VerletClusterLists.h:182
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:298
void addParticleImpl(const Particle_T &p) override
Adds the given particle to the container.
Definition: VerletClusterLists.h:196
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:719
void traverseClustersSequential(LoopBody &&loopBody)
Helper method to sequentially iterate over all owned clusters.
Definition: VerletClusterLists.h:1028
void computeInteractions(TraversalInterface *traversal) override
Iterates over all particle multiples (e.g.
Definition: VerletClusterLists.h:148
double getInteractionLength() const override
Return the interaction length (cutoff+skin) of the container.
Definition: VerletClusterLists.h:976
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:368
double getVerletSkin() const override
Return the verletSkin of the container verletSkin.
Definition: VerletClusterLists.h:968
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:269
Particle_T ParticleType
Type of the Particle.
Definition: VerletClusterLists.h:61
internal::ClusterTowerBlock2D< Particle_T > & getTowerBlock()
Getter for the cell block.
Definition: VerletClusterLists.h:946
auto getNumTowersPerInteractionLength() const
Returns the towers per interaction length.
Definition: VerletClusterLists.h:897
const std::array< double, 3 > & getBoxMin() const override
Get the lower corner of the container without halo.
Definition: VerletClusterLists.h:956
ContainerIterator< Particle_T, false, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior, utils::optRef< typename ContainerIterator< Particle_T, false, true >::ParticleVecType > additionalVectors=std::nullopt) const override
Iterate over all particles in a specified region for(auto iter = container.getRegionIterator(lowCorne...
Definition: VerletClusterLists.h:611
void deleteAllParticles() override
Deletes all particles.
Definition: VerletClusterLists.h:978
void fillClusterRanges(size_t numClusterPairsPerThread, int numThreads)
Fills in the cluster ranges of the cluster thread partition.
Definition: VerletClusterLists.h:1099
void setAoSSortingThresholds(std::shared_ptr< const SortingThresholdInfoInterface > aosSortingThresholds) override
Set the aos-sorting-threshold for traversals that use the CellFunctor.
Definition: VerletClusterLists.h:176
VerletClusterLists(const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax, double cutoff, double skin, size_t clusterSize, LoadEstimatorOption loadEstimator=LoadEstimatorOption::none)
Constructor of the VerletClusterLists class.
Definition: VerletClusterLists.h:97
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:552
size_t getNumberOfParticles(IteratorBehavior behavior) const override
Get the number of particles with respect to the specified IteratorBehavior.
Definition: VerletClusterLists.h:844
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:675
void loadParticlesIntoSoAs(Functor &functor)
Loads all particles of the container in their correct SoA and generates the SoAViews for the clusters...
Definition: VerletClusterLists.h:905
void extractParticlesFromSoAs(Functor &functor)
Extracts all SoAs of the container into the particles.
Definition: VerletClusterLists.h:920
void traverseClustersParallel(LoopBody &&loopBody)
Helper method to iterate over all clusters in parallel.
Definition: VerletClusterLists.h:1050
const std::array< double, 3 > & getHaloBoxMin() const
Get the lower corner of the halo box.
Definition: VerletClusterLists.h:962
const internal::VerletClusterListsRebuilder< Particle_T >::NeighborListsBuffer_T & getNeighborLists() const
Get the neighbor lists buffer object.
Definition: VerletClusterLists.h:988
double getCutoff() const override
Return the cutoff of the container.
Definition: VerletClusterLists.h:964
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:935
auto getTowerSideLength() const
Returns the grid side length of the grids in the container.
Definition: VerletClusterLists.h:879
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:752
ContainerIterator< Particle_T, false, false > begin(IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo, utils::optRef< typename ContainerIterator< Particle_T, false, false >::ParticleVecType > additionalVectors=std::nullopt) const override
Iterate over all particles using for(auto iter = container.begin(); iter.isValid(); ++iter) .
Definition: VerletClusterLists.h:455
const std::array< double, 3 > & getBoxMax() const override
Get the upper corner of the container without halo.
Definition: VerletClusterLists.h:948
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:184
void setCutoff(double cutoff) override
Set the cutoff of the container.
Definition: VerletClusterLists.h:966
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:362
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:275
auto getNumClusters() const
Returns the number of clusters in this container.
Definition: VerletClusterLists.h:873
std::vector< Particle_T > updateContainer(bool keepNeighborListsValid) override
Updates the container.
Definition: VerletClusterLists.h:374
void rebuildTowersAndClusters(bool newton3)
Initializes a new VerletClusterListsRebuilder and uses it to rebuild the towers and the clusters.
Definition: VerletClusterLists.h:997
void calculateClusterThreadPartition()
Calculates a cluster thread partition that aims to give each thread about the same amount of cluster ...
Definition: VerletClusterLists.h:1072
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:642
bool updateHaloParticle(const Particle_T &haloParticle) override
Update a halo particle of the container with the given haloParticle.
Definition: VerletClusterLists.h:206
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:1169
const auto & getClusterThreadPartition() const
Returns the cluster-thread-partition.
Definition: VerletClusterLists.h:867
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:64
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:37
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
std::optional< std::reference_wrapper< T > > optRef
Short alias for std::optional<std::reference_wrapper<T>>
Definition: optRef.h:16
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:34
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:70
size_t startIndexInTower
The index of the first cluster in its tower.
Definition: VerletClusterLists.h:78
size_t startTowerIndex
The index of the tower that contains the first cluster.
Definition: VerletClusterLists.h:74
size_t numClusters
The number of clusters in the range.
Definition: VerletClusterLists.h:82