AutoPas  3.0.0
Loading...
Searching...
No Matches
LCC01Traversal.h
Go to the documentation of this file.
1
7#pragma once
8
18
19namespace autopas {
20
80template <class ParticleCell, class Functor, bool combineSoA = false>
81class LCC01Traversal : public C01BasedTraversal<ParticleCell, Functor, (combineSoA ? 2 : 3)>,
83 public:
96 explicit LCC01Traversal(const std::array<unsigned long, 3> &dims, Functor &functor, const double interactionLength,
97 const std::array<double, 3> &cellLength, DataLayoutOption dataLayout, bool useNewton3)
98 : C01BasedTraversal<ParticleCell, Functor, (combineSoA ? 2 : 3)>(dims, functor, interactionLength, cellLength,
99 dataLayout, useNewton3),
100 _cellFunctor(functor, interactionLength /*should use cutoff here, if not used to build verlet-lists*/,
101 dataLayout, useNewton3),
102 _functor(functor),
103 _cacheOffset(DEFAULT_CACHE_LINE_SIZE / sizeof(unsigned int)) {
105 }
106
110 void computeOffsets();
111
112 void traverseParticles() override;
113
118 [[nodiscard]] bool isApplicableToDomain() const override { return true; }
119
120 [[nodiscard]] TraversalOption getTraversalType() const override {
121 return (combineSoA) ? TraversalOption::lc_c01_combined_SoA : TraversalOption::lc_c01;
122 }
123
127 void setAoSSortingThresholds(const SortingThresholdInfoInterface &aosSortingThreshold) override {
128 _cellFunctor.setAoSSortingThresholds(aosSortingThreshold);
129 }
130
134 void setSoASortingThresholds(const SortingThresholdInfoInterface &soaSortingThreshold) override {
135 _cellFunctor.setSoASortingThresholds(soaSortingThreshold);
136 }
137
138 private:
139 // CellOffsets needs to store interaction pairs or triplets depending on the Functor type.
140 using CellOffsetsType = std::conditional_t<decltype(utils::isPairwiseFunctor<Functor>())::value,
141 std::vector<std::vector<std::pair<long, std::array<double, 3>>>>,
142 std::vector<std::tuple<long, long, std::array<double, 3>>>>;
143
144 // CellFunctor type for either Pairwise or Triwise Functors.
145 using CellFunctorType = std::conditional_t<decltype(utils::isPairwiseFunctor<Functor>())::value,
148
157 inline void processBaseCell(std::vector<ParticleCell> &cells, unsigned long x, unsigned long y, unsigned long z);
158
163 inline void processBaseCellPairwise(std::vector<ParticleCell> &cells, unsigned long x, unsigned long y,
164 unsigned long z);
165
170 inline void processBaseCellTriwise(std::vector<ParticleCell> &cells, unsigned long x, unsigned long y,
171 unsigned long z);
172
177 void computePairwiseOffsets();
178
183 void computeTriwiseOffsets();
184
191 template <std::size_t... I>
192 constexpr void appendNeeded(ParticleCell &cell, ParticleCell &appendCell, std::index_sequence<I...>) {
193 cell._particleSoABuffer.template append<std::get<I>(Functor::getNeededAttr(std::false_type()))...>(
194 appendCell._particleSoABuffer);
195 }
196
201 void resizeBuffers();
202
207 CellOffsetsType _cellOffsets;
208
212 CellFunctorType _cellFunctor;
213
217 Functor &_functor;
218
222 std::vector<std::vector<ParticleCell>> _combinationSlices;
223
227 std::vector<unsigned int> _currentSlices;
228
232 const unsigned int _cacheOffset;
233};
234
235template <class ParticleCell, class Functor, bool combineSoA>
237 if constexpr (utils::isPairwiseFunctor<Functor>()) {
238 computePairwiseOffsets();
240 computeTriwiseOffsets();
241 } else {
242 utils::ExceptionHandler::exception("LCC01Traversal::computeOffsets(): Functor is not valid.");
243 }
244}
245
246template <class ParticleCell, class Functor, bool combineSoA>
248 _cellOffsets.resize(2 * this->_overlap[0] + 1);
249
250 const auto interactionLengthSquare{this->_interactionLength * this->_interactionLength};
251
252 for (long x = -this->_overlap[0]; x <= 0l; ++x) {
253 for (long y = -this->_overlap[1]; y <= static_cast<long>(this->_overlap[1]); ++y) {
254 for (long z = -this->_overlap[2]; z <= static_cast<long>(this->_overlap[2]); ++z) {
255 const std::array<double, 3> pos = {
256 std::max(0l, (std::abs(x) - 1l)) * this->_cellLength[0],
257 std::max(0l, (std::abs(y) - 1l)) * this->_cellLength[1],
258 std::max(0l, (std::abs(z) - 1l)) * this->_cellLength[2],
259 };
260 const double distSquare = utils::ArrayMath::dot(pos, pos);
261 if (distSquare <= interactionLengthSquare) {
262 const long currentOffset = utils::ThreeDimensionalMapping::threeToOneD(
263 x, y, z, utils::ArrayUtils::static_cast_copy_array<long>(this->_cellsPerDimension));
264 const bool containCurrentOffset =
265 std::any_of(_cellOffsets[x + this->_overlap[0]].cbegin(), _cellOffsets[x + this->_overlap[0]].cend(),
266 [currentOffset](const auto &e) { return e.first == currentOffset; });
267 if (containCurrentOffset) {
268 continue;
269 }
270 for (long ix = x; ix <= std::abs(x); ++ix) {
272 ix, y, z, utils::ArrayUtils::static_cast_copy_array<long>(this->_cellsPerDimension));
273 const size_t index = ix + this->_overlap[0];
274
275 // Calculate the sorting direction from the base cell (x, y, z) and the other cell by use of the offset (ix,
276 // y, z).
277 std::array<double, 3> sortingDir = {static_cast<double>(ix) * this->_cellLength[0],
278 static_cast<double>(y) * this->_cellLength[1],
279 static_cast<double>(z) * this->_cellLength[2]};
280
281 // the offset to the current cell itself is zero.
282 if (ix == 0 and y == 0 and z == 0) {
283 sortingDir = {1., 1., 1.};
284 }
285 sortingDir = utils::ArrayMath::normalize(sortingDir);
286
287 if (y == 0l and z == 0l) {
288 // make sure center of slice is always at the beginning
289 _cellOffsets[index].insert(_cellOffsets[index].cbegin(), std::make_pair(offset, sortingDir));
290 } else {
291 _cellOffsets[index].emplace_back(offset, sortingDir);
292 }
293 }
294 }
295 }
296 }
297 }
298}
299
300template <class ParticleCell, class Functor, bool combineSoA>
301inline void LCC01Traversal<ParticleCell, Functor, combineSoA>::computeTriwiseOffsets() {
302 using namespace utils::ArrayMath::literals;
303 // Reserve approximately. Overestimates more for larger overlap.
304 const int cubeSize = this->_overlap[0] * this->_overlap[1] * this->_overlap[2];
305 _cellOffsets.reserve(cubeSize * cubeSize / 4);
306
307 // Helper function to get minimal distance between two cells
308 auto cellDistance = [&](long x1, long y1, long z1, long x2, long y2, long z2) {
309 return std::array<double, 3>{std::max(0l, (std::abs(x1 - x2) - 1l)) * this->_cellLength[0],
310 std::max(0l, (std::abs(y1 - y2) - 1l)) * this->_cellLength[1],
311 std::max(0l, (std::abs(z1 - z2) - 1l)) * this->_cellLength[2]};
312 };
313
314 const auto interactionLengthSquare{this->_interactionLength * this->_interactionLength};
315 _cellOffsets.emplace_back(0, 0, std::array<double, 3>{1., 1., 1.});
316
317 // offsets for the first cell
318 for (long x1 = -this->_overlap[0]; x1 <= static_cast<long>(this->_overlap[0]); ++x1) {
319 for (long y1 = -this->_overlap[1]; y1 <= static_cast<long>(this->_overlap[1]); ++y1) {
320 for (long z1 = -this->_overlap[2]; z1 <= static_cast<long>(this->_overlap[2]); ++z1) {
321 // check distance between base cell and cell 1
322 const auto dist01 = cellDistance(0l, 0l, 0l, x1, y1, z1);
323
324 const double distSquare = utils::ArrayMath::dot(dist01, dist01);
325 if (distSquare > interactionLengthSquare) continue;
326
327 // offsets for cell 2
328 for (long x2 = -this->_overlap[0]; x2 <= static_cast<long>(this->_overlap[0]); ++x2) {
329 for (long y2 = -this->_overlap[1]; y2 <= static_cast<long>(this->_overlap[1]); ++y2) {
330 for (long z2 = -this->_overlap[2]; z2 <= static_cast<long>(this->_overlap[2]); ++z2) {
331 // check distance between cell 1 and cell 2
332 const auto dist12 = cellDistance(x1, y1, z1, x2, y2, z2);
333
334 const double dist12Squared = utils::ArrayMath::dot(dist12, dist12);
335 if (dist12Squared > interactionLengthSquare) continue;
336
337 // check distance between base cell and cell 2
338 const auto dist02 = cellDistance(0l, 0l, 0l, x2, y2, z2);
339
340 const double dist02Squared = utils::ArrayMath::dot(dist02, dist02);
341 if (dist02Squared > interactionLengthSquare) continue;
342
344 x1, y1, z1, utils::ArrayUtils::static_cast_copy_array<long>(this->_cellsPerDimension));
345
347 x2, y2, z2, utils::ArrayUtils::static_cast_copy_array<long>(this->_cellsPerDimension));
348
349 // Only add unique combinations. E.g.: (5, 8) == (8, 5)
350 if (offset2 <= offset1) continue;
351
352 // sorting direction from base cell to the first different cell
353 std::array<double, 3> sortDirection{};
354 if (offset1 == 0) {
355 sortDirection = {x2 * this->_cellLength[0], y2 * this->_cellLength[1], z2 * this->_cellLength[2]};
356 } else {
357 sortDirection = {x1 * this->_cellLength[0], y1 * this->_cellLength[1], z1 * this->_cellLength[2]};
358 }
359 _cellOffsets.emplace_back(offset1, offset2, utils::ArrayMath::normalize(sortDirection));
360 }
361 }
362 }
363 }
364 }
365 }
366}
367
368template <class ParticleCell, class Functor, bool combineSoA>
369inline void LCC01Traversal<ParticleCell, Functor, combineSoA>::processBaseCell(std::vector<ParticleCell> &cells,
370 unsigned long x, unsigned long y,
371 unsigned long z) {
372 if constexpr (utils::isPairwiseFunctor<Functor>()) {
373 processBaseCellPairwise(cells, x, y, z);
374 } else if constexpr (utils::isTriwiseFunctor<Functor>()) {
375 processBaseCellTriwise(cells, x, y, z);
376 } else {
378 "LCC01Traversal::processBaseCell(): Functor {} is not of type PairwiseFunctor or TriwiseFunctor.",
379 _functor.getName());
380 }
381}
382
383template <class ParticleCell, class Functor, bool combineSoA>
384inline void LCC01Traversal<ParticleCell, Functor, combineSoA>::processBaseCellPairwise(std::vector<ParticleCell> &cells,
385 unsigned long x, unsigned long y,
386 unsigned long z) {
387 unsigned long baseIndex = utils::ThreeDimensionalMapping::threeToOneD(x, y, z, this->_cellsPerDimension);
388 ParticleCell &baseCell = cells[baseIndex];
389 const size_t cOffSize = _cellOffsets.size();
390
391 if constexpr (combineSoA) {
392 // Iteration along x
393
394 const auto threadID = static_cast<size_t>(autopas_get_thread_num());
395 auto &currentSlice = _currentSlices[threadID * _cacheOffset];
396 auto &combinationSlice = _combinationSlices[threadID];
397
398 // First cell needs to initialize whole buffer
399 if (x == this->_overlap[0]) {
400 currentSlice = 0;
401 for (unsigned int offsetSlice = 0; offsetSlice < cOffSize; offsetSlice++) {
402 combinationSlice[offsetSlice]._particleSoABuffer.clear();
403 for (const auto &offset : _cellOffsets[offsetSlice]) {
404 const unsigned long otherIndex = baseIndex + offset.first;
405 ParticleCell &otherCell = cells[otherIndex];
406 appendNeeded(combinationSlice[offsetSlice], otherCell,
407 std::make_index_sequence<Functor::getNeededAttr(std::false_type()).size()>{});
408 }
409 }
410 } else {
411 // reduce size
412 size_t i = 0;
413 const size_t midSlice = (currentSlice + this->_overlap[0] + 1) % cOffSize;
414 for (size_t slice = (currentSlice + 1) % cOffSize; slice != midSlice; ++slice %= cOffSize, ++i) {
415 size_t newSize = 0;
416 for (const auto &offset : _cellOffsets[i]) {
417 const unsigned long otherIndex = baseIndex + offset.first;
418 ParticleCell &otherCell = cells[otherIndex];
419 newSize += otherCell.size();
420 }
421 combinationSlice[slice]._particleSoABuffer.resizeArrays(newSize);
422 }
423 // append buffers
424 for (size_t slice = midSlice; slice != currentSlice; ++slice %= cOffSize, ++i) {
425 for (auto offsetIndex = _cellOffsets[(i + 1) % cOffSize].size(); offsetIndex < _cellOffsets[i].size();
426 ++offsetIndex) {
427 const unsigned long otherIndex = baseIndex + _cellOffsets[i][offsetIndex].first;
428 ParticleCell &otherCell = cells[otherIndex];
429 appendNeeded(combinationSlice[slice], otherCell,
430 std::make_index_sequence<Functor::getNeededAttr(std::false_type()).size()>{});
431 }
432 }
433
434 combinationSlice[currentSlice]._particleSoABuffer.clear();
435
436 for (const auto &offset : _cellOffsets.back()) {
437 const unsigned long otherIndex = baseIndex + offset.first;
438 ParticleCell &otherCell = cells[otherIndex];
439 appendNeeded(combinationSlice[currentSlice], otherCell,
440 std::make_index_sequence<Functor::getNeededAttr(std::false_type()).size()>{});
441 }
442
443 ++currentSlice %= cOffSize;
444 }
445
446 // calculate all interactions
447 for (unsigned int slice = 0; slice < cOffSize; slice++) {
448 if (slice == (currentSlice + this->_overlap[0]) % cOffSize) {
449 // slice contains base cell -> skip particles of base cell. This is not supported by CellFunctor, so call
450 // pairwise functor directly.
451 auto startIndex = baseCell.size();
452 auto endIndex = combinationSlice[slice]._particleSoABuffer.size();
453 _functor.SoAFunctorPair(baseCell._particleSoABuffer,
454 {&(combinationSlice[slice]._particleSoABuffer), startIndex, endIndex}, false);
455 // compute base cell
456 this->_cellFunctor.processCell(baseCell);
457 } else {
458 this->_cellFunctor.processCellPair(baseCell, combinationSlice[slice]);
459 }
460 }
461 } else {
462 for (const auto &slice : _cellOffsets) {
463 for (auto const &[offset, r] : slice) {
464 const unsigned long otherIndex = baseIndex + offset;
465 ParticleCell &otherCell = cells[otherIndex];
466
467 if (baseIndex == otherIndex) {
468 this->_cellFunctor.processCell(baseCell);
469 } else {
470 this->_cellFunctor.processCellPair(baseCell, otherCell, r);
471 }
472 }
473 }
474 }
475}
476
477template <class ParticleCell, class Functor, bool combineSoA>
478inline void LCC01Traversal<ParticleCell, Functor, combineSoA>::processBaseCellTriwise(std::vector<ParticleCell> &cells,
479 unsigned long x, unsigned long y,
480 unsigned long z) {
481 unsigned long baseIndex = utils::ThreeDimensionalMapping::threeToOneD(x, y, z, this->_cellsPerDimension);
482 ParticleCell &baseCell = cells[baseIndex];
483
484 for (auto const &[offset1, offset2, r] : _cellOffsets) {
485 const unsigned long otherIndex1 = baseIndex + offset1;
486 const unsigned long otherIndex2 = baseIndex + offset2;
487 ParticleCell &otherCell1 = cells[otherIndex1];
488 ParticleCell &otherCell2 = cells[otherIndex2];
489
490 if (baseIndex == otherIndex1 and baseIndex == otherIndex2) {
491 this->_cellFunctor.processCell(baseCell);
492 } else if (baseIndex == otherIndex1 and baseIndex != otherIndex2) {
493 this->_cellFunctor.processCellPair(baseCell, otherCell2);
494 } else if (baseIndex != otherIndex1 and baseIndex == otherIndex2) {
495 this->_cellFunctor.processCellPair(baseCell, otherCell1);
496 } else if (baseIndex != otherIndex1 and otherIndex1 == otherIndex2) {
497 this->_cellFunctor.processCellPair(baseCell, otherCell1);
498 } else {
499 this->_cellFunctor.processCellTriple(baseCell, otherCell1, otherCell2, r);
500 }
501 }
502}
503
504template <class ParticleCell, class PairwiseFunctor, bool combineSoA>
505inline void LCC01Traversal<ParticleCell, PairwiseFunctor, combineSoA>::resizeBuffers() {
506 const auto numThreads = static_cast<size_t>(autopas_get_max_threads());
507 if (_combinationSlices.size() != numThreads) {
508 _combinationSlices.resize(numThreads);
509 const auto cellOffsetsSize = _cellOffsets.size();
510 std::for_each(_combinationSlices.begin(), _combinationSlices.end(),
511 [cellOffsetsSize](auto &e) { e.resize(cellOffsetsSize); });
512 _currentSlices.resize(numThreads * _cacheOffset);
513 }
514}
515
516template <class ParticleCell, class Functor, bool combineSoA>
518 auto &cells = *(this->_cells);
519 if (not this->isApplicableToDomain()) {
520 if constexpr (combineSoA) {
522 "The C01 traversal with combined SoA buffers cannot work with data layout AoS and enabled newton3 (unless "
523 "only one thread is used)!");
524 } else {
526 "The C01 traversal cannot work with enabled newton3 (unless only one thread is used)!");
527 }
528 }
529 if constexpr (combineSoA) {
530 resizeBuffers();
531 }
532 this->c01Traversal([&](unsigned long x, unsigned long y, unsigned long z) { this->processBaseCell(cells, x, y, z); });
533}
534
535} // namespace autopas
This class provides the base for traversals using the c01 base step.
Definition: C01BasedTraversal.h:25
Functor base class.
Definition: Functor.h:41
static constexpr std::array< typename Particle_T::AttributeNames, 0 > getNeededAttr()
Get attributes needed for computation.
Definition: Functor.h:78
This class provides the c01 traversal and the c01 traversal with combined SoA buffers.
Definition: LCC01Traversal.h:82
void setAoSSortingThresholds(const SortingThresholdInfoInterface &aosSortingThreshold) override
Set the aos-sorting-threshold for traversals that use the CellFunctor.
Definition: LCC01Traversal.h:127
void traverseParticles() override
Traverse the particles by pairs, triplets etc.
Definition: LCC01Traversal.h:517
void setSoASortingThresholds(const SortingThresholdInfoInterface &soaSortingThreshold) override
Set the SoA sorting-threshold for traversals that use the CellFunctor.
Definition: LCC01Traversal.h:134
TraversalOption getTraversalType() const override
Return a enum representing the name of the traversal class.
Definition: LCC01Traversal.h:120
void computeOffsets()
Computes all combinations of cells used in processBaseCell()
Definition: LCC01Traversal.h:236
bool isApplicableToDomain() const override
LC C01 is always applicable to the domain.
Definition: LCC01Traversal.h:118
LCC01Traversal(const std::array< unsigned long, 3 > &dims, Functor &functor, const double interactionLength, const std::array< double, 3 > &cellLength, DataLayoutOption dataLayout, bool useNewton3)
Constructor of the c01 traversal.
Definition: LCC01Traversal.h:96
Interface for traversals used by the LinkedCell class.
Definition: LCTraversalInterface.h:18
Class for Cells of Particles.
Definition: ParticleCell.h:49
A cell functor.
Definition: CellFunctor3B.h:25
A cell functor.
Definition: CellFunctor.h:32
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:64
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 > normalize(const std::array< T, SIZE > &a)
Generates a normalized array (|a| = 1).
Definition: ArrayMath.h:304
constexpr T threeToOneD(T x, T y, T z, const std::array< T, 3 > &dims)
Convert a 3d index to a 1d index.
Definition: ThreeDimensionalMapping.h:29
decltype(isTriwiseFunctorImpl(std::declval< FunctorT >())) isTriwiseFunctor
Check whether a Functor Type is inheriting from TriwiseFunctor.
Definition: checkFunctorType.h:56
decltype(isPairwiseFunctorImpl(std::declval< FunctorT >())) isPairwiseFunctor
Check whether a Functor Type is inheriting from PairwiseFunctor.
Definition: checkFunctorType.h:49
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
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132
constexpr unsigned int DEFAULT_CACHE_LINE_SIZE
Default size for a cache line.
Definition: AlignedAllocator.h:21
Polymorphic base for sorting-threshold storage.
Definition: SortingThresholdInfoInterface.h:18