AutoPas  3.0.0
Loading...
Searching...
No Matches
LogicHandler.h
Go to the documentation of this file.
1
7#pragma once
8#include <atomic>
9#include <limits>
10#include <memory>
11#include <optional>
12#include <tuple>
13#include <type_traits>
14#include <vector>
15
30#include "autopas/utils/Timer.h"
39
40namespace autopas {
41
47template <typename Particle_T>
49 public:
57 LogicHandler(std::unordered_map<InteractionTypeOption::Value, std::unique_ptr<AutoTuner>> &autotuners,
58 const LogicHandlerInfo &logicHandlerInfo, unsigned int rebuildFrequency, const std::string &outputSuffix)
59 : _autoTunerRefs(autotuners),
60 _logicHandlerInfo(logicHandlerInfo),
61 _neighborListRebuildFrequency{rebuildFrequency},
62 _particleBuffer(autopas_get_max_threads()),
63 _haloParticleBuffer(autopas_get_max_threads()),
64 _remainderPairwiseInteractionHandler(_spatialLocks),
65 _remainderTriwiseInteractionHandler(_spatialLocks),
66 _verletClusterSize(logicHandlerInfo.verletClusterSize),
67 _sortingThreshold(logicHandlerInfo.sortingThreshold),
68 _iterationLogger(outputSuffix, std::any_of(autotuners.begin(), autotuners.end(),
69 [](const auto &tuner) { return tuner.second->canMeasureEnergy(); })),
70 _flopLogger(outputSuffix),
71 _liveInfoLogger(outputSuffix) {
72 using namespace autopas::utils::ArrayMath::literals;
73 // Initialize AutoPas with tuners for given interaction types
74 for (const auto &[interactionType, tuner] : autotuners) {
75 _interactionTypes.insert(interactionType);
76
77 const auto configuration = tuner->getCurrentConfig();
78 // initialize the container and make sure it is valid
79 _currentContainerSelectorInfo = ContainerSelectorInfo{_logicHandlerInfo.boxMin,
80 _logicHandlerInfo.boxMax,
81 _logicHandlerInfo.cutoff,
82 configuration.cellSizeFactor,
83 _logicHandlerInfo.verletSkin,
84 _verletClusterSize,
85 _sortingThreshold,
86 configuration.loadEstimator};
87 _currentContainer =
88 ContainerSelector<Particle_T>::generateContainer(configuration.container, _currentContainerSelectorInfo);
89 checkMinimalSize();
90 }
91
92 // initialize locks needed for remainder traversal
93 const auto interactionLength = logicHandlerInfo.cutoff + logicHandlerInfo.verletSkin;
94 const auto interactionLengthInv = 1. / interactionLength;
95 const auto boxLengthWithHalo = logicHandlerInfo.boxMax - logicHandlerInfo.boxMin + (2 * interactionLength);
96 initSpatialLocks(boxLengthWithHalo, interactionLengthInv);
97 }
98
103 ParticleContainerInterface<Particle_T> &getContainer() { return *_currentContainer; }
104
110 [[nodiscard]] std::vector<Particle_T> collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer) {
111 const auto &boxMin = _currentContainer->getBoxMin();
112 const auto &boxMax = _currentContainer->getBoxMax();
113 std::vector<Particle_T> leavingBufferParticles{};
114 for (auto &cell : _particleBuffer) {
115 auto &buffer = cell._particles;
116 if (insertOwnedParticlesToContainer) {
117 // Can't be const because we potentially modify ownership before re-adding
118 for (auto &p : buffer) {
119 if (p.isDummy()) {
120 continue;
121 }
122 if (utils::inBox(p.getR(), boxMin, boxMax)) {
123 p.setOwnershipState(OwnershipState::owned);
124 _currentContainer->addParticle(p);
125 } else {
126 leavingBufferParticles.push_back(p);
127 }
128 }
129 buffer.clear();
130 } else {
131 for (auto iter = buffer.begin(); iter < buffer.end();) {
132 auto &p = *iter;
133
134 auto fastRemoveP = [&]() {
135 // Fast remove of particle, i.e., swap with last entry && pop.
136 std::swap(p, buffer.back());
137 buffer.pop_back();
138 // Do not increment the iter afterward!
139 };
140 if (p.isDummy()) {
141 // We remove dummies!
142 fastRemoveP();
143 // In case we swapped a dummy here, don't increment the iterator and do another iteration to check again.
144 continue;
145 }
146 // if p was a dummy a new particle might now be at the memory location of p so we need to check that.
147 // We also just might have deleted the last particle in the buffer in that case the inBox check is meaningless
148 if (not buffer.empty() and utils::notInBox(p.getR(), boxMin, boxMax)) {
149 leavingBufferParticles.push_back(p);
150 fastRemoveP();
151 } else {
152 ++iter;
153 }
154 }
155 }
156 }
157 return leavingBufferParticles;
158 }
159
163 [[nodiscard]] std::vector<Particle_T> updateContainer() {
164#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
166#endif
167 bool doDataStructureUpdate = not neighborListsAreValid();
168
169 if (_functorCalls > 0) {
170 // Bump iteration counters for all autotuners
171 for (const auto &[interactionType, autoTuner] : _autoTunerRefs) {
172 const bool needsToWait = checkTuningStates(interactionType);
173 // Called before bumpIterationCounters as it would return false after that.
174 if (autoTuner->inLastTuningIteration()) {
175 _iterationAtEndOfLastTuningPhase = _iteration;
176 }
177 autoTuner->bumpIterationCounters(needsToWait);
178 }
179
180 // We will do a rebuild in this timestep
181 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
182 _stepsSinceLastListRebuild = 0;
183 }
184 ++_stepsSinceLastListRebuild;
185 _currentContainer->setStepsSinceLastRebuild(_stepsSinceLastListRebuild);
186 ++_iteration;
187 }
188
189 // The next call also adds particles to the container if doDataStructureUpdate is true.
190 auto leavingBufferParticles = collectLeavingParticlesFromBuffer(doDataStructureUpdate);
191
192 AutoPasLog(TRACE, "Initiating container update.");
193 auto leavingParticles = _currentContainer->updateContainer(not doDataStructureUpdate);
194 leavingParticles.insert(leavingParticles.end(), leavingBufferParticles.begin(), leavingBufferParticles.end());
195
196 // Substract the amount of leaving particles from the number of owned particles.
197 _numParticlesOwned.fetch_sub(leavingParticles.size(), std::memory_order_relaxed);
198 // updateContainer deletes all halo particles.
199 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
200 _numParticlesHalo.store(0, std::memory_order_relaxed);
201 return leavingParticles;
202 }
203
210 std::vector<Particle_T> resizeBox(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax) {
211 using namespace autopas::utils::ArrayMath::literals;
212 const auto &oldMin = _currentContainer->getBoxMin();
213 const auto &oldMax = _currentContainer->getBoxMax();
214
215 // if nothing changed, do nothing
216 if (oldMin == boxMin and oldMax == boxMax) {
217 return {};
218 }
219
220 // sanity check that new size is actually positive
221 for (size_t i = 0; i < boxMin.size(); ++i) {
222 if (boxMin[i] >= boxMax[i]) {
224 "New box size in dimension {} is not positive!\nboxMin[{}] = {}\nboxMax[{}] = {}", i, i, boxMin[i], i,
225 boxMax[i]);
226 }
227 }
228
229 // warn if domain changes too drastically
230 const auto newLength = boxMax - boxMin;
231 const auto oldLength = oldMax - oldMin;
232 const auto relDiffLength = newLength / oldLength;
233 for (size_t i = 0; i < newLength.size(); ++i) {
234 // warning threshold is set arbitrary and up for change if needed
235 if (relDiffLength[i] > 1.3 or relDiffLength[i] < 0.7) {
236 AutoPasLog(WARN,
237 "LogicHandler.resize(): Domain size changed drastically in dimension {}! Gathered AutoTuning "
238 "information might not be applicable anymore!\n"
239 "Size old box : {}\n"
240 "Size new box : {}\n"
241 "Relative diff: {}",
243 utils::ArrayUtils::to_string(relDiffLength));
244 }
245 }
246
247 // The new box size is valid, so update the current container info.
248 _currentContainerSelectorInfo.boxMin = boxMin;
249 _currentContainerSelectorInfo.boxMax = boxMax;
250
251 // check all particles
252 std::vector<Particle_T> particlesNowOutside;
253 for (auto pIter = _currentContainer->begin(); pIter.isValid(); ++pIter) {
254 // make sure only owned ones are present
255 if (not pIter->isOwned()) {
257 "LogicHandler::resizeBox() encountered non owned particle. "
258 "When calling resizeBox() these should be already deleted. "
259 "This could be solved by calling updateContainer() before resizeBox().");
260 }
261 // owned particles that are now outside are removed from the container and returned
262 if (not utils::inBox(pIter->getR(), boxMin, boxMax)) {
263 particlesNowOutside.push_back(*pIter);
266 }
267 }
268
269 // Resize by generating a new container with the new box size and moving all particles to it.
270 auto newContainer = ContainerSelector<Particle_T>::generateContainer(_currentContainer->getContainerType(),
271 _currentContainerSelectorInfo);
272 setCurrentContainer(std::move(newContainer));
273 // The container might have changed sufficiently that we would need a different number of spatial locks.
274 const auto boxLength = boxMax - boxMin;
275 const auto interactionLengthInv = 1. / _currentContainer->getInteractionLength();
276 initSpatialLocks(boxLength, interactionLengthInv);
277
278 // Set this flag, s.t., the container is rebuilt!
279 _neighborListsAreValid.store(false, std::memory_order_relaxed);
280
281 return particlesNowOutside;
282 }
283
290 void reserve(size_t numParticles) {
291 const auto numParticlesHaloEstimate = autopas::utils::NumParticlesEstimator::estimateNumHalosUniform(
292 numParticles, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
293 _currentContainer->getInteractionLength());
294 reserve(numParticles, numParticlesHaloEstimate);
295 }
296
303 void reserve(size_t numParticles, size_t numHaloParticles) {
304 const auto numHaloParticlesPerBuffer = numHaloParticles / _haloParticleBuffer.size();
305 for (auto &buffer : _haloParticleBuffer) {
306 buffer.reserve(numHaloParticlesPerBuffer);
307 }
308 // there is currently no good heuristic for this buffer, so reuse the one for halos.
309 for (auto &buffer : _particleBuffer) {
310 buffer.reserve(numHaloParticlesPerBuffer);
311 }
312
313 // reserve is called for the container only in the rebuild iterations.
314 // during non-rebuild iterations, particles are not added in the container but in buffer.
315 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
316 _currentContainer->reserve(numParticles, numHaloParticles);
317 }
318 }
319
323 void addParticle(const Particle_T &p) {
324 // first check that the particle actually belongs in the container
325 const auto &boxMin = _currentContainer->getBoxMin();
326 const auto &boxMax = _currentContainer->getBoxMax();
327 if (utils::notInBox(p.getR(), boxMin, boxMax)) {
329 "LogicHandler: Trying to add a particle that is not in the bounding box.\n"
330 "Box Min {}\n"
331 "Box Max {}\n"
332 "{}",
333 boxMin, boxMax, p.toString());
334 }
335 Particle_T particleCopy = p;
336 particleCopy.setOwnershipState(OwnershipState::owned);
337 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
338 // Container has to (about to) be invalid to be able to add Particles!
339 _currentContainer->template addParticle<false>(particleCopy);
340 } else {
341 // If the container is valid, we add it to the particle buffer.
342 _particleBuffer[autopas_get_thread_num()].addParticle(particleCopy);
343 }
344 _numParticlesOwned.fetch_add(1, std::memory_order_relaxed);
345 }
346
350 void addHaloParticle(const Particle_T &haloParticle) {
351 const auto &boxMin = _currentContainer->getBoxMin();
352 const auto &boxMax = _currentContainer->getBoxMax();
353 Particle_T haloParticleCopy = haloParticle;
354 if (utils::inBox(haloParticleCopy.getR(), boxMin, boxMax)) {
356 "LogicHandler: Trying to add a halo particle that is not outside the box of the container.\n"
357 "Box Min {}\n"
358 "Box Max {}\n"
359 "{}",
360 utils::ArrayUtils::to_string(boxMin), utils::ArrayUtils::to_string(boxMax), haloParticleCopy.toString());
361 }
362 haloParticleCopy.setOwnershipState(OwnershipState::halo);
363 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
364 // If the neighbor lists are not valid, we can add the particle.
365 _currentContainer->template addHaloParticle</* checkInBox */ false>(haloParticleCopy);
366 } else {
367 // Check if we can update an existing halo(dummy) particle.
368 bool updated = _currentContainer->updateHaloParticle(haloParticleCopy);
369 if (not updated) {
370 // If we couldn't find an existing particle, add it to the halo particle buffer.
371 _haloParticleBuffer[autopas_get_thread_num()].addParticle(haloParticleCopy);
372 }
373 }
374 _numParticlesHalo.fetch_add(1, std::memory_order_relaxed);
375 }
376
381 _neighborListsAreValid.store(false, std::memory_order_relaxed);
382 _currentContainer->deleteAllParticles();
383 std::for_each(_particleBuffer.begin(), _particleBuffer.end(), [](auto &buffer) { buffer.clear(); });
384 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
385 // all particles are gone -> reset counters.
386 _numParticlesOwned.store(0, std::memory_order_relaxed);
387 _numParticlesHalo.store(0, std::memory_order_relaxed);
388 }
389
396 std::tuple<bool, bool> deleteParticleFromBuffers(Particle_T &particle) {
397 // find the buffer the particle belongs to
398 auto &bufferCollection = particle.isOwned() ? _particleBuffer : _haloParticleBuffer;
399 for (auto &cell : bufferCollection) {
400 auto &buffer = cell._particles;
401 // if the address of the particle is between start and end of the buffer it is in this buffer
402 if (not buffer.empty() and &(buffer.front()) <= &particle and &particle <= &(buffer.back())) {
403 const bool isRearParticle = &particle == &buffer.back();
404 // swap-delete
405 particle = buffer.back();
406 buffer.pop_back();
407 return {true, not isRearParticle};
408 }
409 }
410 return {false, true};
411 }
412
418 void decreaseParticleCounter(Particle_T &particle) {
419 if (particle.isOwned()) {
420 _numParticlesOwned.fetch_sub(1, std::memory_order_relaxed);
421 } else {
422 _numParticlesHalo.fetch_sub(1, std::memory_order_relaxed);
423 }
424 }
425
446 template <class Functor>
447 bool computeInteractionsPipeline(Functor *functor, const InteractionTypeOption &interactionType);
448
455 template <class Iterator>
456 typename Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior) {
457 typename Iterator::ParticleVecType additionalVectors;
458 if (not(behavior & IteratorBehavior::containerOnly)) {
459 additionalVectors.reserve(static_cast<bool>(behavior & IteratorBehavior::owned) * _particleBuffer.size() +
460 static_cast<bool>(behavior & IteratorBehavior::halo) * _haloParticleBuffer.size());
461 if (behavior & IteratorBehavior::owned) {
462 for (auto &buffer : _particleBuffer) {
463 // Don't insert empty buffers. This also means that we won't pick up particles added during iterating if they
464 // go to the buffers. But since we wouldn't pick them up if they go into the container to a cell that the
465 // iterators already passed this is unsupported anyways.
466 if (not buffer.isEmpty()) {
467 additionalVectors.push_back(&(buffer._particles));
468 }
469 }
470 }
471 if (behavior & IteratorBehavior::halo) {
472 for (auto &buffer : _haloParticleBuffer) {
473 if (not buffer.isEmpty()) {
474 additionalVectors.push_back(&(buffer._particles));
475 }
476 }
477 }
478 }
479 return additionalVectors;
480 }
481
486 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, false>>(behavior);
487 return _currentContainer->begin(behavior, std::ref(additionalVectors));
488 }
489
493 ContainerIterator<Particle_T, false, false> begin(IteratorBehavior behavior) const {
494 auto additionalVectors =
496 behavior);
497 return _currentContainer->begin(behavior, std::ref(additionalVectors));
498 }
499
503 ContainerIterator<Particle_T, true, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
504 const std::array<double, 3> &higherCorner,
505 IteratorBehavior behavior) {
506 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
507 for (size_t d = 0; d < 3; ++d) {
508 if (lowerCorner[d] > higherCorner[d]) {
510 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
511 "Lower corner: {}\n"
512 "Upper corner: {}",
513 lowerCorner, higherCorner);
514 }
515 }
516
517 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, true>>(behavior);
518 return _currentContainer->getRegionIterator(lowerCorner, higherCorner, behavior, std::ref(additionalVectors));
519 }
520
524 ContainerIterator<Particle_T, false, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
525 const std::array<double, 3> &higherCorner,
526 IteratorBehavior behavior) const {
527 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
528 for (size_t d = 0; d < 3; ++d) {
529 if (lowerCorner[d] > higherCorner[d]) {
531 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
532 "Lower corner: {}\n"
533 "Upper corner: {}",
534 lowerCorner, higherCorner);
535 }
536 }
537
538 auto additionalVectors =
540 return std::as_const(_currentContainer)
541 ->getRegionIterator(lowerCorner, higherCorner, behavior, std::ref(additionalVectors));
542 }
543
548 [[nodiscard]] unsigned long getNumberOfParticlesOwned() const { return _numParticlesOwned; }
549
554 [[nodiscard]] unsigned long getNumberOfParticlesHalo() const { return _numParticlesHalo; }
555
561 bool checkTuningStates(const InteractionTypeOption &interactionType) {
562 // Goes over all pairs in _autoTunerRefs and returns true as soon as one is `inTuningPhase()`.
563 // The tuner associated with the given interaction type is ignored.
564 return std::any_of(std::begin(_autoTunerRefs), std::end(_autoTunerRefs), [&](const auto &entry) {
565 return not(entry.first == interactionType) and entry.second->inTuningPhase();
566 });
567 }
568
581 template <class Functor>
582 [[nodiscard]] std::tuple<std::unique_ptr<TraversalInterface>, bool> isConfigurationApplicable(
583 const Configuration &config, Functor &functor);
584
595 void setParticleBuffers(const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
596 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
597
605 std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
606 getParticleBuffers() const;
607
619 [[nodiscard]] double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase = false) const {
620#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
621 const auto numRebuilds = considerOnlyLastNonTuningPhase ? _numRebuildsInNonTuningPhase : _numRebuilds;
622 // The total number of iterations is iteration + 1
623 const auto iterationCount =
624 considerOnlyLastNonTuningPhase ? _iteration - _iterationAtEndOfLastTuningPhase : _iteration + 1;
625 if (numRebuilds == 0) {
626 return static_cast<double>(_neighborListRebuildFrequency);
627 } else {
628 return static_cast<double>(iterationCount) / numRebuilds;
629 }
630#else
631 return static_cast<double>(_neighborListRebuildFrequency);
632#endif
633 }
634
642 double getVelocityMethodRFEstimate(const double skin, const double deltaT) const {
644 // Initialize the maximum velocity to zero
645 double maxVelocity = 0;
646 // Iterate over the owned particles in container to determine maximum velocity
647 AUTOPAS_OPENMP(parallel reduction(max : maxVelocity))
648 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
649 std::array<double, 3> tempVel = iter->getV();
650 double tempVelAbs = sqrt(dot(tempVel, tempVel));
651 maxVelocity = std::max(tempVelAbs, maxVelocity);
652 }
653 // return the rebuild frequency estimate
654 return skin / maxVelocity / deltaT / 2;
655 }
661
667
673
683
684 private:
697 void initSpatialLocks(const std::array<double, 3> &boxLength, double interactionLengthInv) {
698 using namespace autopas::utils::ArrayMath::literals;
701
702 // The maximum number of spatial locks is capped at 1e6.
703 // This limit is chosen more or less arbitrary. It is big enough so that our regular MD simulations
704 // fall well within it and small enough so that no memory issues arise.
705 // There were no rigorous tests for an optimal number of locks.
706 // Without this cap, very large domains (or tiny cutoffs) would generate an insane number of locks,
707 // that could blow up the memory.
708 constexpr size_t maxNumSpacialLocks{1000000};
709
710 // One lock per interaction length or less if this would generate too many.
711 const std::array<size_t, 3> locksPerDim = [&]() {
712 // First naively calculate the number of locks if we simply take the desired cell length.
713 // Ceil because both decisions are possible, and we are generous gods.
714 const std::array<size_t, 3> locksPerDimNaive =
715 static_cast_copy_array<size_t>(ceil(boxLength * interactionLengthInv));
716 const auto totalLocksNaive =
717 std::accumulate(locksPerDimNaive.begin(), locksPerDimNaive.end(), 1ul, std::multiplies<>());
718 // If the number of locks is within the limits everything is fine and we can return.
719 if (totalLocksNaive <= maxNumSpacialLocks) {
720 return locksPerDimNaive;
721 } else {
722 // If the number of locks grows too large, calculate the locks per dimension proportionally to the side lengths.
723 // Calculate side length relative to dimension 0.
724 const std::array<double, 3> boxSideProportions = {
725 1.,
726 boxLength[0] / boxLength[1],
727 boxLength[0] / boxLength[2],
728 };
729 // With this, calculate the number of locks the first dimension should receive.
730 const auto prodProportions =
731 std::accumulate(boxSideProportions.begin(), boxSideProportions.end(), 1., std::multiplies<>());
732 // Needs floor, otherwise we exceed the limit.
733 const auto locksInFirstDimFloat = std::floor(std::cbrt(maxNumSpacialLocks * prodProportions));
734 // From this and the proportions relative to the first dimension, we can calculate the remaining number of locks
735 const std::array<size_t, 3> locksPerDimLimited = {
736 static_cast<size_t>(locksInFirstDimFloat), // omitted div by 1
737 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[1]),
738 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[2]),
739 };
740 return locksPerDimLimited;
741 }
742 }();
743 _spatialLocks.resize(locksPerDim[0]);
744 for (auto &lockVecVec : _spatialLocks) {
745 lockVecVec.resize(locksPerDim[1]);
746 for (auto &lockVec : lockVecVec) {
747 lockVec.resize(locksPerDim[2]);
748 for (auto &lockPtr : lockVec) {
749 if (not lockPtr) {
750 lockPtr = std::make_unique<std::mutex>();
751 }
752 }
753 }
754 }
755 }
756
764 template <class Functor>
765 std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> selectConfiguration(
766 Functor &functor, const InteractionTypeOption &interactionType);
767
772 void setCurrentContainer(std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer);
773
788 template <class Functor>
789 IterationMeasurements computeInteractions(Functor &functor, TraversalInterface &traversal);
790
801 template <class Functor>
802 void computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA);
803
809 void checkMinimalSize() const;
810
811 const LogicHandlerInfo _logicHandlerInfo;
815 unsigned int _neighborListRebuildFrequency;
816
820 unsigned int _verletClusterSize;
821
825 size_t _numRebuilds{0};
826
831 size_t _numRebuildsInNonTuningPhase{0};
832
836 size_t _sortingThreshold;
837
841 std::unordered_map<InteractionTypeOption::Value, std::unique_ptr<AutoTuner>> &_autoTunerRefs;
842
846 std::unique_ptr<ParticleContainerInterface<Particle_T>> _currentContainer{nullptr};
847
851 ContainerSelectorInfo _currentContainerSelectorInfo;
852
856 RemainderPairwiseInteractionHandler<Particle_T> _remainderPairwiseInteractionHandler;
857
861 RemainderTriwiseInteractionHandler<Particle_T> _remainderTriwiseInteractionHandler;
862
866 std::set<InteractionTypeOption> _interactionTypes{};
867
871 std::atomic<bool> _neighborListsAreValid{false};
872
876 unsigned int _stepsSinceLastListRebuild{0};
877
881 unsigned int _functorCalls{0};
882
886 unsigned int _iteration{0};
887
891 unsigned int _iterationAtEndOfLastTuningPhase{0};
892
896 std::atomic<size_t> _numParticlesOwned{0ul};
897
901 std::atomic<size_t> _numParticlesHalo{0ul};
902
906 std::vector<FullParticleCell<Particle_T>> _particleBuffer;
907
911 std::vector<FullParticleCell<Particle_T>> _haloParticleBuffer;
912
918 std::vector<std::vector<std::vector<std::unique_ptr<std::mutex>>>> _spatialLocks;
919
923 IterationLogger _iterationLogger;
924
929 bool _neighborListInvalidDoDynamicRebuild{false};
930
934 void updateRebuildPositions();
935
939 LiveInfoLogger _liveInfoLogger;
940
944 FLOPLogger _flopLogger;
945};
946
947template <typename Particle_T>
949#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
950 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
951 // e.g. neighbour list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
952 // require a rebuild of neighbor lists.
953 AUTOPAS_OPENMP(parallel)
954 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
955 iter->resetRAtRebuild();
956 }
957#endif
958}
959
960template <typename Particle_T>
962 // check boxSize at least cutoff + skin
963 for (unsigned int dim = 0; dim < 3; ++dim) {
964 if (_currentContainer->getBoxMax()[dim] - _currentContainer->getBoxMin()[dim] <
965 _currentContainer->getInteractionLength()) {
967 "Box (boxMin[{}]={} and boxMax[{}]={}) is too small.\nHas to be at least cutoff({}) + skin({}) = {}.", dim,
968 _currentContainer->getBoxMin()[dim], dim, _currentContainer->getBoxMax()[dim], _currentContainer->getCutoff(),
969 _currentContainer->getVerletSkin(), _currentContainer->getCutoff() + _currentContainer->getVerletSkin());
970 }
971 }
972}
973
974template <typename Particle_T>
976 return _neighborListInvalidDoDynamicRebuild;
977}
978
979template <typename Particle_T>
981 // Implement rebuild indicator as function, so it is only evaluated when needed.
982 const auto needRebuild = [&](const InteractionTypeOption &interactionOption) {
983 return _interactionTypes.count(interactionOption) != 0 and
984 _autoTunerRefs[interactionOption]->willRebuildNeighborLists();
985 };
986
987 if (_stepsSinceLastListRebuild >= _neighborListRebuildFrequency
988#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
989 or getNeighborListsInvalidDoDynamicRebuild()
990#endif
991 or needRebuild(InteractionTypeOption::pairwise) or needRebuild(InteractionTypeOption::triwise)) {
992 _neighborListsAreValid.store(false, std::memory_order_relaxed);
993 }
994
995 return _neighborListsAreValid.load(std::memory_order_relaxed);
996}
997
998template <typename Particle_T>
1000#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1001 const auto skin = getContainer().getVerletSkin();
1002 // (skin/2)^2
1003 const auto halfSkinSquare = skin * skin * 0.25;
1004 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
1005 // e.g. neighbour list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
1006 // require a rebuild of neighbor lists.
1007 AUTOPAS_OPENMP(parallel reduction(or : _neighborListInvalidDoDynamicRebuild))
1008 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
1009 const auto distance = iter->calculateDisplacementSinceRebuild();
1010 const double distanceSquare = utils::ArrayMath::dot(distance, distance);
1011
1012 _neighborListInvalidDoDynamicRebuild |= distanceSquare >= halfSkinSquare;
1013 }
1014#endif
1015}
1016
1017template <typename Particle_T>
1019 _neighborListInvalidDoDynamicRebuild = false;
1020}
1021
1022template <typename Particle_T>
1024 const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1025 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1026 auto exchangeBuffer = [](const auto &newBuffers, auto &oldBuffers, auto &particleCounter) {
1027 // sanity check
1028 if (oldBuffers.size() < newBuffers.size()) {
1030 "The number of new buffers ({}) is larger than number of existing buffers ({})!", newBuffers.size(),
1031 oldBuffers.size());
1032 }
1033
1034 // we will clear the old buffers so subtract the particles from the counters.
1035 const auto numParticlesInOldBuffers =
1036 std::transform_reduce(oldBuffers.begin(), std::next(oldBuffers.begin(), newBuffers.size()), 0, std::plus<>(),
1037 [](const auto &cell) { return cell.size(); });
1038 particleCounter.fetch_sub(numParticlesInOldBuffers, std::memory_order_relaxed);
1039
1040 // clear the old buffers and copy the content of the new buffers over.
1041 size_t numParticlesInNewBuffers = 0;
1042 for (size_t i = 0; i < newBuffers.size(); ++i) {
1043 oldBuffers[i].clear();
1044 for (const auto &p : newBuffers[i]) {
1045 ++numParticlesInNewBuffers;
1046 oldBuffers[i].addParticle(p);
1047 }
1048 }
1049 // update the counters.
1050 particleCounter.fetch_add(numParticlesInNewBuffers, std::memory_order_relaxed);
1051 };
1052
1053 exchangeBuffer(particleBuffers, _particleBuffer, _numParticlesOwned);
1054 exchangeBuffer(haloParticleBuffers, _haloParticleBuffer, _numParticlesHalo);
1055}
1056
1057template <typename Particle_T>
1058std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
1060 return {_particleBuffer, _haloParticleBuffer};
1061}
1062
1063template <typename Particle_T>
1064template <class Functor>
1066 // Helper to derive the Functor type at compile time
1067 constexpr auto interactionType = [] {
1069 return InteractionTypeOption::pairwise;
1070 } else if (utils::isTriwiseFunctor<Functor>()) {
1071 return InteractionTypeOption::triwise;
1072 } else {
1074 "LogicHandler::computeInteractions(): Functor is not valid. Only pairwise and triwise functors are "
1075 "supported. "
1076 "Please use a functor derived from "
1077 "PairwiseFunctor or TriwiseFunctor.");
1078 }
1079 }();
1080
1081 auto &autoTuner = *_autoTunerRefs[interactionType];
1082#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1083 if (autoTuner.inFirstTuningIteration()) {
1084 _numRebuildsInNonTuningPhase = 0;
1085 }
1086
1087 // Rebuild frequency estimation should be triggered early in the tuning phase.
1088 // This is necessary because runtime prediction for each trial configuration
1089 // depends on the rebuild frequency.
1090 // To avoid the influence of poorly initialized velocities at the start of the simulation,
1091 // the rebuild frequency is estimated at iteration corresponding to the last sample of the first configuration.
1092 // The rebuild frequency estimated here is then reused for the remainder of the tuning phase.
1093 if (autoTuner.inFirstConfigurationLastSample()) {
1094 // Fetch the needed information for estimating the rebuild frequency from _logicHandlerInfo
1095 // and estimate the current rebuild frequency using the velocity method.
1096 double rebuildFrequencyEstimate =
1097 getVelocityMethodRFEstimate(_logicHandlerInfo.verletSkin, _logicHandlerInfo.deltaT);
1098 double userProvidedRF = static_cast<double>(_neighborListRebuildFrequency);
1099 // The user defined rebuild frequency is considered as the upper bound.
1100 // If velocity method estimate exceeds upper bound, set the rebuild frequency to the user defined value.
1101 // This is done because we currently use the user defined rebuild frequency as the upper bound to avoid expensive
1102 // buffer interactions.
1103 if (rebuildFrequencyEstimate > userProvidedRF) {
1104 autoTuner.setRebuildFrequency(userProvidedRF);
1105 } else {
1106 autoTuner.setRebuildFrequency(rebuildFrequencyEstimate);
1107 }
1108 }
1109#endif
1110 utils::Timer timerTotal;
1111 utils::Timer timerRebuild;
1112 utils::Timer timerComputeInteractions;
1113 utils::Timer timerComputeRemainder;
1114 long energyTotalRebuild;
1115
1116 const bool energyMeasurementsPossible = autoTuner.resetEnergy();
1117 timerTotal.start();
1118 timerRebuild.start();
1119 functor.initTraversal();
1120
1121 // if lists are not valid -> rebuild;
1122 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
1123#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1124 this->updateRebuildPositions();
1125#endif
1126 _currentContainer->rebuildNeighborLists(&traversal);
1127#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1128 this->resetNeighborListsInvalidDoDynamicRebuild();
1129 _numRebuilds++;
1130 if (not autoTuner.inTuningPhase()) {
1131 _numRebuildsInNonTuningPhase++;
1132 }
1133#endif
1134 _neighborListsAreValid.store(true, std::memory_order_relaxed);
1135 }
1136 timerRebuild.stop();
1137 std::tie(std::ignore, std::ignore, std::ignore, energyTotalRebuild) = autoTuner.sampleEnergy();
1138
1139 timerComputeInteractions.start();
1140 _currentContainer->computeInteractions(&traversal);
1141 timerComputeInteractions.stop();
1142
1143 timerComputeRemainder.start();
1144 const bool newton3 = autoTuner.getCurrentConfig().newton3;
1145 const auto dataLayout = autoTuner.getCurrentConfig().dataLayout;
1146 computeRemainderInteractions(functor, newton3, dataLayout);
1147 timerComputeRemainder.stop();
1148
1149 functor.endTraversal(newton3);
1150
1151 const auto [energyWatts, energyJoules, energyDeltaT, energyTotal] = autoTuner.sampleEnergy();
1152 timerTotal.stop();
1153
1154 constexpr auto nanD = std::numeric_limits<double>::quiet_NaN();
1155 constexpr auto nanL = std::numeric_limits<long>::quiet_NaN();
1156 return {timerComputeInteractions.getTotalTime(),
1157 timerComputeRemainder.getTotalTime(),
1158 timerRebuild.getTotalTime(),
1159 timerTotal.getTotalTime(),
1160 energyMeasurementsPossible,
1161 energyMeasurementsPossible ? energyWatts : nanD,
1162 energyMeasurementsPossible ? energyJoules : nanD,
1163 energyMeasurementsPossible ? energyDeltaT : nanD,
1164 energyMeasurementsPossible ? energyTotalRebuild : nanL,
1165 energyMeasurementsPossible ? energyTotal - energyTotalRebuild
1166 : nanL, // ComputeInteractions + Remainder Traversal energy consumption
1167 energyMeasurementsPossible ? energyTotal : nanL};
1168}
1169
1170template <typename Particle_T>
1171template <class Functor>
1172void LogicHandler<Particle_T>::computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA) {
1173 withStaticContainerType(*_currentContainer, [&](auto &actualContainerType) {
1174 if constexpr (utils::isPairwiseFunctor<Functor>()) {
1175 if (newton3) {
1176 _remainderPairwiseInteractionHandler.template computeRemainderInteractions<true>(
1177 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer, useSoA);
1178 } else {
1179 _remainderPairwiseInteractionHandler.template computeRemainderInteractions<false>(
1180 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer, useSoA);
1181 }
1182 } else if constexpr (utils::isTriwiseFunctor<Functor>()) {
1183 if (newton3) {
1184 _remainderTriwiseInteractionHandler.template computeRemainderInteractions<true>(
1185 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1186 } else {
1187 _remainderTriwiseInteractionHandler.template computeRemainderInteractions<false>(
1188 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1189 }
1190 }
1191 });
1192}
1193
1194template <typename Particle_T>
1195template <class Functor>
1196std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::selectConfiguration(
1197 Functor &functor, const InteractionTypeOption &interactionType) {
1198 auto &autoTuner = *_autoTunerRefs[interactionType];
1199
1200 // Todo: Make LiveInfo persistent between multiple functor calls in the same timestep (e.g. 2B + 3B)
1201 // https://github.com/AutoPas/AutoPas/issues/916
1202 LiveInfo info{};
1203#ifdef AUTOPAS_LOG_LIVEINFO
1204 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1205 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1206 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1207 _liveInfoLogger.logLiveInfo(info, _iteration);
1208#endif
1209
1210 // if this iteration is not relevant, take the same algorithm config as before.
1211 if (not functor.isRelevantForTuning()) {
1212 auto configuration = autoTuner.getCurrentConfig();
1213 auto [traversalPtr, _] = isConfigurationApplicable(configuration, functor);
1214
1215 if (not traversalPtr) {
1216 // TODO: Can we handle this case gracefully?
1218 "LogicHandler: Functor {} is not relevant for tuning but the given configuration is not applicable!",
1219 functor.getName());
1220 }
1221 return {configuration, std::move(traversalPtr), false};
1222 }
1223
1224 if (autoTuner.needsLiveInfo()) {
1225 // If live info has not been gathered yet, gather it now and send it to the tuner.
1226 if (info.get().empty()) {
1227 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1228 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1229 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1230 }
1231 autoTuner.receiveLiveInfo(info);
1232 }
1233
1234 size_t numRejectedConfigs = 0;
1235 utils::TraceTimer selectConfigurationTimer;
1236 selectConfigurationTimer.start();
1237
1238 auto [configuration, stillTuning] = autoTuner.getNextConfig();
1239
1240 // loop as long as we don't get a valid configuration
1241 do {
1242 // applicability check also sets the container
1243 auto [traversalPtr, rejectIndefinitely] = isConfigurationApplicable(configuration, functor);
1244 if (traversalPtr) {
1245 selectConfigurationTimer.stop();
1246 AutoPasLog(TRACE, "Select Configuration took {} ms. A total of {} configurations were rejected.",
1247 selectConfigurationTimer.getTotalTime(), numRejectedConfigs);
1248 return {configuration, std::move(traversalPtr), stillTuning};
1249 }
1250 // if no config is left after rejecting this one, an exception is thrown here.
1251 numRejectedConfigs++;
1252 std::tie(configuration, stillTuning) = autoTuner.rejectConfig(configuration, rejectIndefinitely);
1253 } while (true);
1254}
1255
1256template <typename Particle_T>
1258 std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer) {
1259 // copy particles so they do not get lost when the container is switched
1260 if (_currentContainer != nullptr and newContainer != nullptr) {
1261 // with these assumptions slightly more space is reserved as numParticlesTotal already includes halos
1262 const auto numParticlesTotal = _currentContainer->size();
1263 const auto numParticlesHalo = utils::NumParticlesEstimator::estimateNumHalosUniform(
1264 numParticlesTotal, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
1265 _currentContainer->getInteractionLength());
1266
1267 newContainer->reserve(numParticlesTotal, numParticlesHalo);
1268 for (auto particleIter = _currentContainer->begin(IteratorBehavior::ownedOrHalo); particleIter.isValid();
1269 ++particleIter) {
1270 // add a particle as inner if it is owned
1271 if (particleIter->isOwned()) {
1272 newContainer->addParticle(*particleIter);
1273 } else {
1274 newContainer->addHaloParticle(*particleIter);
1275 }
1276 }
1277 }
1278
1279 _currentContainer = std::move(newContainer);
1280}
1281
1282template <typename Particle_T>
1283template <class Functor>
1285 const InteractionTypeOption &interactionType) {
1286 if (not _interactionTypes.count(interactionType)) {
1288 "LogicHandler::computeInteractionsPipeline(): AutPas was not initialized for the Functor's interactions type: "
1289 "{}.",
1290 interactionType);
1291 }
1293 utils::Timer tuningTimer;
1294 tuningTimer.start();
1295 const auto [configuration, traversalPtr, stillTuning] = selectConfiguration(*functor, interactionType);
1296 tuningTimer.stop();
1297 auto &autoTuner = *_autoTunerRefs[interactionType];
1298 autoTuner.logTuningResult(stillTuning, tuningTimer.getTotalTime());
1299
1300 // Retrieve rebuild info before calling `computeInteractions()` to get the correct value.
1301 const auto rebuildIteration = not _neighborListsAreValid.load(std::memory_order_relaxed);
1302
1304 AutoPasLog(DEBUG, "Iterating with configuration: {} tuning: {}", configuration.toString(), stillTuning);
1305 const IterationMeasurements measurements = computeInteractions(*functor, *traversalPtr);
1306
1308 auto bufferSizeListing = [](const auto &buffers) -> std::string {
1309 std::stringstream ss;
1310 size_t sum = 0;
1311 for (const auto &buffer : buffers) {
1312 ss << buffer.size() << ", ";
1313 sum += buffer.size();
1314 }
1315 ss << " Total: " << sum;
1316 return ss.str();
1317 };
1318 AutoPasLog(TRACE, "particleBuffer size : {}", bufferSizeListing(_particleBuffer));
1319 AutoPasLog(TRACE, "haloParticleBuffer size : {}", bufferSizeListing(_haloParticleBuffer));
1320 AutoPasLog(DEBUG, "Type of interaction : {}", interactionType.to_string());
1321 AutoPasLog(DEBUG, "Container::computeInteractions took {} ns", measurements.timeComputeInteractions);
1322 AutoPasLog(DEBUG, "RemainderTraversal took {} ns", measurements.timeRemainderTraversal);
1323 AutoPasLog(DEBUG, "RebuildNeighborLists took {} ns", measurements.timeRebuild);
1324 AutoPasLog(DEBUG, "AutoPas::computeInteractions took {} ns", measurements.timeTotal);
1325 if (measurements.energyMeasurementsPossible) {
1326 AutoPasLog(DEBUG, "Energy Consumption: Watts: {} Joules: {} Seconds: {}", measurements.energyWatts,
1327 measurements.energyJoules, measurements.energyDeltaT);
1328 }
1329 _iterationLogger.logIteration(configuration, _iteration, functor->getName(), stillTuning, tuningTimer.getTotalTime(),
1330 measurements);
1331
1332 _flopLogger.logIteration(_iteration, functor->getNumFLOPs(), functor->getHitRate());
1333
1335 // if this was a major iteration add measurements
1336 if (functor->isRelevantForTuning()) {
1337 if (stillTuning) {
1338 // choose the metric of interest
1339 const auto measurement = [&]() {
1340 switch (autoTuner.getTuningMetric()) {
1342 return std::make_pair(measurements.timeRebuild,
1343 measurements.timeComputeInteractions + measurements.timeRemainderTraversal);
1345 return std::make_pair(measurements.energyTotalRebuild, measurements.energyTotalNonRebuild);
1346 default:
1347 utils::ExceptionHandler::exception("LogicHandler::computeInteractionsPipeline(): Unknown tuning metric.");
1348 return std::make_pair(0l, 0l);
1349 }
1350 }();
1351 autoTuner.addMeasurement(measurement.first, measurement.second, rebuildIteration);
1352 }
1353 } else {
1354 AutoPasLog(TRACE, "Skipping adding of sample because functor is not marked relevant.");
1355 }
1356 ++_functorCalls;
1357 return stillTuning;
1358}
1359
1360template <typename Particle_T>
1361template <class Functor>
1362std::tuple<std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::isConfigurationApplicable(
1363 const Configuration &config, Functor &functor) {
1364 // Check if the container supports the traversal
1365 const auto allContainerTraversals =
1366 compatibleTraversals::allCompatibleTraversals(config.container, config.interactionType);
1367 if (allContainerTraversals.find(config.traversal) == allContainerTraversals.end()) {
1368 AutoPasLog(WARN, "Configuration rejected: Container {} does not support the traversal {}.", config.container,
1369 config.traversal);
1370 return {nullptr, /*rejectIndefinitely*/ true};
1371 }
1372
1373 // Check if the functor supports the required Newton 3 mode
1374 if ((config.newton3 == Newton3Option::enabled and not functor.allowsNewton3()) or
1375 (config.newton3 == Newton3Option::disabled and not functor.allowsNonNewton3())) {
1376 AutoPasLog(DEBUG, "Configuration rejected: The functor doesn't support Newton 3 {}!", config.newton3);
1377 return {nullptr, /*rejectIndefinitely*/ true};
1378 }
1379
1380 std::unique_ptr<ParticleContainerInterface<Particle_T>> containerPtr{nullptr};
1381 auto containerInfo =
1382 ContainerSelectorInfo(_currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
1383 _currentContainer->getCutoff(), config.cellSizeFactor, _currentContainer->getVerletSkin(),
1384 _verletClusterSize, _sortingThreshold, config.loadEstimator);
1385
1386 // If we have no current container or needs to be updated to the new config.container, we need to generate a new
1387 // container.
1388 const bool generateNewContainer = _currentContainer == nullptr or
1389 _currentContainer->getContainerType() != config.container or
1390 containerInfo != _currentContainerSelectorInfo;
1391
1392 if (generateNewContainer) {
1393 // For now, set the local containerPtr to the new container. We do not copy the particles over and set the member
1394 // _currentContainer until after we know that the traversal is applicable to the domain.
1395 containerPtr = ContainerSelector<Particle_T>::generateContainer(config.container, containerInfo);
1396 }
1397
1398 const auto traversalInfo =
1399 generateNewContainer ? containerPtr->getTraversalSelectorInfo() : _currentContainer->getTraversalSelectorInfo();
1400
1401 // Generates a traversal if applicable, otherwise returns a nullptr
1402 auto traversalPtr =
1403 TraversalSelector::generateTraversalFromConfig<Particle_T, Functor>(config, functor, traversalInfo);
1404
1405 // If the traversal is applicable to the domain, and the configuration requires generating a new container,
1406 // update the member _currentContainer with setCurrentContainer, copying the particle data over, and update
1407 // _currentContainerSelectorInfo.
1408 if (traversalPtr and generateNewContainer) {
1409 _currentContainerSelectorInfo = containerInfo;
1410 setCurrentContainer(std::move(containerPtr));
1411 }
1412
1413 return {std::move(traversalPtr), /*rejectIndefinitely*/ false};
1414}
1415
1416} // namespace autopas
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
Class containing multiple options that form an algorithm configuration for the pairwise iteration.
Definition: Configuration.h:24
LoadEstimatorOption loadEstimator
Load Estimator option.
Definition: Configuration.h:126
TraversalOption traversal
Traversal option.
Definition: Configuration.h:122
double cellSizeFactor
CellSizeFactor.
Definition: Configuration.h:138
InteractionTypeOption interactionType
Interaction type of the configuration.
Definition: Configuration.h:142
ContainerOption container
Container option.
Definition: Configuration.h:118
Newton3Option newton3
Newton 3 option.
Definition: Configuration.h:134
Public iterator class that iterates over a particle container and additional vectors (which are typic...
Definition: ContainerIterator.h:95
Info to generate a container.
Definition: ContainerSelectorInfo.h:17
std::array< double, 3 > boxMin
Lower corner of the container.
Definition: ContainerSelectorInfo.h:93
std::array< double, 3 > boxMax
Upper corner of the container.
Definition: ContainerSelectorInfo.h:98
static std::unique_ptr< ParticleContainerInterface< Particle_T > > generateContainer(ContainerOption containerChoice, const ContainerSelectorInfo &containerInfo)
Container factory method.
Definition: ContainerSelector.h:44
Helper to log FLOP count and HitRate for AutoPas::iteratePairwise() calls with the functors in the mo...
Definition: FLOPLogger.h:30
This class handles the storage of particles in their full form.
Definition: FullParticleCell.h:26
Functor base class.
Definition: Functor.h:40
virtual size_t getNumFLOPs() const
Get the number of FLOPs.
Definition: Functor.h:177
virtual bool allowsNewton3()=0
Specifies whether the functor is capable of Newton3-like functors.
virtual void initTraversal()
This function is called at the start of each traversal.
Definition: Functor.h:63
virtual bool allowsNonNewton3()=0
Specifies whether the functor is capable of non-Newton3-like functors.
virtual void endTraversal(bool newton3)
This function is called at the end of each traversal.
Definition: Functor.h:70
virtual bool isRelevantForTuning()=0
Specifies whether the functor should be considered for the auto-tuning process.
virtual std::string getName()=0
Returns name of functor.
virtual double getHitRate() const
Get the hit rate.
Definition: Functor.h:187
Helper to log performance data of AutoPas::computeInteractions() to a csv file for easier analysis.
Definition: IterationLogger.h:24
Helper to log the collected LiveInfo data during tuning to a csv file for easier analysis.
Definition: LiveInfoLogger.h:23
This class is able to gather and store important information for a tuning phase from a container and ...
Definition: LiveInfo.h:33
Class that wraps all arguments for the logic handler to provide a more stable API.
Definition: LogicHandlerInfo.h:16
double verletSkin
Length added to the cutoff for the Verlet lists' skin.
Definition: LogicHandlerInfo.h:33
std::array< double, 3 > boxMax
Upper corner of the container without halo.
Definition: LogicHandlerInfo.h:25
double cutoff
Cutoff radius to be used in this simulation.
Definition: LogicHandlerInfo.h:29
std::array< double, 3 > boxMin
Lower corner of the container without halo.
Definition: LogicHandlerInfo.h:21
The LogicHandler takes care of the containers s.t.
Definition: LogicHandler.h:48
void setParticleBuffers(const std::vector< FullParticleCell< Particle_T > > &particleBuffers, const std::vector< FullParticleCell< Particle_T > > &haloParticleBuffers)
Directly exchange the internal particle and halo buffers with the given vectors and update particle c...
Definition: LogicHandler.h:1023
void reserve(size_t numParticles, size_t numHaloParticles)
Reserves space in the particle buffers and the container.
Definition: LogicHandler.h:303
std::tuple< std::unique_ptr< TraversalInterface >, bool > isConfigurationApplicable(const Configuration &config, Functor &functor)
Checks if the given configuration can be used with the given functor and the current state of the sim...
Definition: LogicHandler.h:1362
ContainerIterator< Particle_T, true, false > begin(IteratorBehavior behavior)
Iterate over all particles by using for(auto iter = autoPas.begin(); iter.isValid(); ++iter)
Definition: LogicHandler.h:485
std::vector< Particle_T > updateContainer()
Updates the container.
Definition: LogicHandler.h:163
unsigned long getNumberOfParticlesOwned() const
Get the number of owned particles.
Definition: LogicHandler.h:548
bool computeInteractionsPipeline(Functor *functor, const InteractionTypeOption &interactionType)
This function covers the full pipeline of all mechanics happening during the computation of particle ...
Definition: LogicHandler.h:1284
std::tuple< const std::vector< FullParticleCell< Particle_T > > &, const std::vector< FullParticleCell< Particle_T > > & > getParticleBuffers() const
Getter for the particle buffers.
Definition: LogicHandler.h:1059
void addParticle(const Particle_T &p)
Adds a particle to the container.
Definition: LogicHandler.h:323
void decreaseParticleCounter(Particle_T &particle)
Decrease the correct internal particle counters.
Definition: LogicHandler.h:418
std::vector< Particle_T > collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer)
Collects leaving particles from buffer and potentially inserts owned particles to the container.
Definition: LogicHandler.h:110
LogicHandler(std::unordered_map< InteractionTypeOption::Value, std::unique_ptr< AutoTuner > > &autotuners, const LogicHandlerInfo &logicHandlerInfo, unsigned int rebuildFrequency, const std::string &outputSuffix)
Constructor of the LogicHandler.
Definition: LogicHandler.h:57
bool neighborListsAreValid()
Checks if in the next iteration the neighbor lists have to be rebuilt.
Definition: LogicHandler.h:980
unsigned long getNumberOfParticlesHalo() const
Get the number of halo particles.
Definition: LogicHandler.h:554
std::tuple< bool, bool > deleteParticleFromBuffers(Particle_T &particle)
Takes a particle, checks if it is in any of the particle buffers, and deletes it from them if found.
Definition: LogicHandler.h:396
double getVelocityMethodRFEstimate(const double skin, const double deltaT) const
Estimates the rebuild frequency based on the current maximum velocity in the container Using the form...
Definition: LogicHandler.h:642
bool checkTuningStates(const InteractionTypeOption &interactionType)
Check if other autotuners for any other interaction types are still in a tuning phase.
Definition: LogicHandler.h:561
void checkNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:999
Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior)
Create the additional vectors vector for a given iterator behavior.
Definition: LogicHandler.h:456
ParticleContainerInterface< Particle_T > & getContainer()
Returns a non-const reference to the currently selected particle container.
Definition: LogicHandler.h:103
void addHaloParticle(const Particle_T &haloParticle)
Adds a particle to the container that lies in the halo region of the container.
Definition: LogicHandler.h:350
ContainerIterator< Particle_T, false, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior) const
Iterate over all particles in a specified region.
Definition: LogicHandler.h:524
void deleteAllParticles()
Deletes all particles.
Definition: LogicHandler.h:380
ContainerIterator< Particle_T, false, false > begin(IteratorBehavior behavior) const
Iterate over all particles by using for(auto iter = autoPas.begin(); iter.isValid(); ++iter)
Definition: LogicHandler.h:493
bool getNeighborListsInvalidDoDynamicRebuild()
getter function for _neighborListInvalidDoDynamicRebuild
Definition: LogicHandler.h:975
void resetNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:1018
double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase=false) const
Getter for the mean rebuild frequency.
Definition: LogicHandler.h:619
std::vector< Particle_T > resizeBox(const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax)
Pass values to the actual container.
Definition: LogicHandler.h:210
void reserve(size_t numParticles)
Estimates the number of halo particles via autopas::utils::NumParticlesEstimator::estimateNumHalosUni...
Definition: LogicHandler.h:290
ContainerIterator< Particle_T, true, true > getRegionIterator(const std::array< double, 3 > &lowerCorner, const std::array< double, 3 > &higherCorner, IteratorBehavior behavior)
Iterate over all particles in a specified region.
Definition: LogicHandler.h:503
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:38
Handles pairwise interactions involving particle buffers (particles not yet inserted into the main co...
Definition: RemainderPairwiseInteractionHandler.h:31
Handles triwise interactions involving particle buffers (particles not yet inserted into the main con...
Definition: RemainderTriwiseInteractionHandler.h:30
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
@ energy
Optimize for least energy usage.
Definition: TuningMetricOption.h:31
@ time
Optimize for shortest simulation time.
Definition: TuningMetricOption.h:27
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
Timer class to stop times.
Definition: Timer.h:27
void start()
start the timer.
Definition: Timer.cpp:17
long getTotalTime() const
Get total accumulated time.
Definition: Timer.h:60
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: Timer.cpp:25
A wrapper around autopas::utils::Timer that only compiles implementation logic if the SPDLOG_ACTIVE_L...
Definition: TraceTimer.h:20
long getTotalTime() const
Get total accumulated time.
Definition: TraceTimer.h:63
void start()
start the timer.
Definition: TraceTimer.h:25
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: TraceTimer.h:34
void markParticleAsDeleted(Particle_T &p)
Marks a particle as deleted.
Definition: markParticleAsDeleted.h:23
constexpr T dot(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Generates the dot product of two arrays.
Definition: ArrayMath.h:233
constexpr std::array< T, SIZE > ceil(const std::array< T, SIZE > &a)
For each element in a, computes the smallest integer value not less than the element.
Definition: ArrayMath.h:316
constexpr std::array< output_t, SIZE > static_cast_copy_array(const std::array< input_t, SIZE > &a)
Creates a new array by performing an element-wise static_cast<>.
Definition: ArrayUtils.h:33
void to_string(std::ostream &os, const Container &container, const std::string &delimiter, const std::array< std::string, 2 > &surround, Fun elemToString)
Generates a string representation of a container which fulfills the Container requirement (provide cb...
Definition: ArrayUtils.h:54
size_t estimateNumHalosUniform(size_t numParticles, const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax, double haloWidth)
Given a number of particles and the dimensions of a box, estimate the number of halo particles.
Definition: NumParticlesEstimator.cpp:9
decltype(isTriwiseFunctorImpl(std::declval< FunctorT >())) isTriwiseFunctor
Check whether a Functor Type is inheriting from TriwiseFunctor.
Definition: checkFunctorType.h:56
bool notInBox(const std::array< T, 3 > &position, const std::array< T, 3 > &low, const std::array< T, 3 > &high)
Checks if position is not inside of a box defined by low and high.
Definition: inBox.h:50
decltype(isPairwiseFunctorImpl(std::declval< FunctorT >())) isPairwiseFunctor
Check whether a Functor Type is inheriting from PairwiseFunctor.
Definition: checkFunctorType.h:49
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
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
@ halo
Halo state, a particle with this state is an actual particle, but not owned by the current AutoPas ob...
@ owned
Owned state, a particle with this state is an actual particle and owned by the current AutoPas object...
decltype(auto) withStaticContainerType(ParticleContainerInterface< Particle_T > &container, FunctionType &&function)
Will execute the passed function body with the static container type of container.
Definition: StaticContainerSelector.h:35
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132
Struct to collect all sorts of measurements taken during a computeInteractions iteration.
Definition: IterationMeasurements.h:13
double energyWatts
Average energy consumed per time in Watts.
Definition: IterationMeasurements.h:42
double energyDeltaT
Time in seconds during which energy was consumed.
Definition: IterationMeasurements.h:52
long timeRebuild
Time it takes for rebuilding neighbor lists.
Definition: IterationMeasurements.h:27
long timeTotal
Time it takes for the complete iteratePairwise pipeline.
Definition: IterationMeasurements.h:32
long energyTotalRebuild
Total energy consumed during rebuilding.
Definition: IterationMeasurements.h:57
long timeRemainderTraversal
Time it takes for the Remainder Traversal.
Definition: IterationMeasurements.h:22
long energyTotalNonRebuild
Total energy consumed during compute interactions and remainder traversal.
Definition: IterationMeasurements.h:62
long timeComputeInteractions
Time it takes for the LogicHandler's computeInteractions() function.
Definition: IterationMeasurements.h:17
bool energyMeasurementsPossible
Bool whether energy measurements are currently possible.
Definition: IterationMeasurements.h:37
double energyJoules
Total energy consumed in Joules.
Definition: IterationMeasurements.h:47