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