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
28#include "autopas/utils/Timer.h"
36
37namespace autopas {
38
44template <typename Particle_T>
46 public:
54 LogicHandler(std::unordered_map<InteractionTypeOption::Value, std::unique_ptr<AutoTuner>> &autotuners,
55 const LogicHandlerInfo &logicHandlerInfo, unsigned int rebuildFrequency, const std::string &outputSuffix)
56 : _autoTunerRefs(autotuners),
57 _logicHandlerInfo(logicHandlerInfo),
58 _neighborListRebuildFrequency{rebuildFrequency},
59 _particleBuffer(autopas_get_max_threads()),
60 _haloParticleBuffer(autopas_get_max_threads()),
61 _verletClusterSize(logicHandlerInfo.verletClusterSize),
62 _sortingThreshold(logicHandlerInfo.sortingThreshold),
63 _iterationLogger(outputSuffix, std::any_of(autotuners.begin(), autotuners.end(),
64 [](const auto &tuner) { return tuner.second->canMeasureEnergy(); })),
65 _flopLogger(outputSuffix),
66 _liveInfoLogger(outputSuffix),
67 _bufferLocks(std::max(2, autopas::autopas_get_max_threads())) {
68 using namespace autopas::utils::ArrayMath::literals;
69 // Initialize AutoPas with tuners for given interaction types
70 for (const auto &[interactionType, tuner] : autotuners) {
71 _interactionTypes.insert(interactionType);
72
73 const auto configuration = tuner->getCurrentConfig();
74 // initialize the container and make sure it is valid
75 _currentContainerSelectorInfo = ContainerSelectorInfo{_logicHandlerInfo.boxMin,
76 _logicHandlerInfo.boxMax,
77 _logicHandlerInfo.cutoff,
78 configuration.cellSizeFactor,
79 _logicHandlerInfo.verletSkin,
80 _verletClusterSize,
81 _sortingThreshold,
82 configuration.loadEstimator};
83 _currentContainer =
84 ContainerSelector<Particle_T>::generateContainer(configuration.container, _currentContainerSelectorInfo);
85 checkMinimalSize();
86 }
87
88 // initialize locks needed for remainder traversal
89 const auto interactionLength = logicHandlerInfo.cutoff + logicHandlerInfo.verletSkin;
90 const auto interactionLengthInv = 1. / interactionLength;
91 const auto boxLengthWithHalo = logicHandlerInfo.boxMax - logicHandlerInfo.boxMin + (2 * interactionLength);
92 initSpacialLocks(boxLengthWithHalo, interactionLengthInv);
93 for (auto &lockPtr : _bufferLocks) {
94 lockPtr = std::make_unique<std::mutex>();
95 }
96 }
97
102 ParticleContainerInterface<Particle_T> &getContainer() { return *_currentContainer; }
103
109 [[nodiscard]] std::vector<Particle_T> collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer) {
110 const auto &boxMin = _currentContainer->getBoxMin();
111 const auto &boxMax = _currentContainer->getBoxMax();
112 std::vector<Particle_T> leavingBufferParticles{};
113 for (auto &cell : _particleBuffer) {
114 auto &buffer = cell._particles;
115 if (insertOwnedParticlesToContainer) {
116 // Can't be const because we potentially modify ownership before re-adding
117 for (auto &p : buffer) {
118 if (p.isDummy()) {
119 continue;
120 }
121 if (utils::inBox(p.getR(), boxMin, boxMax)) {
122 p.setOwnershipState(OwnershipState::owned);
123 _currentContainer->addParticle(p);
124 } else {
125 leavingBufferParticles.push_back(p);
126 }
127 }
128 buffer.clear();
129 } else {
130 for (auto iter = buffer.begin(); iter < buffer.end();) {
131 auto &p = *iter;
132
133 auto fastRemoveP = [&]() {
134 // Fast remove of particle, i.e., swap with last entry && pop.
135 std::swap(p, buffer.back());
136 buffer.pop_back();
137 // Do not increment the iter afterward!
138 };
139 if (p.isDummy()) {
140 // We remove dummies!
141 fastRemoveP();
142 // In case we swapped a dummy here, don't increment the iterator and do another iteration to check again.
143 continue;
144 }
145 // if p was a dummy a new particle might now be at the memory location of p so we need to check that.
146 // We also just might have deleted the last particle in the buffer in that case the inBox check is meaningless
147 if (not buffer.empty() and utils::notInBox(p.getR(), boxMin, boxMax)) {
148 leavingBufferParticles.push_back(p);
149 fastRemoveP();
150 } else {
151 ++iter;
152 }
153 }
154 }
155 }
156 return leavingBufferParticles;
157 }
158
162 [[nodiscard]] std::vector<Particle_T> updateContainer() {
163#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
165#endif
166 bool doDataStructureUpdate = not neighborListsAreValid();
167
168 if (_functorCalls > 0) {
169 // Bump iteration counters for all autotuners
170 for (const auto &[interactionType, autoTuner] : _autoTunerRefs) {
171 const bool needsToWait = checkTuningStates(interactionType);
172 // Called before bumpIterationCounters as it would return false after that.
173 if (autoTuner->inLastTuningIteration()) {
174 _iterationAtEndOfLastTuningPhase = _iteration;
175 }
176 autoTuner->bumpIterationCounters(needsToWait);
177 }
178
179 // We will do a rebuild in this timestep
180 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
181 _stepsSinceLastListRebuild = 0;
182 }
183 ++_stepsSinceLastListRebuild;
184 _currentContainer->setStepsSinceLastRebuild(_stepsSinceLastListRebuild);
185 ++_iteration;
186 }
187
188 // The next call also adds particles to the container if doDataStructureUpdate is true.
189 auto leavingBufferParticles = collectLeavingParticlesFromBuffer(doDataStructureUpdate);
190
191 AutoPasLog(TRACE, "Initiating container update.");
192 auto leavingParticles = _currentContainer->updateContainer(not doDataStructureUpdate);
193 leavingParticles.insert(leavingParticles.end(), leavingBufferParticles.begin(), leavingBufferParticles.end());
194
195 // Substract the amount of leaving particles from the number of owned particles.
196 _numParticlesOwned.fetch_sub(leavingParticles.size(), std::memory_order_relaxed);
197 // updateContainer deletes all halo particles.
198 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
199 _numParticlesHalo.store(0, std::memory_order_relaxed);
200 return leavingParticles;
201 }
202
209 std::vector<Particle_T> resizeBox(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax) {
210 using namespace autopas::utils::ArrayMath::literals;
211 const auto &oldMin = _currentContainer->getBoxMin();
212 const auto &oldMax = _currentContainer->getBoxMax();
213
214 // if nothing changed, do nothing
215 if (oldMin == boxMin and oldMax == boxMax) {
216 return {};
217 }
218
219 // sanity check that new size is actually positive
220 for (size_t i = 0; i < boxMin.size(); ++i) {
221 if (boxMin[i] >= boxMax[i]) {
223 "New box size in dimension {} is not positive!\nboxMin[{}] = {}\nboxMax[{}] = {}", i, i, boxMin[i], i,
224 boxMax[i]);
225 }
226 }
227
228 // warn if domain changes too drastically
229 const auto newLength = boxMax - boxMin;
230 const auto oldLength = oldMax - oldMin;
231 const auto relDiffLength = newLength / oldLength;
232 for (size_t i = 0; i < newLength.size(); ++i) {
233 // warning threshold is set arbitrary and up for change if needed
234 if (relDiffLength[i] > 1.3 or relDiffLength[i] < 0.7) {
235 AutoPasLog(WARN,
236 "LogicHandler.resize(): Domain size changed drastically in dimension {}! Gathered AutoTuning "
237 "information might not be applicable anymore!\n"
238 "Size old box : {}\n"
239 "Size new box : {}\n"
240 "Relative diff: {}",
242 utils::ArrayUtils::to_string(relDiffLength));
243 }
244 }
245
246 // The new box size is valid, so update the current container info.
247 _currentContainerSelectorInfo.boxMin = boxMin;
248 _currentContainerSelectorInfo.boxMax = boxMax;
249
250 // check all particles
251 std::vector<Particle_T> particlesNowOutside;
252 for (auto pIter = _currentContainer->begin(); pIter.isValid(); ++pIter) {
253 // make sure only owned ones are present
254 if (not pIter->isOwned()) {
256 "LogicHandler::resizeBox() encountered non owned particle. "
257 "When calling resizeBox() these should be already deleted. "
258 "This could be solved by calling updateContainer() before resizeBox().");
259 }
260 // owned particles that are now outside are removed from the container and returned
261 if (not utils::inBox(pIter->getR(), boxMin, boxMax)) {
262 particlesNowOutside.push_back(*pIter);
265 }
266 }
267
268 // Resize by generating a new container with the new box size and moving all particles to it.
269 auto newContainer = ContainerSelector<Particle_T>::generateContainer(_currentContainer->getContainerType(),
270 _currentContainerSelectorInfo);
271 setCurrentContainer(std::move(newContainer));
272 // The container might have changed sufficiently that we would need a different number of spatial locks.
273 const auto boxLength = boxMax - boxMin;
274 const auto interactionLengthInv = 1. / _currentContainer->getInteractionLength();
275 initSpacialLocks(boxLength, interactionLengthInv);
276
277 // Set this flag, s.t., the container is rebuilt!
278 _neighborListsAreValid.store(false, std::memory_order_relaxed);
279
280 return particlesNowOutside;
281 }
282
289 void reserve(size_t numParticles) {
290 const auto numParticlesHaloEstimate = autopas::utils::NumParticlesEstimator::estimateNumHalosUniform(
291 numParticles, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
292 _currentContainer->getInteractionLength());
293 reserve(numParticles, numParticlesHaloEstimate);
294 }
295
302 void reserve(size_t numParticles, size_t numHaloParticles) {
303 const auto numHaloParticlesPerBuffer = numHaloParticles / _haloParticleBuffer.size();
304 for (auto &buffer : _haloParticleBuffer) {
305 buffer.reserve(numHaloParticlesPerBuffer);
306 }
307 // there is currently no good heuristic for this buffer, so reuse the one for halos.
308 for (auto &buffer : _particleBuffer) {
309 buffer.reserve(numHaloParticlesPerBuffer);
310 }
311
312 _currentContainer->reserve(numParticles, numHaloParticles);
313 }
314
318 void addParticle(const Particle_T &p) {
319 // first check that the particle actually belongs in the container
320 const auto &boxMin = _currentContainer->getBoxMin();
321 const auto &boxMax = _currentContainer->getBoxMax();
322 if (utils::notInBox(p.getR(), boxMin, boxMax)) {
324 "LogicHandler: Trying to add a particle that is not in the bounding box.\n"
325 "Box Min {}\n"
326 "Box Max {}\n"
327 "{}",
328 boxMin, boxMax, p.toString());
329 }
330 Particle_T particleCopy = p;
331 particleCopy.setOwnershipState(OwnershipState::owned);
332 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
333 // Container has to (about to) be invalid to be able to add Particles!
334 _currentContainer->template addParticle<false>(particleCopy);
335 } else {
336 // If the container is valid, we add it to the particle buffer.
337 _particleBuffer[autopas_get_thread_num()].addParticle(particleCopy);
338 }
339 _numParticlesOwned.fetch_add(1, std::memory_order_relaxed);
340 }
341
345 void addHaloParticle(const Particle_T &haloParticle) {
346 const auto &boxMin = _currentContainer->getBoxMin();
347 const auto &boxMax = _currentContainer->getBoxMax();
348 Particle_T haloParticleCopy = haloParticle;
349 if (utils::inBox(haloParticleCopy.getR(), boxMin, boxMax)) {
351 "LogicHandler: Trying to add a halo particle that is not outside the box of the container.\n"
352 "Box Min {}\n"
353 "Box Max {}\n"
354 "{}",
355 utils::ArrayUtils::to_string(boxMin), utils::ArrayUtils::to_string(boxMax), haloParticleCopy.toString());
356 }
357 haloParticleCopy.setOwnershipState(OwnershipState::halo);
358 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
359 // If the neighbor lists are not valid, we can add the particle.
360 _currentContainer->template addHaloParticle</* checkInBox */ false>(haloParticleCopy);
361 } else {
362 // Check if we can update an existing halo(dummy) particle.
363 bool updated = _currentContainer->updateHaloParticle(haloParticleCopy);
364 if (not updated) {
365 // If we couldn't find an existing particle, add it to the halo particle buffer.
366 _haloParticleBuffer[autopas_get_thread_num()].addParticle(haloParticleCopy);
367 }
368 }
369 _numParticlesHalo.fetch_add(1, std::memory_order_relaxed);
370 }
371
376 _neighborListsAreValid.store(false, std::memory_order_relaxed);
377 _currentContainer->deleteAllParticles();
378 std::for_each(_particleBuffer.begin(), _particleBuffer.end(), [](auto &buffer) { buffer.clear(); });
379 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
380 // all particles are gone -> reset counters.
381 _numParticlesOwned.store(0, std::memory_order_relaxed);
382 _numParticlesHalo.store(0, std::memory_order_relaxed);
383 }
384
391 std::tuple<bool, bool> deleteParticleFromBuffers(Particle_T &particle) {
392 // find the buffer the particle belongs to
393 auto &bufferCollection = particle.isOwned() ? _particleBuffer : _haloParticleBuffer;
394 for (auto &cell : bufferCollection) {
395 auto &buffer = cell._particles;
396 // if the address of the particle is between start and end of the buffer it is in this buffer
397 if (not buffer.empty() and &(buffer.front()) <= &particle and &particle <= &(buffer.back())) {
398 const bool isRearParticle = &particle == &buffer.back();
399 // swap-delete
400 particle = buffer.back();
401 buffer.pop_back();
402 return {true, not isRearParticle};
403 }
404 }
405 return {false, true};
406 }
407
413 void decreaseParticleCounter(Particle_T &particle) {
414 if (particle.isOwned()) {
415 _numParticlesOwned.fetch_sub(1, std::memory_order_relaxed);
416 } else {
417 _numParticlesHalo.fetch_sub(1, std::memory_order_relaxed);
418 }
419 }
420
441 template <class Functor>
442 bool computeInteractionsPipeline(Functor *functor, const InteractionTypeOption &interactionType);
443
450 template <class Iterator>
451 typename Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior) {
452 typename Iterator::ParticleVecType additionalVectors;
453 if (not(behavior & IteratorBehavior::containerOnly)) {
454 additionalVectors.reserve(static_cast<bool>(behavior & IteratorBehavior::owned) * _particleBuffer.size() +
455 static_cast<bool>(behavior & IteratorBehavior::halo) * _haloParticleBuffer.size());
456 if (behavior & IteratorBehavior::owned) {
457 for (auto &buffer : _particleBuffer) {
458 // Don't insert empty buffers. This also means that we won't pick up particles added during iterating if they
459 // go to the buffers. But since we wouldn't pick them up if they go into the container to a cell that the
460 // iterators already passed this is unsupported anyways.
461 if (not buffer.isEmpty()) {
462 additionalVectors.push_back(&(buffer._particles));
463 }
464 }
465 }
466 if (behavior & IteratorBehavior::halo) {
467 for (auto &buffer : _haloParticleBuffer) {
468 if (not buffer.isEmpty()) {
469 additionalVectors.push_back(&(buffer._particles));
470 }
471 }
472 }
473 }
474 return additionalVectors;
475 }
476
481 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, false>>(behavior);
482 return _currentContainer->begin(behavior, &additionalVectors);
483 }
484
488 ContainerIterator<Particle_T, false, false> begin(IteratorBehavior behavior) const {
489 auto additionalVectors =
491 behavior);
492 return _currentContainer->begin(behavior, &additionalVectors);
493 }
494
498 ContainerIterator<Particle_T, true, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
499 const std::array<double, 3> &higherCorner,
500 IteratorBehavior behavior) {
501 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
502 for (size_t d = 0; d < 3; ++d) {
503 if (lowerCorner[d] > higherCorner[d]) {
505 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
506 "Lower corner: {}\n"
507 "Upper corner: {}",
508 lowerCorner, higherCorner);
509 }
510 }
511
512 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, true>>(behavior);
513 return _currentContainer->getRegionIterator(lowerCorner, higherCorner, behavior, &additionalVectors);
514 }
515
519 ContainerIterator<Particle_T, false, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
520 const std::array<double, 3> &higherCorner,
521 IteratorBehavior behavior) const {
522 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
523 for (size_t d = 0; d < 3; ++d) {
524 if (lowerCorner[d] > higherCorner[d]) {
526 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
527 "Lower corner: {}\n"
528 "Upper corner: {}",
529 lowerCorner, higherCorner);
530 }
531 }
532
533 auto additionalVectors =
535 return std::as_const(_currentContainer)->getRegionIterator(lowerCorner, higherCorner, behavior, &additionalVectors);
536 }
537
542 [[nodiscard]] unsigned long getNumberOfParticlesOwned() const { return _numParticlesOwned; }
543
548 [[nodiscard]] unsigned long getNumberOfParticlesHalo() const { return _numParticlesHalo; }
549
555 bool checkTuningStates(const InteractionTypeOption &interactionType) {
556 // Goes over all pairs in _autoTunerRefs and returns true as soon as one is `inTuningPhase()`.
557 // The tuner associated with the given interaction type is ignored.
558 return std::any_of(std::begin(_autoTunerRefs), std::end(_autoTunerRefs), [&](const auto &entry) {
559 return not(entry.first == interactionType) and entry.second->inTuningPhase();
560 });
561 }
562
575 template <class Functor>
576 [[nodiscard]] std::tuple<std::unique_ptr<TraversalInterface>, bool> isConfigurationApplicable(
577 const Configuration &config, Functor &functor);
578
589 void setParticleBuffers(const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
590 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
591
599 std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
600 getParticleBuffers() const;
601
613 [[nodiscard]] double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase = false) const {
614#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
615 const auto numRebuilds = considerOnlyLastNonTuningPhase ? _numRebuildsInNonTuningPhase : _numRebuilds;
616 // The total number of iterations is iteration + 1
617 const auto iterationCount =
618 considerOnlyLastNonTuningPhase ? _iteration - _iterationAtEndOfLastTuningPhase : _iteration + 1;
619 if (numRebuilds == 0) {
620 return static_cast<double>(_neighborListRebuildFrequency);
621 } else {
622 return static_cast<double>(iterationCount) / numRebuilds;
623 }
624#else
625 return static_cast<double>(_neighborListRebuildFrequency);
626#endif
627 }
628
634
640
646
656
657 private:
670 void initSpacialLocks(const std::array<double, 3> &boxLength, double interactionLengthInv) {
671 using namespace autopas::utils::ArrayMath::literals;
674
675 // The maximum number of spatial locks is capped at 1e6.
676 // This limit is chosen more or less arbitrary. It is big enough so that our regular MD simulations
677 // fall well within it and small enough so that no memory issues arise.
678 // There were no rigorous tests for an optimal number of locks.
679 // Without this cap, very large domains (or tiny cutoffs) would generate an insane number of locks,
680 // that could blow up the memory.
681 constexpr size_t maxNumSpacialLocks{1000000};
682
683 // One lock per interaction length or less if this would generate too many.
684 const std::array<size_t, 3> locksPerDim = [&]() {
685 // First naively calculate the number of locks if we simply take the desired cell length.
686 // Ceil because both decisions are possible, and we are generous gods.
687 const std::array<size_t, 3> locksPerDimNaive =
688 static_cast_copy_array<size_t>(ceil(boxLength * interactionLengthInv));
689 const auto totalLocksNaive =
690 std::accumulate(locksPerDimNaive.begin(), locksPerDimNaive.end(), 1ul, std::multiplies<>());
691 // If the number of locks is within the limits everything is fine and we can return.
692 if (totalLocksNaive <= maxNumSpacialLocks) {
693 return locksPerDimNaive;
694 } else {
695 // If the number of locks grows too large, calculate the locks per dimension proportionally to the side lengths.
696 // Calculate side length relative to dimension 0.
697 const std::array<double, 3> boxSideProportions = {
698 1.,
699 boxLength[0] / boxLength[1],
700 boxLength[0] / boxLength[2],
701 };
702 // With this, calculate the number of locks the first dimension should receive.
703 const auto prodProportions =
704 std::accumulate(boxSideProportions.begin(), boxSideProportions.end(), 1., std::multiplies<>());
705 // Needs floor, otherwise we exceed the limit.
706 const auto locksInFirstDimFloat = std::floor(std::cbrt(maxNumSpacialLocks * prodProportions));
707 // From this and the proportions relative to the first dimension, we can calculate the remaining number of locks
708 const std::array<size_t, 3> locksPerDimLimited = {
709 static_cast<size_t>(locksInFirstDimFloat), // omitted div by 1
710 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[1]),
711 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[2]),
712 };
713 return locksPerDimLimited;
714 }
715 }();
716 _spacialLocks.resize(locksPerDim[0]);
717 for (auto &lockVecVec : _spacialLocks) {
718 lockVecVec.resize(locksPerDim[1]);
719 for (auto &lockVec : lockVecVec) {
720 lockVec.resize(locksPerDim[2]);
721 for (auto &lockPtr : lockVec) {
722 if (not lockPtr) {
723 lockPtr = std::make_unique<std::mutex>();
724 }
725 }
726 }
727 }
728 }
729
737 template <class Functor>
738 std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> selectConfiguration(
739 Functor &functor, const InteractionTypeOption &interactionType);
740
745 void setCurrentContainer(std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer);
746
761 template <class Functor>
762 IterationMeasurements computeInteractions(Functor &functor, TraversalInterface &traversal);
763
774 template <class Functor>
775 void computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA);
776
798 template <bool newton3, class ContainerType, class PairwiseFunctor>
799 void computeRemainderInteractions2B(PairwiseFunctor *f, ContainerType &container,
800 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
801 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA);
802
816 template <bool newton3, class ContainerType, class PairwiseFunctor>
817 void remainderHelperBufferContainerAoS(PairwiseFunctor *f, ContainerType &container,
818 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
819 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
820
830 template <bool newton3, class PairwiseFunctor>
831 void remainderHelperBufferBuffer(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
832 bool useSoA);
833
841 template <bool newton3, class PairwiseFunctor>
842 void remainderHelperBufferBufferAoS(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers);
843
851 template <bool newton3, class PairwiseFunctor>
852 void remainderHelperBufferBufferSoA(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers);
853
867 template <class PairwiseFunctor>
868 void remainderHelperBufferHaloBuffer(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
869 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA);
870
878 template <class PairwiseFunctor>
879 void remainderHelperBufferHaloBufferAoS(PairwiseFunctor *f,
880 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
881 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
882
890 template <class PairwiseFunctor>
891 void remainderHelperBufferHaloBufferSoA(PairwiseFunctor *f,
892 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
893 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
894
915 template <bool newton3, class ContainerType, class TriwiseFunctor>
916 void computeRemainderInteractions3B(TriwiseFunctor *f, ContainerType &container,
917 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
918 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
919
930 size_t collectBufferParticles(std::vector<Particle_T *> &bufferParticles,
931 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
932 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
933
943 template <class TriwiseFunctor>
944 void remainderHelper3bBufferBufferBufferAoS(const std::vector<Particle_T *> &bufferParticles,
945 size_t numOwnedBufferParticles, TriwiseFunctor *f);
946
958 template <class ContainerType, class TriwiseFunctor>
959 void remainderHelper3bBufferBufferContainerAoS(const std::vector<Particle_T *> &bufferParticles,
960 size_t numOwnedBufferParticles, ContainerType &container,
961 TriwiseFunctor *f);
962
975 template <bool newton3, class ContainerType, class TriwiseFunctor>
976 void remainderHelper3bBufferContainerContainerAoS(const std::vector<Particle_T *> &bufferParticles,
977 size_t numOwnedBufferParticles, ContainerType &container,
978 TriwiseFunctor *f);
979
985 void checkMinimalSize() const;
986
987 const LogicHandlerInfo _logicHandlerInfo;
991 unsigned int _neighborListRebuildFrequency;
992
996 unsigned int _verletClusterSize;
997
1001 size_t _numRebuilds{0};
1002
1007 size_t _numRebuildsInNonTuningPhase{0};
1008
1012 size_t _sortingThreshold;
1013
1017 std::unordered_map<InteractionTypeOption::Value, std::unique_ptr<AutoTuner>> &_autoTunerRefs;
1018
1022 std::unique_ptr<ParticleContainerInterface<Particle_T>> _currentContainer;
1023
1027 ContainerSelectorInfo _currentContainerSelectorInfo;
1028
1032 std::set<InteractionTypeOption> _interactionTypes{};
1033
1037 std::atomic<bool> _neighborListsAreValid{false};
1038
1042 unsigned int _stepsSinceLastListRebuild{0};
1043
1047 unsigned int _functorCalls{0};
1048
1052 unsigned int _iteration{0};
1053
1057 unsigned int _iterationAtEndOfLastTuningPhase{0};
1058
1062 std::atomic<size_t> _numParticlesOwned{0ul};
1063
1067 std::atomic<size_t> _numParticlesHalo{0ul};
1068
1072 std::vector<FullParticleCell<Particle_T>> _particleBuffer;
1073
1077 std::vector<FullParticleCell<Particle_T>> _haloParticleBuffer;
1078
1084 std::vector<std::vector<std::vector<std::unique_ptr<std::mutex>>>> _spacialLocks;
1085
1089 std::vector<std::unique_ptr<std::mutex>> _bufferLocks;
1090
1094 IterationLogger _iterationLogger;
1095
1100 bool _neighborListInvalidDoDynamicRebuild{false};
1101
1105 void updateRebuildPositions();
1106
1110 LiveInfoLogger _liveInfoLogger;
1111
1115 FLOPLogger _flopLogger;
1116};
1117
1118template <typename Particle_T>
1120#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1121 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
1122 // e.g. neighbour list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
1123 // require a rebuild of neighbor lists.
1124 AUTOPAS_OPENMP(parallel)
1125 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
1126 iter->resetRAtRebuild();
1127 }
1128#endif
1129}
1130
1131template <typename Particle_T>
1133 // check boxSize at least cutoff + skin
1134 for (unsigned int dim = 0; dim < 3; ++dim) {
1135 if (_currentContainer->getBoxMax()[dim] - _currentContainer->getBoxMin()[dim] <
1136 _currentContainer->getInteractionLength()) {
1138 "Box (boxMin[{}]={} and boxMax[{}]={}) is too small.\nHas to be at least cutoff({}) + skin({}) = {}.", dim,
1139 _currentContainer->getBoxMin()[dim], dim, _currentContainer->getBoxMax()[dim], _currentContainer->getCutoff(),
1140 _currentContainer->getVerletSkin(), _currentContainer->getCutoff() + _currentContainer->getVerletSkin());
1141 }
1142 }
1143}
1144
1145template <typename Particle_T>
1147 return _neighborListInvalidDoDynamicRebuild;
1148}
1149
1150template <typename Particle_T>
1152 // Implement rebuild indicator as function, so it is only evaluated when needed.
1153 const auto needRebuild = [&](const InteractionTypeOption &interactionOption) {
1154 return _interactionTypes.count(interactionOption) != 0 and
1155 _autoTunerRefs[interactionOption]->willRebuildNeighborLists();
1156 };
1157
1158 if (_stepsSinceLastListRebuild >= _neighborListRebuildFrequency
1159#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1160 or getNeighborListsInvalidDoDynamicRebuild()
1161#endif
1162 or needRebuild(InteractionTypeOption::pairwise) or needRebuild(InteractionTypeOption::triwise)) {
1163 _neighborListsAreValid.store(false, std::memory_order_relaxed);
1164 }
1165
1166 return _neighborListsAreValid.load(std::memory_order_relaxed);
1167}
1168
1169template <typename Particle_T>
1171#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1172 const auto skin = getContainer().getVerletSkin();
1173 // (skin/2)^2
1174 const auto halfSkinSquare = skin * skin * 0.25;
1175 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
1176 // e.g. neighbour list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
1177 // require a rebuild of neighbor lists.
1178 AUTOPAS_OPENMP(parallel reduction(or : _neighborListInvalidDoDynamicRebuild))
1179 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
1180 const auto distance = iter->calculateDisplacementSinceRebuild();
1181 const double distanceSquare = utils::ArrayMath::dot(distance, distance);
1182
1183 _neighborListInvalidDoDynamicRebuild |= distanceSquare >= halfSkinSquare;
1184 }
1185#endif
1186}
1187
1188template <typename Particle_T>
1190 _neighborListInvalidDoDynamicRebuild = false;
1191}
1192
1193template <typename Particle_T>
1195 const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1196 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1197 auto exchangeBuffer = [](const auto &newBuffers, auto &oldBuffers, auto &particleCounter) {
1198 // sanity check
1199 if (oldBuffers.size() < newBuffers.size()) {
1201 "The number of new buffers ({}) is larger than number of existing buffers ({})!", newBuffers.size(),
1202 oldBuffers.size());
1203 }
1204
1205 // we will clear the old buffers so subtract the particles from the counters.
1206 const auto numParticlesInOldBuffers =
1207 std::transform_reduce(oldBuffers.begin(), std::next(oldBuffers.begin(), newBuffers.size()), 0, std::plus<>(),
1208 [](const auto &cell) { return cell.size(); });
1209 particleCounter.fetch_sub(numParticlesInOldBuffers, std::memory_order_relaxed);
1210
1211 // clear the old buffers and copy the content of the new buffers over.
1212 size_t numParticlesInNewBuffers = 0;
1213 for (size_t i = 0; i < newBuffers.size(); ++i) {
1214 oldBuffers[i].clear();
1215 for (const auto &p : newBuffers[i]) {
1216 ++numParticlesInNewBuffers;
1217 oldBuffers[i].addParticle(p);
1218 }
1219 }
1220 // update the counters.
1221 particleCounter.fetch_add(numParticlesInNewBuffers, std::memory_order_relaxed);
1222 };
1223
1224 exchangeBuffer(particleBuffers, _particleBuffer, _numParticlesOwned);
1225 exchangeBuffer(haloParticleBuffers, _haloParticleBuffer, _numParticlesHalo);
1226}
1227
1228template <typename Particle_T>
1229std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
1231 return {_particleBuffer, _haloParticleBuffer};
1232}
1233
1234template <typename Particle_T>
1235template <class Functor>
1237 // Helper to derive the Functor type at compile time
1238 constexpr auto interactionType = [] {
1240 return InteractionTypeOption::pairwise;
1241 } else if (utils::isTriwiseFunctor<Functor>()) {
1242 return InteractionTypeOption::triwise;
1243 } else {
1245 "LogicHandler::computeInteractions(): Functor is not valid. Only pairwise and triwise functors are "
1246 "supported. "
1247 "Please use a functor derived from "
1248 "PairwiseFunctor or TriwiseFunctor.");
1249 }
1250 }();
1251
1252 auto &autoTuner = *_autoTunerRefs[interactionType];
1253#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1254 if (autoTuner.inFirstTuningIteration()) {
1255 autoTuner.setRebuildFrequency(getMeanRebuildFrequency(/* considerOnlyLastNonTuningPhase */ true));
1256 _numRebuildsInNonTuningPhase = 0;
1257 }
1258#endif
1259 utils::Timer timerTotal;
1260 utils::Timer timerRebuild;
1261 utils::Timer timerComputeInteractions;
1262 utils::Timer timerComputeRemainder;
1263
1264 const bool energyMeasurementsPossible = autoTuner.resetEnergy();
1265
1266 timerTotal.start();
1267 functor.initTraversal();
1268
1269 // if lists are not valid -> rebuild;
1270 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
1271 timerRebuild.start();
1272#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1273 this->updateRebuildPositions();
1274#endif
1275 _currentContainer->rebuildNeighborLists(&traversal);
1276#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1277 this->resetNeighborListsInvalidDoDynamicRebuild();
1278 _numRebuilds++;
1279 if (not autoTuner.inTuningPhase()) {
1280 _numRebuildsInNonTuningPhase++;
1281 }
1282#endif
1283 timerRebuild.stop();
1284 _neighborListsAreValid.store(true, std::memory_order_relaxed);
1285 }
1286
1287 timerComputeInteractions.start();
1288 _currentContainer->computeInteractions(&traversal);
1289 timerComputeInteractions.stop();
1290
1291 timerComputeRemainder.start();
1292 const bool newton3 = autoTuner.getCurrentConfig().newton3;
1293 const auto dataLayout = autoTuner.getCurrentConfig().dataLayout;
1294 computeRemainderInteractions(functor, newton3, dataLayout);
1295 timerComputeRemainder.stop();
1296
1297 functor.endTraversal(newton3);
1298
1299 const auto [energyWatts, energyJoules, energyDeltaT, energyTotal] = autoTuner.sampleEnergy();
1300 timerTotal.stop();
1301
1302 constexpr auto nanD = std::numeric_limits<double>::quiet_NaN();
1303 constexpr auto nanL = std::numeric_limits<long>::quiet_NaN();
1304 return {timerComputeInteractions.getTotalTime(),
1305 timerComputeRemainder.getTotalTime(),
1306 timerRebuild.getTotalTime(),
1307 timerTotal.getTotalTime(),
1308 energyMeasurementsPossible,
1309 energyMeasurementsPossible ? energyWatts : nanD,
1310 energyMeasurementsPossible ? energyJoules : nanD,
1311 energyMeasurementsPossible ? energyDeltaT : nanD,
1312 energyMeasurementsPossible ? energyTotal : nanL};
1313}
1314
1315template <typename Particle_T>
1316template <class Functor>
1317void LogicHandler<Particle_T>::computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA) {
1318 withStaticContainerType(*_currentContainer, [&](auto &actualContainerType) {
1319 if constexpr (utils::isPairwiseFunctor<Functor>()) {
1320 if (newton3) {
1321 computeRemainderInteractions2B<true>(&functor, actualContainerType, _particleBuffer, _haloParticleBuffer,
1322 useSoA);
1323 } else {
1324 computeRemainderInteractions2B<false>(&functor, actualContainerType, _particleBuffer, _haloParticleBuffer,
1325 useSoA);
1326 }
1327 } else if constexpr (utils::isTriwiseFunctor<Functor>()) {
1328 if (newton3) {
1329 computeRemainderInteractions3B<true>(&functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1330 } else {
1331 computeRemainderInteractions3B<false>(&functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1332 }
1333 }
1334 });
1335}
1336
1337template <class Particle_T>
1338template <bool newton3, class ContainerType, class PairwiseFunctor>
1340 PairwiseFunctor *f, ContainerType &container, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1341 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA) {
1342 // Sanity check. If this is violated feel free to add some logic here that adapts the number of locks.
1343 if (_bufferLocks.size() < particleBuffers.size()) {
1344 utils::ExceptionHandler::exception("Not enough locks for non-halo buffers! Num Locks: {}, Buffers: {}",
1345 _bufferLocks.size(), particleBuffers.size());
1346 }
1347
1348 // Balance buffers. This makes processing them with static scheduling quite efficient.
1349 // Also, if particles were not inserted in parallel, this enables us to process them in parallel now.
1350 // Cost is at max O(2N) worst O(N) per buffer collection and negligible compared to interacting them.
1351 auto cellToVec = [](auto &cell) -> std::vector<Particle_T> & { return cell._particles; };
1352 utils::ArrayUtils::balanceVectors(particleBuffers, cellToVec);
1353 utils::ArrayUtils::balanceVectors(haloParticleBuffers, cellToVec);
1354
1355 // The following part performs the main remainder traversal. The actual calculation is done in 4 steps carried out
1356 // in three helper functions.
1357
1358 // only activate time measurements if it will actually be logged
1359#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1360 autopas::utils::Timer timerBufferContainer;
1361 autopas::utils::Timer timerPBufferPBuffer;
1362 autopas::utils::Timer timerPBufferHBuffer;
1363 autopas::utils::Timer timerBufferSoAConversion;
1364
1365 timerBufferContainer.start();
1366#endif
1367 // steps 1 & 2.
1368 // particleBuffer with all particles close in container
1369 // and haloParticleBuffer with owned, close particles in container.
1370 // This is always AoS-based because the container particles are found with region iterators,
1371 // which don't have an SoA interface.
1372 remainderHelperBufferContainerAoS<newton3>(f, container, particleBuffers, haloParticleBuffers);
1373
1374#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1375 timerBufferContainer.stop();
1376 timerBufferSoAConversion.start();
1377#endif
1378 if (useSoA) {
1379 // All (halo-)buffer interactions shall happen vectorized, hence, load all buffer data into SoAs
1380 for (auto &buffer : particleBuffers) {
1381 f->SoALoader(buffer, buffer._particleSoABuffer, 0, /*skipSoAResize*/ false);
1382 }
1383 for (auto &buffer : haloParticleBuffers) {
1384 f->SoALoader(buffer, buffer._particleSoABuffer, 0, /*skipSoAResize*/ false);
1385 }
1386 }
1387#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1388 timerBufferSoAConversion.stop();
1389 timerPBufferPBuffer.start();
1390#endif
1391
1392 // step 3. particleBuffer with itself and all other buffers
1393 remainderHelperBufferBuffer<newton3>(f, particleBuffers, useSoA);
1394
1395#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1396 timerPBufferPBuffer.stop();
1397 timerPBufferHBuffer.start();
1398#endif
1399
1400 // step 4. particleBuffer with haloParticleBuffer
1401 remainderHelperBufferHaloBuffer(f, particleBuffers, haloParticleBuffers, useSoA);
1402
1403#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1404 timerPBufferHBuffer.stop();
1405#endif
1406
1407 // unpack particle SoAs. Halo data is not interesting
1408 if (useSoA) {
1409 for (auto &buffer : particleBuffers) {
1410 f->SoAExtractor(buffer, buffer._particleSoABuffer, 0);
1411 }
1412 }
1413
1414 AutoPasLog(TRACE, "Timer Buffers <-> Container (1+2): {}", timerBufferContainer.getTotalTime());
1415 AutoPasLog(TRACE, "Timer PBuffers<-> PBuffer ( 3): {}", timerPBufferPBuffer.getTotalTime());
1416 AutoPasLog(TRACE, "Timer PBuffers<-> HBuffer ( 4): {}", timerPBufferHBuffer.getTotalTime());
1417 AutoPasLog(TRACE, "Timer Load and extract SoA buffers: {}", timerBufferSoAConversion.getTotalTime());
1418
1419 // Note: haloParticleBuffer with itself is NOT needed, as interactions between halo particles are unneeded!
1420}
1421
1422template <class Particle_T>
1423template <bool newton3, class ContainerType, class PairwiseFunctor>
1425 PairwiseFunctor *f, ContainerType &container, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1426 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1428 using namespace autopas::utils::ArrayMath::literals;
1429
1430 // Bunch of shorthands
1431 const auto cutoff = container.getCutoff();
1432 const auto interactionLength = container.getInteractionLength();
1433 const auto interactionLengthInv = 1. / interactionLength;
1434 const auto haloBoxMin = container.getBoxMin() - interactionLength;
1435 const auto totalBoxLengthInv = 1. / (container.getBoxMax() + interactionLength - haloBoxMin);
1436 const std::array<size_t, 3> spacialLocksPerDim{
1437 _spacialLocks.size(),
1438 _spacialLocks[0].size(),
1439 _spacialLocks[0][0].size(),
1440 };
1441
1442 // Helper function to obtain the lock responsible for a given position.
1443 // Implemented as lambda because it can reuse a lot of information that is known in this context.
1444 const auto getSpacialLock = [&](const std::array<double, 3> &pos) -> std::mutex & {
1445 const auto posDistFromLowerCorner = pos - haloBoxMin;
1446 const auto relativePos = posDistFromLowerCorner * totalBoxLengthInv;
1447 // Lock coordinates are the position scaled by the number of locks
1448 const auto lockCoords =
1449 static_cast_copy_array<size_t>(static_cast_copy_array<double>(spacialLocksPerDim) * relativePos);
1450 return *_spacialLocks[lockCoords[0]][lockCoords[1]][lockCoords[2]];
1451 };
1452
1453 // one halo and particle buffer pair per thread
1454 AUTOPAS_OPENMP(parallel for schedule(static, 1) default(shared))
1455 for (int bufferId = 0; bufferId < particleBuffers.size(); ++bufferId) {
1456 auto &particleBuffer = particleBuffers[bufferId];
1457 auto &haloParticleBuffer = haloParticleBuffers[bufferId];
1458 // 1. particleBuffer with all close particles in container
1459 for (auto &&p1 : particleBuffer) {
1460 const auto pos = p1.getR();
1461 const auto min = pos - cutoff;
1462 const auto max = pos + cutoff;
1463 container.forEachInRegion(
1464 [&](auto &p2) {
1465 if constexpr (newton3) {
1466 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
1467 f->AoSFunctor(p1, p2, true);
1468 } else {
1469 f->AoSFunctor(p1, p2, false);
1470 // no need to calculate force enacted on a halo
1471 if (not p2.isHalo()) {
1472 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
1473 f->AoSFunctor(p2, p1, false);
1474 }
1475 }
1476 },
1477 min, max, IteratorBehavior::ownedOrHalo);
1478 }
1479
1480 // 2. haloParticleBuffer with owned, close particles in container
1481 for (auto &&p1halo : haloParticleBuffer) {
1482 const auto pos = p1halo.getR();
1483 const auto min = pos - cutoff;
1484 const auto max = pos + cutoff;
1485 container.forEachInRegion(
1486 [&](auto &p2) {
1487 // No need to apply anything to p1halo
1488 // -> AoSFunctor(p1, p2, false) not needed as it neither adds force nor Upot (potential energy)
1489 // -> newton3 argument needed for correct globals
1490 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
1491 f->AoSFunctor(p2, p1halo, newton3);
1492 },
1493 min, max, IteratorBehavior::owned);
1494 }
1495 }
1496}
1497
1498template <class Particle_T>
1499template <bool newton3, class PairwiseFunctor>
1501 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1502 bool useSoA) {
1503 if (useSoA) {
1504 remainderHelperBufferBufferSoA<newton3>(f, particleBuffers);
1505 } else {
1506 remainderHelperBufferBufferAoS<newton3>(f, particleBuffers);
1507 }
1508}
1509
1510template <class Particle_T>
1511template <bool newton3, class PairwiseFunctor>
1513 PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers) {
1514 // For all interactions between different buffers we turn newton3 always off,
1515 // which ensures that only one thread at a time is writing to a buffer.
1516 // This saves expensive locks.
1517
1518 // We can not use collapse here without locks, otherwise races would occur.
1519 AUTOPAS_OPENMP(parallel for)
1520 for (size_t bufferIdxI = 0; bufferIdxI < particleBuffers.size(); ++bufferIdxI) {
1521 for (size_t bufferIdxJOffset = 0; bufferIdxJOffset < particleBuffers.size(); ++bufferIdxJOffset) {
1522 // Let each bufferI use a different starting point for bufferJ to minimize false sharing
1523 const auto bufferIdxJ = (bufferIdxI + bufferIdxJOffset) % particleBuffers.size();
1524
1525 // interact the two buffers
1526 if (bufferIdxI == bufferIdxJ) {
1527 // CASE Same buffer
1528 // Only use Newton3 if it is allowed, and we are working on only one buffer. This avoids data races.
1529 const bool useNewton3 = newton3;
1530 auto &bufferRef = particleBuffers[bufferIdxI];
1531 const auto bufferSize = bufferRef.size();
1532 for (auto i = 0; i < bufferSize; ++i) {
1533 auto &p1 = bufferRef[i];
1534 // If Newton3 is disabled run over the whole buffer, otherwise only what is ahead
1535 for (auto j = useNewton3 ? i + 1 : 0; j < bufferSize; ++j) {
1536 if (i == j) {
1537 continue;
1538 }
1539 auto &p2 = bufferRef[j];
1540 f->AoSFunctor(p1, p2, useNewton3);
1541 }
1542 }
1543 } else {
1544 // CASE: Two buffers
1545 for (auto &p1 : particleBuffers[bufferIdxI]) {
1546 for (auto &p2 : particleBuffers[bufferIdxJ]) {
1547 f->AoSFunctor(p1, p2, false);
1548 }
1549 }
1550 }
1551 }
1552 }
1553}
1554
1555template <class Particle_T>
1556template <bool newton3, class PairwiseFunctor>
1558 PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers) {
1559 // we can not use collapse here without locks, otherwise races would occur.
1560 AUTOPAS_OPENMP(parallel for)
1561 for (size_t i = 0; i < particleBuffers.size(); ++i) {
1562 for (size_t jj = 0; jj < particleBuffers.size(); ++jj) {
1563 auto *particleBufferSoAA = &particleBuffers[i]._particleSoABuffer;
1564 // instead of starting every (parallel) iteration i at j == 0 offset them by i to minimize waiting times at locks
1565 const auto j = (i + jj) % particleBuffers.size();
1566 if (i == j) {
1567 // For buffer interactions where bufferA == bufferB we can always enable newton3 if it is allowed.
1568 f->SoAFunctorSingle(*particleBufferSoAA, newton3);
1569 } else {
1570 // For all interactions between different buffers we turn newton3 always off,
1571 // which ensures that only one thread at a time is writing to a buffer. This saves expensive locks.
1572 auto *particleBufferSoAB = &particleBuffers[j]._particleSoABuffer;
1573 f->SoAFunctorPair(*particleBufferSoAA, *particleBufferSoAB, false);
1574 }
1575 }
1576 }
1577}
1578
1579template <class Particle_T>
1580template <class PairwiseFunctor>
1582 PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1583 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA) {
1584 if (useSoA) {
1585 remainderHelperBufferHaloBufferSoA<PairwiseFunctor>(f, particleBuffers, haloParticleBuffers);
1586 } else {
1587 remainderHelperBufferHaloBufferAoS<PairwiseFunctor>(f, particleBuffers, haloParticleBuffers);
1588 }
1589}
1590
1591template <class Particle_T>
1592template <class PairwiseFunctor>
1594 PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1595 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1596 // Here, phase / color based parallelism turned out to be more efficient than tasks
1597 AUTOPAS_OPENMP(parallel)
1598 for (int interactionOffset = 0; interactionOffset < haloParticleBuffers.size(); ++interactionOffset) {
1599 AUTOPAS_OPENMP(for)
1600 for (size_t i = 0; i < particleBuffers.size(); ++i) {
1601 auto &particleBuffer = particleBuffers[i];
1602 auto &haloBuffer = haloParticleBuffers[(i + interactionOffset) % haloParticleBuffers.size()];
1603
1604 for (auto &p1 : particleBuffer) {
1605 for (auto &p2 : haloBuffer) {
1606 f->AoSFunctor(p1, p2, false);
1607 }
1608 }
1609 }
1610 }
1611}
1612
1613template <class Particle_T>
1614template <class PairwiseFunctor>
1616 PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1617 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1618 // Here, phase / color based parallelism turned out to be more efficient than tasks
1619 AUTOPAS_OPENMP(parallel)
1620 for (int interactionOffset = 0; interactionOffset < haloParticleBuffers.size(); ++interactionOffset) {
1621 AUTOPAS_OPENMP(for)
1622 for (size_t i = 0; i < particleBuffers.size(); ++i) {
1623 auto &particleBufferSoA = particleBuffers[i]._particleSoABuffer;
1624 auto &haloBufferSoA =
1625 haloParticleBuffers[(i + interactionOffset) % haloParticleBuffers.size()]._particleSoABuffer;
1626 f->SoAFunctorPair(particleBufferSoA, haloBufferSoA, false);
1627 }
1628 }
1629}
1630
1631template <class Particle_T>
1632template <bool newton3, class ContainerType, class TriwiseFunctor>
1634 TriwiseFunctor *f, ContainerType &container, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1635 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1636 // Vector to collect pointers to all buffer particles
1637 std::vector<Particle_T *> bufferParticles;
1638 const auto numOwnedBufferParticles = collectBufferParticles(bufferParticles, particleBuffers, haloParticleBuffers);
1639
1640 // The following part performs the main remainder traversal.
1641
1642 // only activate time measurements if it will actually be logged
1643#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1644 autopas::utils::Timer timerBufferBufferBuffer;
1645 autopas::utils::Timer timerBufferBufferContainer;
1646 autopas::utils::Timer timerBufferContainerContainer;
1647 timerBufferBufferBuffer.start();
1648#endif
1649
1650 // Step 1: Triwise interactions of all particles in the buffers (owned and halo)
1651 remainderHelper3bBufferBufferBufferAoS(bufferParticles, numOwnedBufferParticles, f);
1652
1653#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1654 timerBufferBufferBuffer.stop();
1655 timerBufferBufferContainer.start();
1656#endif
1657
1658 // Step 2: Triwise interactions of 2 buffer particles with 1 container particle
1659 remainderHelper3bBufferBufferContainerAoS(bufferParticles, numOwnedBufferParticles, container, f);
1660
1661#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1662 timerBufferBufferContainer.stop();
1663 timerBufferContainerContainer.start();
1664#endif
1665
1666 // Step 3: Triwise interactions of 1 buffer particle and 2 container particles
1667 remainderHelper3bBufferContainerContainerAoS<newton3>(bufferParticles, numOwnedBufferParticles, container, f);
1668
1669#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
1670 timerBufferContainerContainer.stop();
1671#endif
1672
1673 AutoPasLog(TRACE, "Timer Buffer <-> Buffer <-> Buffer : {}", timerBufferBufferBuffer.getTotalTime());
1674 AutoPasLog(TRACE, "Timer Buffer <-> Buffer <-> Container : {}", timerBufferBufferContainer.getTotalTime());
1675 AutoPasLog(TRACE, "Timer Buffer <-> Container <-> Container : {}", timerBufferContainerContainer.getTotalTime());
1676}
1677
1678template <class Particle_T>
1680 std::vector<Particle_T *> &bufferParticles, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1681 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1682 // Reserve the needed amount
1683 auto cellToVec = [](auto &cell) -> std::vector<Particle_T> & { return cell._particles; };
1684
1685 const size_t numOwnedBufferParticles =
1686 std::transform_reduce(particleBuffers.begin(), particleBuffers.end(), 0, std::plus<>(),
1687 [&](auto &vec) { return cellToVec(vec).size(); });
1688
1689 const size_t numHaloBufferParticles =
1690 std::transform_reduce(haloParticleBuffers.begin(), haloParticleBuffers.end(), 0, std::plus<>(),
1691 [&](auto &vec) { return cellToVec(vec).size(); });
1692
1693 bufferParticles.reserve(numOwnedBufferParticles + numHaloBufferParticles);
1694
1695 // Collect owned buffer particles
1696 for (auto &buffer : particleBuffers) {
1697 for (auto &particle : buffer._particles) {
1698 bufferParticles.push_back(&particle);
1699 }
1700 }
1701
1702 // Collect halo buffer particles
1703 for (auto &buffer : haloParticleBuffers) {
1704 for (auto &particle : buffer._particles) {
1705 bufferParticles.push_back(&particle);
1706 }
1707 }
1708
1709 return numOwnedBufferParticles;
1710}
1711
1712template <class Particle_T>
1713template <class TriwiseFunctor>
1714void LogicHandler<Particle_T>::remainderHelper3bBufferBufferBufferAoS(const std::vector<Particle_T *> &bufferParticles,
1715 const size_t numOwnedBufferParticles,
1716 TriwiseFunctor *f) {
1717 AUTOPAS_OPENMP(parallel for)
1718 for (auto i = 0; i < numOwnedBufferParticles; ++i) {
1719 Particle_T &p1 = *bufferParticles[i];
1720
1721 for (auto j = 0; j < bufferParticles.size(); ++j) {
1722 if (i == j) continue;
1723 Particle_T &p2 = *bufferParticles[j];
1724
1725 for (auto k = j + 1; k < bufferParticles.size(); ++k) {
1726 if (k == i) continue;
1727 Particle_T &p3 = *bufferParticles[k];
1728
1729 f->AoSFunctor(p1, p2, p3, false);
1730 }
1731 }
1732 }
1733}
1734
1735template <class Particle_T>
1736template <class ContainerType, class TriwiseFunctor>
1738 const std::vector<Particle_T *> &bufferParticles, const size_t numOwnedBufferParticles, ContainerType &container,
1739 TriwiseFunctor *f) {
1741 using namespace autopas::utils::ArrayMath::literals;
1742
1743 const auto haloBoxMin = container.getBoxMin() - container.getInteractionLength();
1744 const auto interactionLengthInv = 1. / container.getInteractionLength();
1745 const double cutoff = container.getCutoff();
1746
1747 AUTOPAS_OPENMP(parallel for)
1748 for (auto i = 0; i < bufferParticles.size(); ++i) {
1749 Particle_T &p1 = *bufferParticles[i];
1750 const auto pos = p1.getR();
1751 const auto min = pos - cutoff;
1752 const auto max = pos + cutoff;
1753
1754 for (auto j = 0; j < bufferParticles.size(); ++j) {
1755 if (j == i) continue;
1756 Particle_T &p2 = *bufferParticles[j];
1757
1758 container.forEachInRegion(
1759 [&](auto &p3) {
1760 const auto lockCoords = static_cast_copy_array<size_t>((p3.getR() - haloBoxMin) * interactionLengthInv);
1761 if (i < numOwnedBufferParticles) f->AoSFunctor(p1, p2, p3, false);
1762 if (!p3.isHalo() && i < j) {
1763 const std::lock_guard<std::mutex> lock(*_spacialLocks[lockCoords[0]][lockCoords[1]][lockCoords[2]]);
1764 f->AoSFunctor(p3, p1, p2, false);
1765 }
1766 },
1767 min, max, IteratorBehavior::ownedOrHalo);
1768 }
1769 }
1770}
1771
1772template <class Particle_T>
1773template <bool newton3, class ContainerType, class TriwiseFunctor>
1775 const std::vector<Particle_T *> &bufferParticles, const size_t numOwnedBufferParticles, ContainerType &container,
1776 TriwiseFunctor *f) {
1777 // Todo: parallelize without race conditions - https://github.com/AutoPas/AutoPas/issues/904
1779 using namespace autopas::utils::ArrayMath::literals;
1780
1781 const double cutoff = container.getCutoff();
1782
1783 for (auto i = 0; i < bufferParticles.size(); ++i) {
1784 Particle_T &p1 = *bufferParticles[i];
1785 const auto pos = p1.getR();
1786 const auto boxmin = pos - cutoff;
1787 const auto boxmax = pos + cutoff;
1788
1789 auto p2Iter = container.getRegionIterator(
1790 boxmin, boxmax, IteratorBehavior::ownedOrHalo | IteratorBehavior::forceSequential, nullptr);
1791 for (; p2Iter.isValid(); ++p2Iter) {
1792 Particle_T &p2 = *p2Iter;
1793
1794 auto p3Iter = p2Iter;
1795 ++p3Iter;
1796
1797 for (; p3Iter.isValid(); ++p3Iter) {
1798 Particle_T &p3 = *p3Iter;
1799
1800 if constexpr (newton3) {
1801 f->AoSFunctor(p1, p2, p3, true);
1802 } else {
1803 if (i < numOwnedBufferParticles) {
1804 f->AoSFunctor(p1, p2, p3, false);
1805 }
1806 if (p2.isOwned()) {
1807 f->AoSFunctor(p2, p1, p3, false);
1808 }
1809 if (p3.isOwned()) {
1810 f->AoSFunctor(p3, p1, p2, false);
1811 }
1812 }
1813 }
1814 }
1815 }
1816}
1817
1818template <typename Particle_T>
1819template <class Functor>
1820std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::selectConfiguration(
1821 Functor &functor, const InteractionTypeOption &interactionType) {
1822 auto &autoTuner = *_autoTunerRefs[interactionType];
1823
1824 // Todo: Make LiveInfo persistent between multiple functor calls in the same timestep (e.g. 2B + 3B)
1825 // https://github.com/AutoPas/AutoPas/issues/916
1826 LiveInfo info{};
1827#ifdef AUTOPAS_LOG_LIVEINFO
1828 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1829 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1830 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1831 _liveInfoLogger.logLiveInfo(info, _iteration);
1832#endif
1833
1834 // if this iteration is not relevant, take the same algorithm config as before.
1835 if (not functor.isRelevantForTuning()) {
1836 auto configuration = autoTuner.getCurrentConfig();
1837 auto [traversalPtr, _] = isConfigurationApplicable(configuration, functor);
1838
1839 if (not traversalPtr) {
1840 // TODO: Can we handle this case gracefully?
1842 "LogicHandler: Functor {} is not relevant for tuning but the given configuration is not applicable!",
1843 functor.getName());
1844 }
1845 return {configuration, std::move(traversalPtr), false};
1846 }
1847
1848 if (autoTuner.needsLiveInfo()) {
1849 // If live info has not been gathered yet, gather it now and send it to the tuner.
1850 if (info.get().empty()) {
1851 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1852 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1853 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1854 }
1855 autoTuner.receiveLiveInfo(info);
1856 }
1857
1858 auto [configuration, stillTuning] = autoTuner.getNextConfig();
1859
1860 // loop as long as we don't get a valid configuration
1861 do {
1862 // applicability check also sets the container
1863 auto [traversalPtr, rejectIndefinitely] = isConfigurationApplicable(configuration, functor);
1864 if (traversalPtr) {
1865 return {configuration, std::move(traversalPtr), stillTuning};
1866 }
1867 // if no config is left after rejecting this one, an exception is thrown here.
1868 std::tie(configuration, stillTuning) = autoTuner.rejectConfig(configuration, rejectIndefinitely);
1869 } while (true);
1870}
1871
1872template <typename Particle_T>
1874 std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer) {
1875 // copy particles so they do not get lost when the container is switched
1876 if (_currentContainer != nullptr and newContainer != nullptr) {
1877 // with these assumptions slightly more space is reserved as numParticlesTotal already includes halos
1878 const auto numParticlesTotal = _currentContainer->size();
1879 const auto numParticlesHalo = utils::NumParticlesEstimator::estimateNumHalosUniform(
1880 numParticlesTotal, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
1881 _currentContainer->getInteractionLength());
1882
1883 newContainer->reserve(numParticlesTotal, numParticlesHalo);
1884 for (auto particleIter = _currentContainer->begin(IteratorBehavior::ownedOrHalo); particleIter.isValid();
1885 ++particleIter) {
1886 // add a particle as inner if it is owned
1887 if (particleIter->isOwned()) {
1888 newContainer->addParticle(*particleIter);
1889 } else {
1890 newContainer->addHaloParticle(*particleIter);
1891 }
1892 }
1893 }
1894
1895 _currentContainer = std::move(newContainer);
1896}
1897
1898template <typename Particle_T>
1899template <class Functor>
1901 const InteractionTypeOption &interactionType) {
1902 if (not _interactionTypes.count(interactionType)) {
1904 "LogicHandler::computeInteractionsPipeline(): AutPas was not initialized for the Functor's interactions type: "
1905 "{}.",
1906 interactionType);
1907 }
1909 utils::Timer tuningTimer;
1910 tuningTimer.start();
1911 const auto [configuration, traversalPtr, stillTuning] = selectConfiguration(*functor, interactionType);
1912 tuningTimer.stop();
1913 auto &autoTuner = *_autoTunerRefs[interactionType];
1914 autoTuner.logTuningResult(stillTuning, tuningTimer.getTotalTime());
1915
1916 // Retrieve rebuild info before calling `computeInteractions()` to get the correct value.
1917 const auto rebuildIteration = not _neighborListsAreValid.load(std::memory_order_relaxed);
1918
1920 AutoPasLog(DEBUG, "Iterating with configuration: {} tuning: {}", configuration.toString(), stillTuning);
1921 const IterationMeasurements measurements = computeInteractions(*functor, *traversalPtr);
1922
1924 auto bufferSizeListing = [](const auto &buffers) -> std::string {
1925 std::stringstream ss;
1926 size_t sum = 0;
1927 for (const auto &buffer : buffers) {
1928 ss << buffer.size() << ", ";
1929 sum += buffer.size();
1930 }
1931 ss << " Total: " << sum;
1932 return ss.str();
1933 };
1934 AutoPasLog(TRACE, "particleBuffer size : {}", bufferSizeListing(_particleBuffer));
1935 AutoPasLog(TRACE, "haloParticleBuffer size : {}", bufferSizeListing(_haloParticleBuffer));
1936 AutoPasLog(DEBUG, "Type of interaction : {}", interactionType.to_string());
1937 AutoPasLog(DEBUG, "Container::computeInteractions took {} ns", measurements.timeComputeInteractions);
1938 AutoPasLog(DEBUG, "RemainderTraversal took {} ns", measurements.timeRemainderTraversal);
1939 AutoPasLog(DEBUG, "RebuildNeighborLists took {} ns", measurements.timeRebuild);
1940 AutoPasLog(DEBUG, "AutoPas::computeInteractions took {} ns", measurements.timeTotal);
1941 if (measurements.energyMeasurementsPossible) {
1942 AutoPasLog(DEBUG, "Energy Consumption: Watts: {} Joules: {} Seconds: {}", measurements.energyWatts,
1943 measurements.energyJoules, measurements.energyDeltaT);
1944 }
1945 _iterationLogger.logIteration(configuration, _iteration, functor->getName(), stillTuning, tuningTimer.getTotalTime(),
1946 measurements);
1947
1948 _flopLogger.logIteration(_iteration, functor->getNumFLOPs(), functor->getHitRate());
1949
1951 // if this was a major iteration add measurements
1952 if (functor->isRelevantForTuning()) {
1953 if (stillTuning) {
1954 // choose the metric of interest
1955 const auto measurement = [&]() {
1956 switch (autoTuner.getTuningMetric()) {
1958 return measurements.timeTotal;
1960 return measurements.energyTotal;
1961 default:
1962 utils::ExceptionHandler::exception("LogicHandler::computeInteractionsPipeline(): Unknown tuning metric.");
1963 return 0l;
1964 }
1965 }();
1966 autoTuner.addMeasurement(measurement, rebuildIteration);
1967 }
1968 } else {
1969 AutoPasLog(TRACE, "Skipping adding of sample because functor is not marked relevant.");
1970 }
1971 ++_functorCalls;
1972 return stillTuning;
1973}
1974
1975template <typename Particle_T>
1976template <class Functor>
1977std::tuple<std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::isConfigurationApplicable(
1978 const Configuration &config, Functor &functor) {
1979 // Check if the container supports the traversal
1980 const auto allContainerTraversals =
1981 compatibleTraversals::allCompatibleTraversals(config.container, config.interactionType);
1982 if (allContainerTraversals.find(config.traversal) == allContainerTraversals.end()) {
1983 AutoPasLog(WARN, "Configuration rejected: Container {} does not support the traversal {}.", config.container,
1984 config.traversal);
1985 return {nullptr, /*rejectIndefinitely*/ true};
1986 }
1987
1988 // Check if the functor supports the required Newton 3 mode
1989 if ((config.newton3 == Newton3Option::enabled and not functor.allowsNewton3()) or
1990 (config.newton3 == Newton3Option::disabled and not functor.allowsNonNewton3())) {
1991 AutoPasLog(DEBUG, "Configuration rejected: The functor doesn't support Newton 3 {}!", config.newton3);
1992 return {nullptr, /*rejectIndefinitely*/ true};
1993 }
1994
1995 auto containerInfo =
1996 ContainerSelectorInfo(_currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
1997 _currentContainer->getCutoff(), config.cellSizeFactor, _currentContainer->getVerletSkin(),
1998 _verletClusterSize, _sortingThreshold, config.loadEstimator);
1999 auto containerPtr = ContainerSelector<Particle_T>::generateContainer(config.container, containerInfo);
2000 const auto traversalInfo = containerPtr->getTraversalSelectorInfo();
2001
2002 // Generates a traversal if applicable, otherwise returns a nullptr
2003 auto traversalPtr = TraversalSelector::generateTraversalFromConfig<Particle_T, Functor>(
2004 config, functor, containerPtr->getTraversalSelectorInfo());
2005
2006 if (traversalPtr and (_currentContainer->getContainerType() != containerPtr->getContainerType() or
2007 containerInfo != _currentContainerSelectorInfo)) {
2008 _currentContainerSelectorInfo = containerInfo;
2009 setCurrentContainer(std::move(containerPtr));
2010 }
2011
2012 return {std::move(traversalPtr), /*rejectIndefinitely*/ false};
2013}
2014
2015} // 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:93
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
void SoALoader(ParticleCell &cell, SoA< SoAArraysType > &soa, size_t offset, bool skipSoAResize)
Copies the AoS data of the given cell in the given soa.
Definition: Functor.h:112
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.
void SoAExtractor(ParticleCell &cell, SoA< SoAArraysType > &soa, size_t offset)
Copies the data stored in the soa back into the cell.
Definition: Functor.h:126
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:45
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:1194
void reserve(size_t numParticles, size_t numHaloParticles)
Reserves space in the particle buffers and the container.
Definition: LogicHandler.h:302
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:1977
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:480
std::vector< Particle_T > updateContainer()
Updates the container.
Definition: LogicHandler.h:162
unsigned long getNumberOfParticlesOwned() const
Get the number of owned particles.
Definition: LogicHandler.h:542
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:1900
std::tuple< const std::vector< FullParticleCell< Particle_T > > &, const std::vector< FullParticleCell< Particle_T > > & > getParticleBuffers() const
Getter for the particle buffers.
Definition: LogicHandler.h:1230
void addParticle(const Particle_T &p)
Adds a particle to the container.
Definition: LogicHandler.h:318
void decreaseParticleCounter(Particle_T &particle)
Decrease the correct internal particle counters.
Definition: LogicHandler.h:413
std::vector< Particle_T > collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer)
Collects leaving particles from buffer and potentially inserts owned particles to the container.
Definition: LogicHandler.h:109
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:54
bool neighborListsAreValid()
Checks if in the next iteration the neighbor lists have to be rebuilt.
Definition: LogicHandler.h:1151
unsigned long getNumberOfParticlesHalo() const
Get the number of halo particles.
Definition: LogicHandler.h:548
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:391
bool checkTuningStates(const InteractionTypeOption &interactionType)
Check if other autotuners for any other interaction types are still in a tuning phase.
Definition: LogicHandler.h:555
void checkNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:1170
Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior)
Create the additional vectors vector for a given iterator behavior.
Definition: LogicHandler.h:451
ParticleContainerInterface< Particle_T > & getContainer()
Returns a non-const reference to the currently selected particle container.
Definition: LogicHandler.h:102
void addHaloParticle(const Particle_T &haloParticle)
Adds a particle to the container that lies in the halo region of the container.
Definition: LogicHandler.h:345
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:519
void deleteAllParticles()
Deletes all particles.
Definition: LogicHandler.h:375
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:488
bool getNeighborListsInvalidDoDynamicRebuild()
getter function for _neighborListInvalidDoDynamicRebuild
Definition: LogicHandler.h:1146
void resetNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:1189
double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase=false) const
Getter for the mean rebuild frequency.
Definition: LogicHandler.h:613
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:209
void reserve(size_t numParticles)
Estimates the number of halo particles via autopas::utils::NumParticlesEstimator::estimateNumHalosUni...
Definition: LogicHandler.h:289
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:498
PairwiseFunctor class.
Definition: PairwiseFunctor.h:31
virtual void AoSFunctor(Particle_T &i, Particle_T &j, bool newton3)
PairwiseFunctor for arrays of structures (AoS).
Definition: PairwiseFunctor.h:56
virtual void SoAFunctorPair(SoAView< SoAArraysType > soa1, SoAView< SoAArraysType > soa2, bool newton3)
PairwiseFunctor for structure of arrays (SoA)
Definition: PairwiseFunctor.h:102
virtual void SoAFunctorSingle(SoAView< SoAArraysType > soa, bool newton3)
PairwiseFunctor for structure of arrays (SoA)
Definition: PairwiseFunctor.h:70
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:37
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
TriwiseFunctor class.
Definition: TriwiseFunctor.h:28
virtual void AoSFunctor(Particle_T &i, Particle_T &j, Particle_T &k, bool newton3)
TriwiseFunctor for arrays of structures (AoS).
Definition: TriwiseFunctor.h:54
@ 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:20
void start()
start the timer.
Definition: Timer.cpp:17
long getTotalTime() const
Get total accumulated time.
Definition: Timer.h:53
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: Timer.cpp:25
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< T, SIZE > max(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Takes elementwise maximum and returns the result.
Definition: ArrayMath.h:96
constexpr std::array< T, SIZE > min(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Takes elementwise minimum, returns the result.
Definition: ArrayMath.h:62
void balanceVectors(OuterContainerT &vecvec)
Given a collection of vectors, redistributes the elements of the vectors so they all have the same (o...
Definition: ArrayUtils.h:200
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:81
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:102
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 energyTotal
Total energy consumed so far.
Definition: IterationMeasurements.h:57
long timeTotal
Time it takes for the complete iteratePairwise pipeline.
Definition: IterationMeasurements.h:32
long timeRemainderTraversal
Time it takes for the Remainder Traversal.
Definition: IterationMeasurements.h:22
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