AutoPas  3.0.0
Loading...
Searching...
No Matches
ContainerIterator.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <array>
11#include <functional>
12#include <limits>
13#include <optional>
14#include <tuple>
15#include <type_traits>
16#include <vector>
17
22#include "autopas/utils/inBox.h"
23
24namespace autopas {
25
26namespace internal {
36template <class ParticleIterator>
37void deleteParticle(ParticleIterator &iterator) {
38 iterator.deleteCurrentParticle();
39}
40} // namespace internal
41
45namespace containerIteratorUtils {
58template <bool regionIter, class Particle_T, class Arr>
59[[nodiscard]] bool particleFulfillsIteratorRequirements(const Particle_T &p, IteratorBehavior behavior,
60 const Arr &regionMin, const Arr &regionMax) {
61 // If this is a region iterator check box condition first, otherwise race conditions can occur
62 // if multiple region iterator overlap and ownership state is touched.
63 // (e.g. during collection of leaving particles)
64 if constexpr (regionIter) {
65 if (not utils::inBox(p.getR(), regionMin, regionMax)) {
66 return false;
67 }
68 }
69
70 // Check ownership
71 // Dummy is not in sync with iterator behavior because it needs to be 0.
72 // `a & b == b` idiom is to check a == b disregarding any bits beyond b. This is needed due to e.g. forceSequential.
73 if (((behavior & IteratorBehavior::ownedOrHaloOrDummy) == IteratorBehavior::ownedOrHaloOrDummy) or
74 (behavior & IteratorBehavior::dummy and p.isDummy())) {
75 return true;
76 } else {
77 // relies on both enums having the same encoding. This should be guaranteed statically in IteratorBehaviorTest!
78 return static_cast<unsigned int>(p.getOwnershipState()) & static_cast<unsigned int>(behavior);
79 }
80}
81} // namespace containerIteratorUtils
82
94template <class Particle_T, bool modifiable, bool regionIter>
96 template <class T>
97 friend void internal::deleteParticle(T &);
98
99 public:
103 using ParticleType = std::conditional_t<modifiable, Particle_T, const Particle_T>;
107 using ParticleVecType = std::conditional_t<modifiable, std::vector<std::vector<Particle_T> *>,
108 std::vector<std::vector<Particle_T> const *>>;
112 using ContainerType = std::conditional_t<modifiable, ParticleContainerInterface<Particle_T>,
114
118 ContainerIterator() : _currentParticle(nullptr){};
119
129 ContainerIterator(ContainerType &container, IteratorBehavior behavior,
130 utils::optRef<ParticleVecType> additionalVectorsToIterate, const std::array<double, 3> &regionMin,
131 const std::array<double, 3> &regionMax)
132 : ContainerIterator(nullptr, container, behavior, additionalVectorsToIterate, regionMin, regionMax) {
133 // sanity check
134 static_assert(regionIter == true,
135 "Constructor for Region iterator called but template argument regionIter is false");
136 }
137
145 ContainerIterator(ContainerType &container, IteratorBehavior behavior,
146 utils::optRef<ParticleVecType> additionalVectorsToIterate)
147 : ContainerIterator(nullptr, container, behavior, additionalVectorsToIterate, {}, {}) {
148 static_assert(regionIter == false,
149 "Constructor for non-Region iterator called but template argument regionIter is true");
150 }
151
157 : _container(other._container),
158 _currentParticleIndex(other._currentParticleIndex),
159 _currentVectorIndex(other._currentVectorIndex),
160 _currentParticle(other._currentParticle),
161 _additionalVectors(other._additionalVectors),
162 _behavior(other._behavior),
163 _iteratingAdditionalVectors(other._iteratingAdditionalVectors),
164 _vectorIndexOffset(other._vectorIndexOffset),
165 _regionMin(other._regionMin),
166 _regionMax(other._regionMax) {}
167
175 if (this != other) {
176 _container = other._container;
177 _currentParticleIndex = other._currentParticleIndex;
178 _currentVectorIndex = other._currentVectorIndex;
179 _currentParticle = other._currentParticle;
180 _additionalVectors = other._additionalVectors;
181 _behavior = other._behavior;
182 _iteratingAdditionalVectors = other._iteratingAdditionalVectors;
183 _vectorIndexOffset = other._vectorIndexOffset;
184 if constexpr (regionIter) {
185 _regionMin = other._regionMin;
186 _regionMax = other._regionMax;
187 }
188 }
189 return *this;
190 }
191
197 : _container(other._container),
198 _currentParticleIndex(other._currentParticleIndex),
199 _currentVectorIndex(other._currentVectorIndex),
200 _currentParticle(other._currentParticle),
201 _additionalVectors(std::move(other._additionalVectors)),
202 _behavior(other._behavior),
203 _iteratingAdditionalVectors(other._iteratingAdditionalVectors),
204 _vectorIndexOffset(other._vectorIndexOffset),
205 _regionMin(std::move(other._regionMin)),
206 _regionMax(std::move(other._regionMax)) {}
207
215 if (this != &other) {
216 _container = other._container;
217 _currentParticleIndex = other._currentParticleIndex;
218 _currentVectorIndex = other._currentVectorIndex;
219 _currentParticle = other._currentParticle;
220 _additionalVectors = std::move(other._additionalVectors);
221 _behavior = other._behavior;
222 _iteratingAdditionalVectors = other._iteratingAdditionalVectors;
223 _vectorIndexOffset = other._vectorIndexOffset;
224 if constexpr (regionIter) {
225 _regionMin = std::move(other._regionMin);
226 _regionMax = std::move(other._regionMax);
227 }
228 }
229 return *this;
230 }
231
232 private:
244 ContainerIterator(void * /*dummy*/, ContainerType &container, IteratorBehavior behavior,
245 utils::optRef<ParticleVecType> additionalVectorsToIterate, const std::array<double, 3> &regionMin,
246 const std::array<double, 3> &regionMax)
247 : _container(&container),
248 _behavior(behavior),
249 _vectorIndexOffset((behavior & IteratorBehavior::forceSequential) ? 1 : autopas_get_num_threads()) {
250 if (additionalVectorsToIterate.has_value()) {
251 // store pointers to all additional vectors
252 _additionalVectors.insert(_additionalVectors.end(), additionalVectorsToIterate->get().begin(),
253 additionalVectorsToIterate->get().end());
254 }
255
256 if constexpr (regionIter) {
257 // clamp region to the container, either with or without halo
258 const auto boxMaxWithHalo = utils::ArrayMath::addScalar(container.getBoxMax(), container.getInteractionLength());
259 const auto boxMinWithHalo = utils::ArrayMath::subScalar(container.getBoxMin(), container.getInteractionLength());
260 _regionMax = utils::ArrayMath::min(regionMax, boxMaxWithHalo);
261 _regionMin = utils::ArrayMath::max(regionMin, boxMinWithHalo);
262 }
263 // fetches the next (=first) valid particle or sets _currentParticle = nullptr which marks the iterator as invalid.
264 fetchParticleAtCurrentIndex();
265 }
266
267 public:
277 // bump the index. If it is invalid now, the container will give us the proper one.
278 ++_currentParticleIndex;
279 fetchParticleAtCurrentIndex();
280 return *this;
281 }
282
287 inline ParticleType &operator*() const { return *_currentParticle; }
288
293 inline ParticleType *operator->() const { return _currentParticle; }
294
299 [[nodiscard]] bool isValid() const { return _currentParticle != nullptr; }
300
310 bool operator==(const bool input) const { return isValid() == input; }
311
317 bool operator!=(const bool input) const { return not(*this == input); }
318
319 private:
324 void fetchParticleAtCurrentIndex() {
325 if (not _iteratingAdditionalVectors) {
326 // getParticle either gives us a particle with the desired properties or a nullptr
327 if constexpr (regionIter) {
328 std::tie(_currentParticle, _currentVectorIndex, _currentParticleIndex) =
329 _container->getParticle(_currentVectorIndex, _currentParticleIndex, _behavior, _regionMin, _regionMax);
330 } else {
331 std::tie(_currentParticle, _currentVectorIndex, _currentParticleIndex) =
332 _container->getParticle(_currentVectorIndex, _currentParticleIndex, _behavior);
333 }
334 // if getParticle told us that the container doesn't have a particle in the first vector for our thread...
335 if (_currentParticle == nullptr and not _additionalVectors.empty()) {
336 // determine which additional vector this thread should start to iterate
337 _currentVectorIndex = (_behavior & IteratorBehavior::forceSequential) ? 0 : autopas_get_thread_num();
338 _currentParticleIndex = 0;
339 _iteratingAdditionalVectors = true;
340 } else {
341 // Case: nothing left in the container and no additional vectors to iterate
342 return;
343 }
344 }
345 // no "else" here because the first case might trigger the second if we reached the end of the container
346 if (_iteratingAdditionalVectors) {
347 // check all vectors ...
348 for (; _currentVectorIndex < _additionalVectors.size(); _currentVectorIndex += _vectorIndexOffset) {
349 // ... and all particles within the vector ...
350 for (; _currentParticleIndex < _additionalVectors[_currentVectorIndex]->size(); ++_currentParticleIndex) {
351 _currentParticle = &(_additionalVectors[_currentVectorIndex]->operator[](_currentParticleIndex));
352 // ... until we find a particle that satisfies our requirements.
353 if (containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(*_currentParticle, _behavior,
354 _regionMin, _regionMax)) {
355 return;
356 }
357 }
358 _currentParticleIndex = 0;
359 }
360 }
361 // if we reach this point there is no satisfying particle left neither in the container nor the additional vectors.
362 _currentParticle = nullptr;
363 _currentParticleIndex = std::numeric_limits<decltype(_currentParticleIndex)>::max();
364 _currentVectorIndex = std::numeric_limits<decltype(_currentVectorIndex)>::max();
365 }
366
372 void deleteCurrentParticle() {
373 if (_iteratingAdditionalVectors) {
374 // the current particle address needs to be between start and end of the current vector
375 // otherwise it is from the previous vector
376 auto &currentVector = *_additionalVectors[_currentVectorIndex];
377 // swap-delete
378 *_currentParticle = currentVector.back();
379 currentVector.pop_back();
380 // make sure _currentParticle always points to a valid particle if there is one.
381 if (currentVector.empty() or not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
382 *_currentParticle, _behavior, _regionMin, _regionMax)) {
383 this->operator++();
384 }
385 } else {
386 const auto indicesValid = _container->deleteParticle(_currentVectorIndex, _currentParticleIndex);
387 // Edge cases:
388 if (not indicesValid) {
389 // CASE: the indices and thus the pointer are invalid now
390 this->operator++();
391 } else if (not containerIteratorUtils::particleFulfillsIteratorRequirements<regionIter>(
392 *_currentParticle, _behavior, _regionMin, _regionMax)) {
393 // CASE: the particle was swapped and now the pointer points at something uninteresting
394 this->operator++();
395 }
396 }
397 // move the current index back so that the next ++ points again to the same particle.
398 --_currentParticleIndex;
399 }
400
405 ContainerType *_container = nullptr;
406
411 size_t _currentParticleIndex{0};
412
418 size_t _currentVectorIndex{};
419
424 ParticleType *_currentParticle = nullptr;
425
429 ParticleVecType _additionalVectors;
430
434 IteratorBehavior _behavior;
435
440 bool _iteratingAdditionalVectors{false};
441
445 size_t _vectorIndexOffset{};
446
450 struct empty {};
454 using RegionCornerT = std::conditional_t<regionIter, std::array<double, 3>, empty>;
460 [[no_unique_address]] RegionCornerT _regionMin{};
466 [[no_unique_address]] RegionCornerT _regionMax{};
467};
468} // namespace autopas
Public iterator class that iterates over a particle container and additional vectors (which are typic...
Definition: ContainerIterator.h:95
std::conditional_t< modifiable, Particle_T, const Particle_T > ParticleType
Type of the particle this iterator points to.
Definition: ContainerIterator.h:103
ContainerIterator(ContainerType &container, IteratorBehavior behavior, utils::optRef< ParticleVecType > additionalVectorsToIterate, const std::array< double, 3 > &regionMin, const std::array< double, 3 > &regionMax)
Region Iterator constructor meant to be called from the logic handler.
Definition: ContainerIterator.h:129
ContainerIterator(const ContainerIterator< Particle_T, modifiable, regionIter > &other)
Copy constructor.
Definition: ContainerIterator.h:156
ContainerIterator< Particle_T, modifiable, regionIter > & operator=(ContainerIterator< Particle_T, modifiable, regionIter > &&other) noexcept
Move assignment operator.
Definition: ContainerIterator.h:213
std::conditional_t< modifiable, ParticleContainerInterface< Particle_T >, const ParticleContainerInterface< Particle_T > > ContainerType
Type of the Particle Container type.
Definition: ContainerIterator.h:113
ContainerIterator< Particle_T, modifiable, regionIter > & operator=(const ContainerIterator< Particle_T, modifiable, regionIter > &other)
Copy assignment operator.
Definition: ContainerIterator.h:173
ParticleType * operator->() const
Dereference operator.
Definition: ContainerIterator.h:293
bool operator==(const bool input) const
Checks if the current iterator has a given validity.
Definition: ContainerIterator.h:310
ContainerIterator(ContainerType &container, IteratorBehavior behavior, utils::optRef< ParticleVecType > additionalVectorsToIterate)
Regular Iterator constructor meant to be called from the logic handler.
Definition: ContainerIterator.h:145
bool operator!=(const bool input) const
Checks if the current iterator does not have a given validity.
Definition: ContainerIterator.h:317
ContainerIterator< Particle_T, modifiable, regionIter > & operator++()
Increments the iterator.
Definition: ContainerIterator.h:276
std::conditional_t< modifiable, std::vector< std::vector< Particle_T > * >, std::vector< std::vector< Particle_T > const * > > ParticleVecType
Type of the additional vector collection.
Definition: ContainerIterator.h:108
ContainerIterator(ContainerIterator< Particle_T, modifiable, regionIter > &&other) noexcept
Move constructor.
Definition: ContainerIterator.h:196
ContainerIterator()
Default constructor that guarantees an invalid iterator.
Definition: ContainerIterator.h:118
ParticleType & operator*() const
Dereference operator.
Definition: ContainerIterator.h:287
bool isValid() const
Check whether the iterator currently points to a valid particle.
Definition: ContainerIterator.h:299
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:38
bool particleFulfillsIteratorRequirements(const Particle_T &p, IteratorBehavior behavior, const Arr &regionMin, const Arr &regionMax)
Indicates whether the particle has the correct ownership state and if this is a region iterator is in...
Definition: ContainerIterator.h:59
void deleteParticle(ParticleIterator &iterator)
Function to access private iterator.deleteCurrentParticle() via friend.
Definition: ContainerIterator.h:37
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 > subScalar(const std::array< T, SIZE > &a, T s)
Subtracts a scalar s from each element of array a and returns the result.
Definition: ArrayMath.h:164
constexpr std::array< T, SIZE > addScalar(const std::array< T, SIZE > &a, T s)
Adds a scalar s to each element of array a and returns the result.
Definition: ArrayMath.h:147
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
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
std::optional< std::reference_wrapper< T > > optRef
Short alias for std::optional<std::reference_wrapper<T>>
Definition: optRef.h:16
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:32
int autopas_get_num_threads()
Dummy for omp_get_num_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:138
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132