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