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
32#include "autopas/utils/Timer.h"
41
42namespace autopas {
43
49template <typename Particle_T>
51 public:
64 LogicHandler(const std::shared_ptr<TuningManager> &tunerManager, const LogicHandlerInfo &logicHandlerInfo,
65 unsigned int rebuildFrequency, const std::string &outputSuffix, size_t aosSortingThresholdFallback,
66 size_t soaSortingThresholdFallback)
67 : _tuningManager(tunerManager),
68 _logicHandlerInfo(logicHandlerInfo),
69 _neighborListRebuildFrequency{rebuildFrequency},
70 _particleBuffer(autopas_get_max_threads()),
71 _haloParticleBuffer(autopas_get_max_threads()),
72 _remainderPairwiseInteractionHandler(_spatialLocks),
73 _remainderTriwiseInteractionHandler(_spatialLocks),
74 _verletClusterSize(logicHandlerInfo.verletClusterSize),
75 _aosSortingThresholdFallback(aosSortingThresholdFallback),
76 _soaSortingThresholdFallback(soaSortingThresholdFallback),
77 _iterationLogger(outputSuffix,
78 std::any_of(tunerManager->getAutoTuners().begin(), tunerManager->getAutoTuners().end(),
79 [](const auto &tuner) { return tuner.second->canMeasureEnergy(); })),
80 _flopLogger(outputSuffix),
81 _liveInfoLogger(outputSuffix) {
82 using namespace autopas::utils::ArrayMath::literals;
83 // Initialize AutoPas with tuners for given interaction types
84 for (const auto &[interactionType, tuner] : tunerManager->getAutoTuners()) {
85 _interactionTypes.insert(interactionType);
86
87 const auto configuration = tuner->getCurrentConfig();
88 // initialize the container and make sure it is valid
89 _currentContainerSelectorInfo = ContainerSelectorInfo{
90 _logicHandlerInfo.boxMin, _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff,
91 configuration.cellSizeFactor, _logicHandlerInfo.verletSkin, _verletClusterSize,
92 _aosSortingThresholdFallback, _soaSortingThresholdFallback, configuration.loadEstimator};
93 _currentContainer =
94 ContainerSelector<Particle_T>::generateContainer(configuration.container, _currentContainerSelectorInfo);
95 checkMinimalSize();
96 }
97
98 // initialize locks needed for remainder traversal
99 const auto interactionLength = logicHandlerInfo.cutoff + logicHandlerInfo.verletSkin;
100 const auto interactionLengthInv = 1. / interactionLength;
101 const auto boxLengthWithHalo = logicHandlerInfo.boxMax - logicHandlerInfo.boxMin + (2 * interactionLength);
102 initSpatialLocks(boxLengthWithHalo, interactionLengthInv);
103 }
104
109 ParticleContainerInterface<Particle_T> &getContainer() { return *_currentContainer; }
110
116 [[nodiscard]] std::vector<Particle_T> collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer) {
117 const auto &boxMin = _currentContainer->getBoxMin();
118 const auto &boxMax = _currentContainer->getBoxMax();
119 std::vector<Particle_T> leavingBufferParticles{};
120 for (auto &cell : _particleBuffer) {
121 auto &buffer = cell._particles;
122 if (insertOwnedParticlesToContainer) {
123 // Can't be const because we potentially modify ownership before re-adding
124 for (auto &p : buffer) {
125 if (p.isDummy()) {
126 continue;
127 }
128 if (utils::inBox(p.getR(), boxMin, boxMax)) {
129 p.setOwnershipState(OwnershipState::owned);
130 _currentContainer->addParticle(p);
131 } else {
132 leavingBufferParticles.push_back(p);
133 }
134 }
135 buffer.clear();
136 } else {
137 for (auto iter = buffer.begin(); iter < buffer.end();) {
138 auto &p = *iter;
139
140 auto fastRemoveP = [&]() {
141 // Fast remove of particle, i.e., swap with last entry && pop.
142 std::swap(p, buffer.back());
143 buffer.pop_back();
144 // Do not increment the iter afterward!
145 };
146 if (p.isDummy()) {
147 // We remove dummies!
148 fastRemoveP();
149 // In case we swapped a dummy here, don't increment the iterator and do another iteration to check again.
150 continue;
151 }
152 // if p was a dummy a new particle might now be at the memory location of p so we need to check that.
153 // We also just might have deleted the last particle in the buffer in that case the inBox check is meaningless
154 if (not buffer.empty() and utils::notInBox(p.getR(), boxMin, boxMax)) {
155 leavingBufferParticles.push_back(p);
156 fastRemoveP();
157 } else {
158 ++iter;
159 }
160 }
161 }
162 }
163 return leavingBufferParticles;
164 }
165
169 std::vector<Particle_T> updateContainer() {
170 ++_iteration;
171
172#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
174
175 if (_tuningManager->isStartOfTuningPhase(_iteration)) {
176 _numRebuildsInNonTuningPhase = 0;
177 }
178
179 // Rebuild frequency estimation should be triggered early in the tuning phase.
180 // This is necessary because runtime prediction for each trial configuration
181 // depends on the rebuild frequency.
182 // To avoid the influence of poorly initialized velocities at the start of the simulation,
183 // the rebuild frequency is estimated at iteration corresponding to the last sample of the first configuration.
184 // The rebuild frequency estimated here is then reused for the remainder of the tuning phase.
185 if (_tuningManager->inFirstConfigurationLastSample(_iteration)) {
186 // Fetch the needed information for estimating the rebuild frequency from _logicHandlerInfo
187 // and estimate the current rebuild frequency using the velocity method.
188 double rebuildFrequencyEstimate =
189 getVelocityMethodRFEstimate(_logicHandlerInfo.verletSkin, _logicHandlerInfo.deltaT);
190 double userProvidedRF = static_cast<double>(_neighborListRebuildFrequency);
191 // The user defined rebuild frequency is considered as the upper bound.
192 // If velocity method estimate exceeds upper bound, set the rebuild frequency to the user defined value.
193 // This is done because we currently use the user defined rebuild frequency as the upper bound to avoid expensive
194 // buffer interactions.
195 _tuningManager->setRebuildFrequency(std::min(userProvidedRF, rebuildFrequencyEstimate));
196 }
197#endif
198 bool doDataStructureUpdate = not neighborListsAreValid();
199
200 if (_tuningManager->tuningPhaseJustFinished()) {
201 _iterationAfterLastTuningPhase = _iteration;
202 }
203 // We will do a rebuild in this timestep
204 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
205 _stepsSinceLastListRebuild = 0;
206 }
207 ++_stepsSinceLastListRebuild;
208
209 // The next call also adds particles to the container if doDataStructureUpdate is true.
210 auto leavingBufferParticles = collectLeavingParticlesFromBuffer(doDataStructureUpdate);
211
212 AutoPasLog(TRACE, "Initiating container update.");
213 auto leavingParticles = _currentContainer->updateContainer(not doDataStructureUpdate);
214 leavingParticles.insert(leavingParticles.end(), leavingBufferParticles.begin(), leavingBufferParticles.end());
215
216 // Subtract the amount of leaving particles from the number of owned particles.
217 _numParticlesOwned.fetch_sub(leavingParticles.size(), std::memory_order_relaxed);
218 // updateContainer deletes all halo particles.
219 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
220 _numParticlesHalo.store(0, std::memory_order_relaxed);
221 return leavingParticles;
222 }
223
230 std::vector<Particle_T> resizeBox(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax) {
231 using namespace autopas::utils::ArrayMath::literals;
232 const auto &oldMin = _currentContainer->getBoxMin();
233 const auto &oldMax = _currentContainer->getBoxMax();
234
235 // if nothing changed, do nothing
236 if (oldMin == boxMin and oldMax == boxMax) {
237 return {};
238 }
239
240 // sanity check that new size is actually positive
241 for (size_t i = 0; i < boxMin.size(); ++i) {
242 if (boxMin[i] >= boxMax[i]) {
244 "New box size in dimension {} is not positive!\nboxMin[{}] = {}\nboxMax[{}] = {}", i, i, boxMin[i], i,
245 boxMax[i]);
246 }
247 }
248
249 // warn if domain changes too drastically
250 const auto newLength = boxMax - boxMin;
251 const auto oldLength = oldMax - oldMin;
252 const auto relDiffLength = newLength / oldLength;
253 for (size_t i = 0; i < newLength.size(); ++i) {
254 // warning threshold is set arbitrary and up for change if needed
255 if (relDiffLength[i] > 1.3 or relDiffLength[i] < 0.7) {
256 AutoPasLog(WARN,
257 "LogicHandler.resize(): Domain size changed drastically in dimension {}! Gathered AutoTuning "
258 "information might not be applicable anymore!\n"
259 "Size old box : {}\n"
260 "Size new box : {}\n"
261 "Relative diff: {}",
263 utils::ArrayUtils::to_string(relDiffLength));
264 }
265 }
266
267 // The new box size is valid, so update the current container info.
268 _currentContainerSelectorInfo.boxMin = boxMin;
269 _currentContainerSelectorInfo.boxMax = boxMax;
270
271 // check all particles
272 std::vector<Particle_T> particlesNowOutside;
273 for (auto pIter = _currentContainer->begin(); pIter.isValid(); ++pIter) {
274 // make sure only owned ones are present
275 if (not pIter->isOwned()) {
277 "LogicHandler::resizeBox() encountered non owned particle. "
278 "When calling resizeBox() these should be already deleted. "
279 "This could be solved by calling updateContainer() before resizeBox().");
280 }
281 // owned particles that are now outside are removed from the container and returned
282 if (not utils::inBox(pIter->getR(), boxMin, boxMax)) {
283 particlesNowOutside.push_back(*pIter);
286 }
287 }
288
289 // Resize by generating a new container with the new box size and moving all particles to it.
290 auto newContainer = ContainerSelector<Particle_T>::generateContainer(_currentContainer->getContainerType(),
291 _currentContainerSelectorInfo);
292 setCurrentContainer(std::move(newContainer));
293 // The container might have changed sufficiently that we would need a different number of spatial locks.
294 const auto boxLength = boxMax - boxMin;
295 const auto interactionLengthInv = 1. / _currentContainer->getInteractionLength();
296 initSpatialLocks(boxLength, interactionLengthInv);
297
298 // Set this flag, s.t., the container is rebuilt!
299 _neighborListsAreValid.store(false, std::memory_order_relaxed);
300
301 return particlesNowOutside;
302 }
303
310 void reserve(size_t numParticles) {
311 const auto numParticlesHaloEstimate = autopas::utils::NumParticlesEstimator::estimateNumHalosUniform(
312 numParticles, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
313 _currentContainer->getInteractionLength());
314 reserve(numParticles, numParticlesHaloEstimate);
315 }
316
323 void reserve(size_t numParticles, size_t numHaloParticles) {
324 const auto numHaloParticlesPerBuffer = numHaloParticles / _haloParticleBuffer.size();
325 for (auto &buffer : _haloParticleBuffer) {
326 buffer.reserve(numHaloParticlesPerBuffer);
327 }
328 // there is currently no good heuristic for this buffer, so reuse the one for halos.
329 for (auto &buffer : _particleBuffer) {
330 buffer.reserve(numHaloParticlesPerBuffer);
331 }
332
333 // reserve is called for the container only in the rebuild iterations.
334 // during non-rebuild iterations, particles are not added in the container but in buffer.
335 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
336 _currentContainer->reserve(numParticles, numHaloParticles);
337 }
338 }
339
343 void addParticle(const Particle_T &p) {
344 // first check that the particle actually belongs in the container
345 const auto &boxMin = _currentContainer->getBoxMin();
346 const auto &boxMax = _currentContainer->getBoxMax();
347 if (utils::notInBox(p.getR(), boxMin, boxMax)) {
349 "LogicHandler: Trying to add a particle that is not in the bounding box.\n"
350 "Box Min {}\n"
351 "Box Max {}\n"
352 "{}",
353 boxMin, boxMax, p.toString());
354 }
355 Particle_T particleCopy = p;
356 particleCopy.setOwnershipState(OwnershipState::owned);
357 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
358 // Container has to (about to) be invalid to be able to add Particles!
359 _currentContainer->template addParticle<false>(particleCopy);
360 } else {
361 // If the container is valid, we add it to the particle buffer.
362 _particleBuffer[autopas_get_thread_num()].addParticle(particleCopy);
363 }
364 _numParticlesOwned.fetch_add(1, std::memory_order_relaxed);
365 }
366
370 void addHaloParticle(const Particle_T &haloParticle) {
371 const auto &boxMin = _currentContainer->getBoxMin();
372 const auto &boxMax = _currentContainer->getBoxMax();
373 Particle_T haloParticleCopy = haloParticle;
374 if (utils::inBox(haloParticleCopy.getR(), boxMin, boxMax)) {
376 "LogicHandler: Trying to add a halo particle that is not outside the box of the container.\n"
377 "Box Min {}\n"
378 "Box Max {}\n"
379 "{}",
380 utils::ArrayUtils::to_string(boxMin), utils::ArrayUtils::to_string(boxMax), haloParticleCopy.toString());
381 }
382 haloParticleCopy.setOwnershipState(OwnershipState::halo);
383 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
384 // If the neighbor lists are not valid, we can add the particle.
385 _currentContainer->template addHaloParticle</* checkInBox */ false>(haloParticleCopy);
386 } else {
387 // Check if we can update an existing halo(dummy) particle.
388 bool updated = _currentContainer->updateHaloParticle(haloParticleCopy);
389 if (not updated) {
390 // If we couldn't find an existing particle, add it to the halo particle buffer.
391 _haloParticleBuffer[autopas_get_thread_num()].addParticle(haloParticleCopy);
392 }
393 }
394 _numParticlesHalo.fetch_add(1, std::memory_order_relaxed);
395 }
396
401 _neighborListsAreValid.store(false, std::memory_order_relaxed);
402 _currentContainer->deleteAllParticles();
403 std::for_each(_particleBuffer.begin(), _particleBuffer.end(), [](auto &buffer) { buffer.clear(); });
404 std::for_each(_haloParticleBuffer.begin(), _haloParticleBuffer.end(), [](auto &buffer) { buffer.clear(); });
405 // all particles are gone -> reset counters.
406 _numParticlesOwned.store(0, std::memory_order_relaxed);
407 _numParticlesHalo.store(0, std::memory_order_relaxed);
408 }
409
416 std::tuple<bool, bool> deleteParticleFromBuffers(Particle_T &particle) {
417 // find the buffer the particle belongs to
418 auto &bufferCollection = particle.isOwned() ? _particleBuffer : _haloParticleBuffer;
419 for (auto &cell : bufferCollection) {
420 auto &buffer = cell._particles;
421 // if the address of the particle is between start and end of the buffer it is in this buffer
422 if (not buffer.empty() and &(buffer.front()) <= &particle and &particle <= &(buffer.back())) {
423 const bool isRearParticle = &particle == &buffer.back();
424 // swap-delete
425 particle = buffer.back();
426 buffer.pop_back();
427 return {true, not isRearParticle};
428 }
429 }
430 return {false, true};
431 }
432
438 void decreaseParticleCounter(Particle_T &particle) {
439 if (particle.isOwned()) {
440 _numParticlesOwned.fetch_sub(1, std::memory_order_relaxed);
441 } else {
442 _numParticlesHalo.fetch_sub(1, std::memory_order_relaxed);
443 }
444 }
445
466 template <class Functor>
467 bool computeInteractionsPipeline(Functor *functor, const InteractionTypeOption &interactionType);
468
475 template <class Iterator>
476 typename Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior) {
477 typename Iterator::ParticleVecType additionalVectors;
478 if (not(behavior & IteratorBehavior::containerOnly)) {
479 additionalVectors.reserve(static_cast<bool>(behavior & IteratorBehavior::owned) * _particleBuffer.size() +
480 static_cast<bool>(behavior & IteratorBehavior::halo) * _haloParticleBuffer.size());
481 if (behavior & IteratorBehavior::owned) {
482 for (auto &buffer : _particleBuffer) {
483 // Don't insert empty buffers. This also means that we won't pick up particles added during iterating if they
484 // go to the buffers. But since we wouldn't pick them up if they go into the container to a cell that the
485 // iterators already passed this is unsupported anyways.
486 if (not buffer.isEmpty()) {
487 additionalVectors.push_back(&(buffer._particles));
488 }
489 }
490 }
491 if (behavior & IteratorBehavior::halo) {
492 for (auto &buffer : _haloParticleBuffer) {
493 if (not buffer.isEmpty()) {
494 additionalVectors.push_back(&(buffer._particles));
495 }
496 }
497 }
498 }
499 return additionalVectors;
500 }
501
506 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, false>>(behavior);
507 return _currentContainer->begin(behavior, std::ref(additionalVectors));
508 }
509
513 ContainerIterator<Particle_T, false, false> begin(IteratorBehavior behavior) const {
514 auto additionalVectors =
516 behavior);
517 return _currentContainer->begin(behavior, std::ref(additionalVectors));
518 }
519
523 ContainerIterator<Particle_T, true, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
524 const std::array<double, 3> &higherCorner,
525 IteratorBehavior behavior) {
526 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
527 for (size_t d = 0; d < 3; ++d) {
528 if (lowerCorner[d] > higherCorner[d]) {
530 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
531 "Lower corner: {}\n"
532 "Upper corner: {}",
533 lowerCorner, higherCorner);
534 }
535 }
536
537 auto additionalVectors = gatherAdditionalVectors<ContainerIterator<Particle_T, true, true>>(behavior);
538 return _currentContainer->getRegionIterator(lowerCorner, higherCorner, behavior, std::ref(additionalVectors));
539 }
540
544 ContainerIterator<Particle_T, false, true> getRegionIterator(const std::array<double, 3> &lowerCorner,
545 const std::array<double, 3> &higherCorner,
546 IteratorBehavior behavior) const {
547 // sanity check: Most of our stuff depends on `inBox`, which does not handle lowerCorner > higherCorner well.
548 for (size_t d = 0; d < 3; ++d) {
549 if (lowerCorner[d] > higherCorner[d]) {
551 "Requesting region Iterator where the upper corner is lower than the lower corner!\n"
552 "Lower corner: {}\n"
553 "Upper corner: {}",
554 lowerCorner, higherCorner);
555 }
556 }
557
558 auto additionalVectors =
560 return std::as_const(_currentContainer)
561 ->getRegionIterator(lowerCorner, higherCorner, behavior, std::ref(additionalVectors));
562 }
563
568 [[nodiscard]] unsigned long getNumberOfParticlesOwned() const { return _numParticlesOwned; }
569
574 [[nodiscard]] unsigned long getNumberOfParticlesHalo() const { return _numParticlesHalo; }
575
599 template <class Functor>
600 [[nodiscard]] std::tuple<std::unique_ptr<TraversalInterface>, bool> isConfigurationApplicable(
601 const Configuration &config, Functor &functor);
602
613 void setParticleBuffers(const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
614 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers);
615
623 std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
624 getParticleBuffers() const;
625
637 [[nodiscard]] double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase = false) const {
638#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
639 const auto numRebuilds = considerOnlyLastNonTuningPhase ? _numRebuildsInNonTuningPhase : _numRebuilds;
640 // The total number of iterations is iteration + 1
641 const auto iterationCount =
642 considerOnlyLastNonTuningPhase ? _iteration - _iterationAfterLastTuningPhase : _iteration + 1;
643 if (numRebuilds == 0) {
644 return static_cast<double>(_neighborListRebuildFrequency);
645 } else {
646 return static_cast<double>(iterationCount) / numRebuilds;
647 }
648#else
649 return static_cast<double>(_neighborListRebuildFrequency);
650#endif
651 }
652
660 double getVelocityMethodRFEstimate(const double skin, const double deltaT) const {
662 // Initialize the maximum velocity to zero
663 double maxVelocity = 0;
664 // Iterate over the owned particles in container to determine maximum velocity
665 AUTOPAS_OPENMP(parallel reduction(max : maxVelocity))
666 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
667 std::array<double, 3> tempVel = iter->getV();
668 double tempVelAbs = sqrt(dot(tempVel, tempVel));
669 maxVelocity = std::max(tempVelAbs, maxVelocity);
670 }
671 // return the rebuild frequency estimate
672 return skin / maxVelocity / deltaT / 2;
673 }
679
685
691
701
702 private:
715 void initSpatialLocks(const std::array<double, 3> &boxLength, double interactionLengthInv) {
716 using namespace autopas::utils::ArrayMath::literals;
719
720 // The maximum number of spatial locks is capped at 1e6.
721 // This limit is chosen more or less arbitrary. It is big enough so that our regular MD simulations
722 // fall well within it and small enough so that no memory issues arise.
723 // There were no rigorous tests for an optimal number of locks.
724 // Without this cap, very large domains (or tiny cutoffs) would generate an insane number of locks,
725 // that could blow up the memory.
726 constexpr size_t maxNumSpacialLocks{1000000};
727
728 // One lock per interaction length or less if this would generate too many.
729 const std::array<size_t, 3> locksPerDim = [&]() {
730 // First naively calculate the number of locks if we simply take the desired cell length.
731 // Ceil because both decisions are possible, and we are generous gods.
732 const std::array<size_t, 3> locksPerDimNaive =
733 static_cast_copy_array<size_t>(ceil(boxLength * interactionLengthInv));
734 const auto totalLocksNaive =
735 std::accumulate(locksPerDimNaive.begin(), locksPerDimNaive.end(), 1ul, std::multiplies<>());
736 // If the number of locks is within the limits everything is fine and we can return.
737 if (totalLocksNaive <= maxNumSpacialLocks) {
738 return locksPerDimNaive;
739 } else {
740 // If the number of locks grows too large, calculate the locks per dimension proportionally to the side lengths.
741 // Calculate side length relative to dimension 0.
742 const std::array<double, 3> boxSideProportions = {
743 1.,
744 boxLength[0] / boxLength[1],
745 boxLength[0] / boxLength[2],
746 };
747 // With this, calculate the number of locks the first dimension should receive.
748 const auto prodProportions =
749 std::accumulate(boxSideProportions.begin(), boxSideProportions.end(), 1., std::multiplies<>());
750 // Needs floor, otherwise we exceed the limit.
751 const auto locksInFirstDimFloat = std::floor(std::cbrt(maxNumSpacialLocks * prodProportions));
752 // From this and the proportions relative to the first dimension, we can calculate the remaining number of locks
753 const std::array<size_t, 3> locksPerDimLimited = {
754 static_cast<size_t>(locksInFirstDimFloat), // omitted div by 1
755 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[1]),
756 static_cast<size_t>(locksInFirstDimFloat / boxSideProportions[2]),
757 };
758 return locksPerDimLimited;
759 }
760 }();
761 _spatialLocks.resize(locksPerDim[0]);
762 for (auto &lockVecVec : _spatialLocks) {
763 lockVecVec.resize(locksPerDim[1]);
764 for (auto &lockVec : lockVecVec) {
765 lockVec.resize(locksPerDim[2]);
766 for (auto &lockPtr : lockVec) {
767 if (not lockPtr) {
768 lockPtr = std::make_unique<std::mutex>();
769 }
770 }
771 }
772 }
773 }
774
782 template <class Functor>
783 std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> selectConfiguration(
784 Functor &functor, const InteractionTypeOption &interactionType);
785
790 void setCurrentContainer(std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer);
791
806 template <class Functor>
807 IterationMeasurements computeInteractions(Functor &functor, TraversalInterface &traversal);
808
819 template <class Functor>
820 void computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA);
821
827 void checkMinimalSize() const;
828
829 const LogicHandlerInfo _logicHandlerInfo;
833 unsigned int _neighborListRebuildFrequency;
834
838 unsigned int _verletClusterSize;
839
844 size_t _aosSortingThresholdFallback;
845
849 size_t _soaSortingThresholdFallback;
850
854 size_t _numRebuilds{0};
855
860 size_t _numRebuildsInNonTuningPhase{0};
861
862 std::shared_ptr<TuningManager> _tuningManager;
863
867 std::unique_ptr<ParticleContainerInterface<Particle_T>> _currentContainer{nullptr};
868
872 ContainerSelectorInfo _currentContainerSelectorInfo;
873
877 RemainderPairwiseInteractionHandler<Particle_T> _remainderPairwiseInteractionHandler;
878
882 RemainderTriwiseInteractionHandler<Particle_T> _remainderTriwiseInteractionHandler;
883
887 std::set<InteractionTypeOption> _interactionTypes{};
888
892 std::atomic<bool> _neighborListsAreValid{false};
893
897 size_t _stepsSinceLastListRebuild{0};
898
903 size_t _iteration{std::numeric_limits<size_t>::max()};
904
908 size_t _iterationAfterLastTuningPhase{0};
909
913 std::atomic<size_t> _numParticlesOwned{0ul};
914
918 std::atomic<size_t> _numParticlesHalo{0ul};
919
923 std::vector<FullParticleCell<Particle_T>> _particleBuffer;
924
928 std::vector<FullParticleCell<Particle_T>> _haloParticleBuffer;
929
935 std::vector<std::vector<std::vector<std::unique_ptr<std::mutex>>>> _spatialLocks;
936
940 IterationLogger _iterationLogger;
941
946 bool _neighborListInvalidDoDynamicRebuild{false};
947
951 void updateRebuildPositions();
952
956 LiveInfoLogger _liveInfoLogger;
957
961 FLOPLogger _flopLogger;
962};
963
964template <typename Particle_T>
966#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
967 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
968 // e.g. neighbor list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
969 // require a rebuild of neighbor lists.
970 AUTOPAS_OPENMP(parallel)
971 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
972 iter->resetRAtRebuild();
973 }
974#endif
975}
976
977template <typename Particle_T>
979 // check boxSize at least cutoff + skin
980 for (unsigned int dim = 0; dim < 3; ++dim) {
981 if (_currentContainer->getBoxMax()[dim] - _currentContainer->getBoxMin()[dim] <
982 _currentContainer->getInteractionLength()) {
984 "Box (boxMin[{}]={} and boxMax[{}]={}) is too small.\nHas to be at least cutoff({}) + skin({}) = {}.", dim,
985 _currentContainer->getBoxMin()[dim], dim, _currentContainer->getBoxMax()[dim], _currentContainer->getCutoff(),
986 _currentContainer->getVerletSkin(), _currentContainer->getCutoff() + _currentContainer->getVerletSkin());
987 }
988 }
989}
990
991template <typename Particle_T>
993 return _neighborListInvalidDoDynamicRebuild;
994}
995
996template <typename Particle_T>
998 if (_stepsSinceLastListRebuild >= _neighborListRebuildFrequency
999#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1000 or getNeighborListsInvalidDoDynamicRebuild()
1001#endif
1002 or _tuningManager->requiresRebuilding(_iteration)) {
1003 _neighborListsAreValid.store(false, std::memory_order_relaxed);
1004 }
1005
1006 return _neighborListsAreValid.load(std::memory_order_relaxed);
1007}
1008
1009template <typename Particle_T>
1011#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1012 const auto skin = getContainer().getVerletSkin();
1013 // (skin/2)^2
1014 const auto halfSkinSquare = skin * skin * 0.25;
1015 // The owned particles in buffer are ignored because they do not rely on the structure of the particle containers,
1016 // e.g. neighbor list, and these are iterated over using the region iterator. Movement of particles in buffer doesn't
1017 // require a rebuild of neighbor lists.
1018 AUTOPAS_OPENMP(parallel reduction(or : _neighborListInvalidDoDynamicRebuild))
1019 for (auto iter = this->begin(IteratorBehavior::owned | IteratorBehavior::containerOnly); iter.isValid(); ++iter) {
1020 const auto distance = iter->calculateDisplacementSinceRebuild();
1021 const double distanceSquare = utils::ArrayMath::dot(distance, distance);
1022
1023 _neighborListInvalidDoDynamicRebuild |= distanceSquare >= halfSkinSquare;
1024 }
1025#endif
1026}
1027
1028template <typename Particle_T>
1030 _neighborListInvalidDoDynamicRebuild = false;
1031}
1032
1033template <typename Particle_T>
1035 const std::vector<FullParticleCell<Particle_T>> &particleBuffers,
1036 const std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
1037 auto exchangeBuffer = [](const auto &newBuffers, auto &oldBuffers, auto &particleCounter) {
1038 // sanity check
1039 if (oldBuffers.size() < newBuffers.size()) {
1041 "The number of new buffers ({}) is larger than number of existing buffers ({})!", newBuffers.size(),
1042 oldBuffers.size());
1043 }
1044
1045 // we will clear the old buffers so subtract the particles from the counters.
1046 const auto numParticlesInOldBuffers =
1047 std::transform_reduce(oldBuffers.begin(), std::next(oldBuffers.begin(), newBuffers.size()), 0, std::plus<>(),
1048 [](const auto &cell) { return cell.size(); });
1049 particleCounter.fetch_sub(numParticlesInOldBuffers, std::memory_order_relaxed);
1050
1051 // clear the old buffers and copy the content of the new buffers over.
1052 size_t numParticlesInNewBuffers = 0;
1053 for (size_t i = 0; i < newBuffers.size(); ++i) {
1054 oldBuffers[i].clear();
1055 for (const auto &p : newBuffers[i]) {
1056 ++numParticlesInNewBuffers;
1057 oldBuffers[i].addParticle(p);
1058 }
1059 }
1060 // update the counters.
1061 particleCounter.fetch_add(numParticlesInNewBuffers, std::memory_order_relaxed);
1062 };
1063
1064 exchangeBuffer(particleBuffers, _particleBuffer, _numParticlesOwned);
1065 exchangeBuffer(haloParticleBuffers, _haloParticleBuffer, _numParticlesHalo);
1066}
1067
1068template <typename Particle_T>
1069std::tuple<const std::vector<FullParticleCell<Particle_T>> &, const std::vector<FullParticleCell<Particle_T>> &>
1071 return {_particleBuffer, _haloParticleBuffer};
1072}
1073
1074template <typename Particle_T>
1075template <class Functor>
1077 // Helper to derive the Functor type at compile time
1078 constexpr auto interactionType = [] {
1080 return InteractionTypeOption::pairwise;
1081 } else if (utils::isTriwiseFunctor<Functor>()) {
1082 return InteractionTypeOption::triwise;
1083 } else {
1085 "LogicHandler::computeInteractions(): Functor is not valid. Only pairwise and triwise functors are "
1086 "supported. "
1087 "Please use a functor derived from "
1088 "PairwiseFunctor or TriwiseFunctor.");
1089 }
1090 }();
1091
1092 auto &autoTuner = *_tuningManager->getAutoTuners()[interactionType];
1093 utils::Timer timerTotal;
1094 utils::Timer timerRebuild;
1095 utils::Timer timerComputeInteractions;
1096 utils::Timer timerComputeRemainder;
1097 long energyTotalRebuild;
1098
1099 const bool energyMeasurementsPossible = autoTuner.resetEnergy();
1100 timerTotal.start();
1101 timerRebuild.start();
1102 functor.initTraversal();
1103
1104 // if lists are not valid -> rebuild;
1105 if (not _neighborListsAreValid.load(std::memory_order_relaxed)) {
1106#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1107 this->updateRebuildPositions();
1108#endif
1109 _currentContainer->rebuildNeighborLists(&traversal);
1110#ifdef AUTOPAS_ENABLE_DYNAMIC_CONTAINERS
1111 this->resetNeighborListsInvalidDoDynamicRebuild();
1112 _numRebuilds++;
1113 if (not autoTuner.inTuningPhase()) {
1114 _numRebuildsInNonTuningPhase++;
1115 }
1116#endif
1117 _neighborListsAreValid.store(true, std::memory_order_relaxed);
1118 }
1119 timerRebuild.stop();
1120 std::tie(std::ignore, std::ignore, std::ignore, energyTotalRebuild) = autoTuner.sampleEnergy();
1121
1122 // Balance buffer vectors
1123 const auto cellToVec = [](auto &cell) -> std::vector<Particle_T> & { return cell._particles; };
1124 utils::ArrayUtils::balanceVectors(_particleBuffer, cellToVec);
1125 utils::ArrayUtils::balanceVectors(_haloParticleBuffer, cellToVec);
1126
1127 // Mainly for NeighborIdentificationFunctor, initialize the neighbors
1128 if constexpr (requires { functor.initializeNeighborList(this->begin(IteratorBehavior::ownedOrHalo)); }) {
1129 functor.initializeNeighborList(this->begin(IteratorBehavior::ownedOrHalo));
1130 }
1131
1132 timerComputeInteractions.start();
1133 _currentContainer->computeInteractions(&traversal);
1134 timerComputeInteractions.stop();
1135
1136 timerComputeRemainder.start();
1137 const bool newton3 = autoTuner.getCurrentConfig().newton3;
1138 const auto dataLayout = autoTuner.getCurrentConfig().dataLayout;
1139 computeRemainderInteractions(functor, newton3, dataLayout);
1140 timerComputeRemainder.stop();
1141
1142 functor.endTraversal(newton3);
1143
1144 const auto [energyWatts, energyJoules, energyDeltaT, energyTotal] = autoTuner.sampleEnergy();
1145 timerTotal.stop();
1146
1147 constexpr auto nanD = std::numeric_limits<double>::quiet_NaN();
1148 constexpr auto nanL = std::numeric_limits<long>::quiet_NaN();
1149 return {timerComputeInteractions.getTotalTime(),
1150 timerComputeRemainder.getTotalTime(),
1151 timerRebuild.getTotalTime(),
1152 timerTotal.getTotalTime(),
1153 energyMeasurementsPossible,
1154 energyMeasurementsPossible ? energyWatts : nanD,
1155 energyMeasurementsPossible ? energyJoules : nanD,
1156 energyMeasurementsPossible ? energyDeltaT : nanD,
1157 energyMeasurementsPossible ? energyTotalRebuild : nanL,
1158 energyMeasurementsPossible ? energyTotal - energyTotalRebuild
1159 : nanL, // ComputeInteractions + Remainder Traversal energy consumption
1160 energyMeasurementsPossible ? energyTotal : nanL};
1161}
1162
1163template <typename Particle_T>
1164template <class Functor>
1165void LogicHandler<Particle_T>::computeRemainderInteractions(Functor &functor, bool newton3, bool useSoA) {
1166 withStaticContainerType(*_currentContainer, [&](auto &actualContainerType) {
1167 if constexpr (utils::isPairwiseFunctor<Functor>()) {
1168 if (newton3) {
1169 _remainderPairwiseInteractionHandler.template computeRemainderInteractions<true>(
1170 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer, useSoA);
1171 } else {
1172 _remainderPairwiseInteractionHandler.template computeRemainderInteractions<false>(
1173 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer, useSoA);
1174 }
1175 } else if constexpr (utils::isTriwiseFunctor<Functor>()) {
1176 if (newton3) {
1177 _remainderTriwiseInteractionHandler.template computeRemainderInteractions<true>(
1178 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1179 } else {
1180 _remainderTriwiseInteractionHandler.template computeRemainderInteractions<false>(
1181 &functor, actualContainerType, _particleBuffer, _haloParticleBuffer);
1182 }
1183 }
1184 });
1185}
1186
1187template <typename Particle_T>
1188template <class Functor>
1189std::tuple<Configuration, std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::selectConfiguration(
1190 Functor &functor, const InteractionTypeOption &interactionType) {
1191 // Todo: Make LiveInfo persistent between multiple functor calls in the same timestep (e.g. 2B + 3B)
1192 // https://github.com/AutoPas/AutoPas/issues/916
1193 LiveInfo info{};
1194#ifdef AUTOPAS_LOG_LIVEINFO
1195 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1196 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1197 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1198 _liveInfoLogger.logLiveInfo(info, _iteration);
1199#endif
1200
1201 // if this iteration is not relevant, take the same algorithm config as before.
1202 if (not functor.isRelevantForTuning()) {
1203 auto configuration = _tuningManager->getCurrentConfig(interactionType);
1204 auto [traversalPtr, _] = isConfigurationApplicable(configuration, functor);
1205
1206 if (not traversalPtr) {
1207 // TODO: Can we handle this case gracefully?
1209 "LogicHandler: Functor {} is not relevant for tuning but the given configuration is not applicable!",
1210 functor.getName());
1211 }
1212 functor.setVecPattern(configuration.vecPattern);
1213 return {configuration, std::move(traversalPtr), false};
1214 }
1215
1216 if (_tuningManager->needsLiveInfo(_iteration)) {
1217 // If live info has not been gathered yet, gather it now and send it to the tuner.
1218 if (info.get().empty()) {
1219 auto particleIter = this->begin(IteratorBehavior::ownedOrHalo);
1220 info.gather(particleIter, _neighborListRebuildFrequency, getNumberOfParticlesOwned(), _logicHandlerInfo.boxMin,
1221 _logicHandlerInfo.boxMax, _logicHandlerInfo.cutoff, _logicHandlerInfo.verletSkin);
1222 }
1223 }
1224
1225 size_t numRejectedConfigs = 0;
1226 utils::TraceTimer selectConfigurationTimer;
1227 selectConfigurationTimer.start();
1228
1229 auto stillTuning = _tuningManager->tune(_iteration, info);
1230
1231 auto configuration = _tuningManager->getCurrentConfig(interactionType);
1232
1233 // loop as long as we don't get a valid configuration
1234 do {
1235 // applicability check also sets the container
1236 auto [traversalPtr, rejectIndefinitely] = isConfigurationApplicable(configuration, functor);
1237 if (traversalPtr) {
1238 functor.setVecPattern(configuration.vecPattern);
1239 selectConfigurationTimer.stop();
1240 AutoPasLog(TRACE, "Select Configuration took {} ms. A total of {} configurations were rejected.",
1241 selectConfigurationTimer.getTotalTime(), numRejectedConfigs);
1242 return {configuration, std::move(traversalPtr), stillTuning};
1243 }
1244 numRejectedConfigs++;
1245 // if no config is left after rejecting this one, an exception is thrown here.
1246 configuration = _tuningManager->rejectConfiguration(configuration, rejectIndefinitely, interactionType);
1247 } while (true);
1248}
1249
1250template <typename Particle_T>
1252 std::unique_ptr<ParticleContainerInterface<Particle_T>> newContainer) {
1253 // copy particles so they do not get lost when the container is switched
1254 if (_currentContainer != nullptr and newContainer != nullptr) {
1255 // with these assumptions slightly more space is reserved as numParticlesTotal already includes halos
1256 const auto numParticlesTotal = _currentContainer->size();
1257 const auto numParticlesHalo = utils::NumParticlesEstimator::estimateNumHalosUniform(
1258 numParticlesTotal, _currentContainer->getBoxMin(), _currentContainer->getBoxMax(),
1259 _currentContainer->getInteractionLength());
1260
1261 newContainer->reserve(numParticlesTotal, numParticlesHalo);
1262 for (auto particleIter = _currentContainer->begin(IteratorBehavior::ownedOrHalo); particleIter.isValid();
1263 ++particleIter) {
1264 // add a particle as inner if it is owned
1265 if (particleIter->isOwned()) {
1266 newContainer->addParticle(*particleIter);
1267 } else {
1268 newContainer->addHaloParticle(*particleIter);
1269 }
1270 }
1271 }
1272
1273 _currentContainer = std::move(newContainer);
1274}
1275
1276template <typename Particle_T>
1277template <class Functor>
1279 const InteractionTypeOption &interactionType) {
1280 if (not _interactionTypes.contains(interactionType)) {
1282 "LogicHandler::computeInteractionsPipeline(): AutPas was not initialized for the Functor's interactions type: "
1283 "{}.",
1284 interactionType);
1285 }
1287 utils::Timer tuningTimer;
1288 tuningTimer.start();
1289 const auto [configuration, traversalPtr, stillTuning] = selectConfiguration(*functor, interactionType);
1290 tuningTimer.stop();
1291 _tuningManager->logTuningResult(tuningTimer.getTotalTime(), _iteration, interactionType);
1292
1293 // Run SortingThresholdBenchmark lazily on the first iteration for which an owned particle is available, and
1294 // apply its result to the active container. Retrying on the next iteration if the edge case that no Particle is
1295 // available occurs. SortingThresholdBenchmark is pairwise-only. Within pairwise functors, AoS benchmarking runs for
1296 // any of them. SoA benchmarking additionally requires Functor::supportsSoASorting.
1297 auto &sortingThresholdBenchmark = _tuningManager->getAutoTuners()[interactionType]->getSortingThresholdBenchmark();
1298
1299 if (_logicHandlerInfo.useSortingThresholdBenchmark) {
1300 if constexpr (utils::isPairwiseFunctor<Functor>()) {
1301 const bool needsAoSRun = not sortingThresholdBenchmark.hasRunAoS();
1302 const bool needsSoARun = Functor::supportsSoASorting and not sortingThresholdBenchmark.hasRunSoA();
1303 if (needsAoSRun or needsSoARun) {
1304 // Sample an existing particle from the container to use as the template for the particles generated by the
1305 // benchmark and to use it to copy construct new Particles.
1306 // Because AutoPas doesn't make any assumptions about the Particle apart from it being copy-constructible.
1307 auto particleIter = begin(IteratorBehavior::owned);
1308 if (particleIter.isValid()) {
1309 const Particle_T defaultParticle = *particleIter;
1310 if (needsAoSRun) {
1311 sortingThresholdBenchmark.runAoSBenchmark<Functor, Particle_T>(*functor, defaultParticle);
1312 }
1313 if constexpr (Functor::supportsSoASorting) {
1314 if (needsSoARun) {
1315 sortingThresholdBenchmark.runSoABenchmark<Functor, Particle_T>(*functor, defaultParticle);
1316 }
1317 }
1318 }
1319 }
1320 }
1321 }
1322 // Each interaction type has its own AutoTuner and thus its own SortingThresholdBenchmark. Before any benchmark run,
1323 // getAoSThreshold()/getSoAThreshold() return the uniform default it was seeded with, so this is safe to apply
1324 // unconditionally, regardless of useSortingThresholdBenchmark, interaction type, or functor kind.
1325 _currentContainer->setAoSSortingThresholds(sortingThresholdBenchmark.getAoSThreshold());
1326 _currentContainer->setSoASortingThresholds(sortingThresholdBenchmark.getSoAThreshold());
1327
1328 // Retrieve rebuild info before calling `computeInteractions()` to get the correct value.
1329 const auto rebuildIteration = not _neighborListsAreValid.load(std::memory_order_relaxed);
1330
1332 AutoPasLog(DEBUG, "Iterating with configuration: {} tuning: {}", configuration.toString(), stillTuning);
1333 const IterationMeasurements measurements = computeInteractions(*functor, *traversalPtr);
1334
1336 auto bufferSizeListing = [](const auto &buffers) -> std::string {
1337 std::stringstream ss;
1338 size_t sum = 0;
1339 for (const auto &buffer : buffers) {
1340 ss << buffer.size() << ", ";
1341 sum += buffer.size();
1342 }
1343 ss << " Total: " << sum;
1344 return ss.str();
1345 };
1346 AutoPasLog(TRACE, "particleBuffer size : {}", bufferSizeListing(_particleBuffer));
1347 AutoPasLog(TRACE, "haloParticleBuffer size : {}", bufferSizeListing(_haloParticleBuffer));
1348 AutoPasLog(DEBUG, "Type of interaction : {}", interactionType.to_string());
1349 AutoPasLog(DEBUG, "Container::computeInteractions took {} ns", measurements.timeComputeInteractions);
1350 AutoPasLog(DEBUG, "RemainderTraversal took {} ns", measurements.timeRemainderTraversal);
1351 AutoPasLog(DEBUG, "RebuildNeighborLists took {} ns", measurements.timeRebuild);
1352 AutoPasLog(DEBUG, "AutoPas::computeInteractions took {} ns", measurements.timeTotal);
1353 if (measurements.energyMeasurementsPossible) {
1354 AutoPasLog(DEBUG, "Energy Consumption: Watts: {} Joules: {} Seconds: {}", measurements.energyWatts,
1355 measurements.energyJoules, measurements.energyDeltaT);
1356 }
1357 _iterationLogger.logIteration(configuration, _iteration, functor->getName(), stillTuning, tuningTimer.getTotalTime(),
1358 measurements);
1359
1360 _flopLogger.logIteration(_iteration, functor->getNumFLOPs(), functor->getHitRate());
1361
1363 // if this was a major iteration add measurements
1364 if (functor->isRelevantForTuning()) {
1365 if (stillTuning) {
1366 // choose the metric of interest
1367 const auto measurement = [&]() {
1368 switch (_tuningManager->getTuningMetric(interactionType)) {
1370 return std::make_pair(measurements.timeRebuild,
1371 measurements.timeComputeInteractions + measurements.timeRemainderTraversal);
1373 return std::make_pair(measurements.energyTotalRebuild, measurements.energyTotalNonRebuild);
1374 default:
1375 utils::ExceptionHandler::exception("LogicHandler::computeInteractionsPipeline(): Unknown tuning metric.");
1376 return std::make_pair(0l, 0l);
1377 }
1378 }();
1379 _tuningManager->addMeasurement(measurement.first, measurement.second, rebuildIteration, _iteration,
1380 interactionType);
1381 }
1382 } else {
1383 AutoPasLog(TRACE, "Skipping adding of sample because functor is not marked relevant.");
1384 }
1385 return stillTuning;
1386}
1387
1388template <typename Particle_T>
1389template <class Functor>
1390std::tuple<std::unique_ptr<TraversalInterface>, bool> LogicHandler<Particle_T>::isConfigurationApplicable(
1391 const Configuration &config, Functor &functor) {
1392 // Check if the configuration is compatible, independent of domain or functor
1393 if (not config.hasCompatibleValues()) {
1394 AutoPasLog(
1395 WARN,
1396 "A configuration was rejected by LogicHandler::isConfigurationApplicable, as it was incompatible"
1397 " independently of domain or functor. This should not occur and implies illegal configurations are being added"
1398 " to the configuration queue during simulation (potentially by a tuning strategy).");
1399 AutoPasLog(WARN, "Configuration rejected: {}", config.toString());
1400 // Such illegal configurations should be filtered out in the generation of the search space.
1401 return {nullptr, /*rejectIndefinitely*/ true};
1402 }
1403
1404 // Check if the functor supports the required Newton 3 mode
1405 if ((config.newton3 == Newton3Option::enabled and not functor.allowsNewton3()) or
1406 (config.newton3 == Newton3Option::disabled and not functor.allowsNonNewton3())) {
1407 AutoPasLog(DEBUG, "Configuration rejected: The functor doesn't support Newton 3 {}!", config.newton3);
1408 return {nullptr, /*rejectIndefinitely*/ true};
1409 }
1410
1411 // Check if the VectorizationPattern is supported by the functor. Only SoA configurations use a vectorization pattern;
1412 // AoS configurations always use the not-applicable (N/A) pattern, which every functor supports.
1413 if (config.dataLayout == DataLayoutOption::soa and not functor.isVecPatternAllowed(config.vecPattern)) {
1414 AutoPasLog(DEBUG, "Configuration rejected: The functor doesn't support the SoA Vectorization Pattern {}!",
1415 config.vecPattern);
1416 return {nullptr, /*rejectIndefinitely*/ true};
1417 }
1418
1419 std::unique_ptr<ParticleContainerInterface<Particle_T>> containerPtr{nullptr};
1420 auto containerInfo = ContainerSelectorInfo(
1421 _currentContainer->getBoxMin(), _currentContainer->getBoxMax(), _currentContainer->getCutoff(),
1422 config.cellSizeFactor, _currentContainer->getVerletSkin(), _verletClusterSize, _aosSortingThresholdFallback,
1423 _soaSortingThresholdFallback, config.loadEstimator);
1424
1425 // If we have no current container or needs to be updated to the new config.container, we need to generate a new
1426 // container.
1427 const bool generateNewContainer = _currentContainer == nullptr or
1428 _currentContainer->getContainerType() != config.container or
1429 containerInfo != _currentContainerSelectorInfo;
1430
1431 if (generateNewContainer) {
1432 // For now, set the local containerPtr to the new container. We do not copy the particles over and set the member
1433 // _currentContainer until after we know that the traversal is applicable to the domain.
1434 containerPtr = ContainerSelector<Particle_T>::generateContainer(config.container, containerInfo);
1435 }
1436
1437 const auto traversalInfo =
1438 generateNewContainer ? containerPtr->getTraversalSelectorInfo() : _currentContainer->getTraversalSelectorInfo();
1439
1440 // Generates a traversal if applicable to domain, otherwise returns a nullptr
1441 auto traversalPtr =
1442 TraversalSelector::generateTraversalFromConfig<Particle_T, Functor>(config, functor, traversalInfo);
1443
1444 // If the traversal is applicable to the domain, and the configuration requires generating a new container,
1445 // update the member _currentContainer with setCurrentContainer, copying the particle data over, and update
1446 // _currentContainerSelectorInfo.
1447 if (traversalPtr and generateNewContainer) {
1448 _currentContainerSelectorInfo = containerInfo;
1449 setCurrentContainer(std::move(containerPtr));
1450 }
1451
1452 return {std::move(traversalPtr), /*rejectIndefinitely*/ false};
1453}
1454
1455} // namespace autopas
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:74
#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:30
std::string toString() const
Returns string representation in JSON style of the configuration object.
Definition: Configuration.cpp:12
LoadEstimatorOption loadEstimator
Load Estimator option.
Definition: Configuration.h:159
double cellSizeFactor
CellSizeFactor.
Definition: Configuration.h:171
DataLayoutOption dataLayout
Data Layout option.
Definition: Configuration.h:163
ContainerOption container
Container option.
Definition: Configuration.h:147
Newton3Option newton3
Newton 3 option.
Definition: Configuration.h:167
VectorizationPatternOption vecPattern
Vectorization Pattern option.
Definition: Configuration.h:155
bool hasCompatibleValues() const
Checks if any of the configuration values are incompatible with each other.
Definition: Configuration.cpp:53
Public iterator class that iterates over a particle container and additional vectors (which are typic...
Definition: ContainerIterator.h:95
Info to generate a container.
Definition: ContainerSelectorInfo.h:17
std::array< double, 3 > boxMin
Lower corner of the container.
Definition: ContainerSelectorInfo.h:100
std::array< double, 3 > boxMax
Upper corner of the container.
Definition: ContainerSelectorInfo.h:105
static std::unique_ptr< ParticleContainerInterface< Particle_T > > generateContainer(ContainerOption containerChoice, const ContainerSelectorInfo &containerInfo)
Container factory method.
Definition: ContainerSelector.h:44
Helper to log FLOP count and HitRate for AutoPas::iteratePairwise() calls with the functors in the mo...
Definition: FLOPLogger.h:30
This class handles the storage of particles in their full form.
Definition: FullParticleCell.h:26
Functor base class.
Definition: Functor.h:41
virtual size_t getNumFLOPs() const
Get the number of FLOPs.
Definition: Functor.h:208
virtual bool isVecPatternAllowed(const VectorizationPatternOption::Value vecPattern)=0
Specifies whether the functor is capable of using the specified Vectorization Pattern in the SoA func...
virtual bool allowsNewton3()=0
Specifies whether the functor is capable of Newton3-like functors.
virtual void setVecPattern(const VectorizationPatternOption::Value vecPattern)
Setter for the vectorization pattern to be used.
Definition: Functor.h:199
virtual void initTraversal()
This function is called at the start of each traversal.
Definition: Functor.h:64
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:71
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:218
static constexpr bool supportsSoASorting
Whether this functor supports the SortedSoAView optimization path (SoAFunctorPairSorted).
Definition: Functor.h:112
Helper to log performance data of AutoPas::computeInteractions() to a csv file for easier analysis.
Definition: IterationLogger.h:24
Helper to log the collected LiveInfo data during tuning to a csv file for easier analysis.
Definition: LiveInfoLogger.h:23
This class is able to gather and store important information for a tuning phase from a container and ...
Definition: LiveInfo.h:33
Class that wraps all arguments for the logic handler to provide a more stable API.
Definition: LogicHandlerInfo.h:15
double verletSkin
Length added to the cutoff for the Verlet lists' skin.
Definition: LogicHandlerInfo.h:32
std::array< double, 3 > boxMax
Upper corner of the container without halo.
Definition: LogicHandlerInfo.h:24
double deltaT
Time step used in the simulation.
Definition: LogicHandlerInfo.h:47
double cutoff
Cutoff radius to be used in this simulation.
Definition: LogicHandlerInfo.h:28
std::array< double, 3 > boxMin
Lower corner of the container without halo.
Definition: LogicHandlerInfo.h:20
The LogicHandler takes care of the containers s.t.
Definition: LogicHandler.h:50
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:1034
void reserve(size_t numParticles, size_t numHaloParticles)
Reserves space in the particle buffers and the container.
Definition: LogicHandler.h:323
std::tuple< std::unique_ptr< TraversalInterface >, bool > isConfigurationApplicable(const Configuration &config, Functor &functor)
Checks if the given configuration can be used with the given functor and the current state of the sim...
Definition: LogicHandler.h:1390
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:505
std::vector< Particle_T > updateContainer()
Updates the container.
Definition: LogicHandler.h:169
unsigned long getNumberOfParticlesOwned() const
Get the number of owned particles.
Definition: LogicHandler.h:568
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:1278
std::tuple< const std::vector< FullParticleCell< Particle_T > > &, const std::vector< FullParticleCell< Particle_T > > & > getParticleBuffers() const
Getter for the particle buffers.
Definition: LogicHandler.h:1070
void addParticle(const Particle_T &p)
Adds a particle to the container.
Definition: LogicHandler.h:343
void decreaseParticleCounter(Particle_T &particle)
Decrease the correct internal particle counters.
Definition: LogicHandler.h:438
std::vector< Particle_T > collectLeavingParticlesFromBuffer(bool insertOwnedParticlesToContainer)
Collects leaving particles from buffer and potentially inserts owned particles to the container.
Definition: LogicHandler.h:116
bool neighborListsAreValid()
Checks if in the next iteration the neighbor lists have to be rebuilt.
Definition: LogicHandler.h:997
unsigned long getNumberOfParticlesHalo() const
Get the number of halo particles.
Definition: LogicHandler.h:574
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:416
double getVelocityMethodRFEstimate(const double skin, const double deltaT) const
Estimates the rebuild frequency based on the current maximum velocity in the container Using the form...
Definition: LogicHandler.h:660
void checkNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:1010
Iterator::ParticleVecType gatherAdditionalVectors(IteratorBehavior behavior)
Create the additional vectors vector for a given iterator behavior.
Definition: LogicHandler.h:476
ParticleContainerInterface< Particle_T > & getContainer()
Returns a non-const reference to the currently selected particle container.
Definition: LogicHandler.h:109
void addHaloParticle(const Particle_T &haloParticle)
Adds a particle to the container that lies in the halo region of the container.
Definition: LogicHandler.h:370
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:544
void deleteAllParticles()
Deletes all particles.
Definition: LogicHandler.h:400
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:513
bool getNeighborListsInvalidDoDynamicRebuild()
getter function for _neighborListInvalidDoDynamicRebuild
Definition: LogicHandler.h:992
LogicHandler(const std::shared_ptr< TuningManager > &tunerManager, const LogicHandlerInfo &logicHandlerInfo, unsigned int rebuildFrequency, const std::string &outputSuffix, size_t aosSortingThresholdFallback, size_t soaSortingThresholdFallback)
Constructor of the LogicHandler.
Definition: LogicHandler.h:64
void resetNeighborListsInvalidDoDynamicRebuild()
Checks if any particle has moved more than skin/2.
Definition: LogicHandler.h:1029
double getMeanRebuildFrequency(bool considerOnlyLastNonTuningPhase=false) const
Getter for the mean rebuild frequency.
Definition: LogicHandler.h:637
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:230
void reserve(size_t numParticles)
Estimates the number of halo particles via autopas::utils::NumParticlesEstimator::estimateNumHalosUni...
Definition: LogicHandler.h:310
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:523
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:40
Handles pairwise interactions involving particle buffers (particles not yet inserted into the main co...
Definition: RemainderPairwiseInteractionHandler.h:31
Handles triwise interactions involving particle buffers (particles not yet inserted into the main con...
Definition: RemainderTriwiseInteractionHandler.h:30
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
@ energy
Optimize for least energy usage.
Definition: TuningMetricOption.h:31
@ time
Optimize for shortest simulation time.
Definition: TuningMetricOption.h:27
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:64
Timer class to stop times.
Definition: Timer.h:27
void start()
start the timer.
Definition: Timer.cpp:17
long getTotalTime() const
Get total accumulated time.
Definition: Timer.h:60
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: Timer.cpp:25
A wrapper around autopas::utils::Timer that only compiles implementation logic if AUTOPAS_ACTIVE_LEVE...
Definition: TraceTimer.h:20
long getTotalTime() const
Get total accumulated time.
Definition: TraceTimer.h:63
void start()
start the timer.
Definition: TraceTimer.h:25
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: TraceTimer.h:34
void markParticleAsDeleted(Particle_T &p)
Marks a particle as deleted.
Definition: markParticleAsDeleted.h:23
constexpr T dot(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Generates the dot product of two arrays.
Definition: ArrayMath.h:233
constexpr std::array< T, SIZE > ceil(const std::array< T, SIZE > &a)
For each element in a, computes the smallest integer value not less than the element.
Definition: ArrayMath.h:316
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:151
constexpr std::array< output_t, SIZE > static_cast_copy_array(const std::array< input_t, SIZE > &a)
Creates a new array by performing an element-wise static_cast<>.
Definition: ArrayUtils.h:33
void to_string(std::ostream &os, const Container &container, const std::string &delimiter, const std::array< std::string, 2 > &surround, Fun elemToString)
Generates a string representation of a container which fulfills the Container requirement (provide cb...
Definition: ArrayUtils.h:54
size_t estimateNumHalosUniform(size_t numParticles, const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax, double haloWidth)
Given a number of particles and the dimensions of a box, estimate the number of halo particles.
Definition: NumParticlesEstimator.cpp:9
decltype(isTriwiseFunctorImpl(std::declval< FunctorT >())) isTriwiseFunctor
Check whether a Functor Type is inheriting from TriwiseFunctor.
Definition: checkFunctorType.h:56
bool notInBox(const std::array< T, 3 > &position, const std::array< T, 3 > &low, const std::array< T, 3 > &high)
Checks if position is not inside of a box defined by low and high.
Definition: inBox.h:50
decltype(isPairwiseFunctorImpl(std::declval< FunctorT >())) isPairwiseFunctor
Check whether a Functor Type is inheriting from PairwiseFunctor.
Definition: checkFunctorType.h:49
bool inBox(const std::array< T, 3 > &position, const std::array< T, 3 > &low, const std::array< T, 3 > &high)
Checks if position is inside of a box defined by low and high.
Definition: inBox.h:26
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:34
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
@ halo
Halo state, a particle with this state is an actual particle, but not owned by the current AutoPas ob...
@ owned
Owned state, a particle with this state is an actual particle and owned by the current AutoPas object...
decltype(auto) withStaticContainerType(ParticleContainerInterface< Particle_T > &container, FunctionType &&function)
Will execute the passed function body with the static container type of container.
Definition: StaticContainerSelector.h:35
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132
Struct to collect all sorts of measurements taken during a computeInteractions iteration.
Definition: IterationMeasurements.h:13
double energyWatts
Average energy consumed per time in Watts.
Definition: IterationMeasurements.h:42
double energyDeltaT
Time in seconds during which energy was consumed.
Definition: IterationMeasurements.h:52
long timeRebuild
Time it takes for rebuilding neighbor lists.
Definition: IterationMeasurements.h:27
long timeTotal
Time it takes for the complete iteratePairwise pipeline.
Definition: IterationMeasurements.h:32
long energyTotalRebuild
Total energy consumed during rebuilding.
Definition: IterationMeasurements.h:57
long timeRemainderTraversal
Time it takes for the Remainder Traversal.
Definition: IterationMeasurements.h:22
long energyTotalNonRebuild
Total energy consumed during compute interactions and remainder traversal.
Definition: IterationMeasurements.h:62
long timeComputeInteractions
Time it takes for the LogicHandler's computeInteractions() function.
Definition: IterationMeasurements.h:17
bool energyMeasurementsPossible
Bool whether energy measurements are currently possible.
Definition: IterationMeasurements.h:37
double energyJoules
Total energy consumed in Joules.
Definition: IterationMeasurements.h:47