AutoPas  3.0.0
Loading...
Searching...
No Matches
CellBlock3D.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <algorithm>
11#include <array>
12#include <cmath>
13#include <numeric>
14#include <vector>
15
21#include "autopas/utils/inBox.h"
22
23namespace autopas::internal {
29template <class ParticleCell>
31 public:
35 using index_t = std::size_t;
44 CellBlock3D(std::vector<ParticleCell> &vec, const std::array<double, 3> &bMin, const std::array<double, 3> &bMax,
45 double interactionLength, double cellSizeFactor = 1.0)
46 : _cells(&vec), _boxMin(bMin), _boxMax(bMax), _interactionLength(interactionLength) {
47 rebuild(vec, bMin, bMax, interactionLength, cellSizeFactor);
48
49 for (int i = 0; i < 3; ++i) {
50 if (bMax[i] < bMin[i] + interactionLength) {
51 AutoPasLog(ERROR, "Interaction length too large is {}, bmin {}, bmax {}", interactionLength, bMin[i], bMax[i]);
52 utils::ExceptionHandler::exception("Error in CellBlock3D: interaction Length too large!");
53 }
54 }
55 }
56
60 CellBlock3D(const CellBlock3D &) = delete;
61
67
68 [[nodiscard]] bool cellCanContainHaloParticles(index_t index1d) const override {
69 if (index1d < _firstOwnedCellIndex or index1d > _lastOwnedCellIndex) {
70 return true;
71 }
72 const auto index3d = oneToThreeD(index1d);
73 bool isHaloCell = false;
74 for (size_t i = 0; i < index3d.size(); ++i) {
75 if (index3d[i] < _cellsPerInteractionLength or
76 index3d[i] >= _cellsPerDimensionWithHalo[i] - _cellsPerInteractionLength) {
77 isHaloCell = true;
78 break;
79 }
80 }
81 return isHaloCell;
82 }
83
84 [[nodiscard]] bool cellCanContainOwnedParticles(index_t index1d) const override {
85 return not cellCanContainHaloParticles(index1d);
86 }
87
93 ParticleCell &getCell(index_t index1d) const;
94
100 ParticleCell &getCell(const std::array<index_t, 3> &index3d) const;
101
106 void reserve(size_t numParticles);
107
116 void rebuild(std::vector<ParticleCell> &vec, const std::array<double, 3> &bMin, const std::array<double, 3> &bMax,
117 double interactionLength, double cellSizeFactor);
118
126 ParticleCell &getContainingCell(const std::array<double, 3> &pos) const;
127
133 std::pair<std::array<double, 3>, std::array<double, 3>> getCellBoundingBox(index_t index1d) const;
134
140 std::pair<std::array<double, 3>, std::array<double, 3>> getCellBoundingBox(
141 const std::array<index_t, 3> &index3d) const;
142
150 [[nodiscard]] std::array<index_t, 3> get3DIndexOfPosition(const std::array<double, 3> &pos) const;
151
157 [[nodiscard]] index_t get1DIndexOfPosition(const std::array<double, 3> &pos) const;
158
163 [[nodiscard]] const std::array<index_t, 3> &getCellsPerDimensionWithHalo() const {
164 return _cellsPerDimensionWithHalo;
165 }
166
172 [[nodiscard]] bool checkInHalo(const std::array<double, 3> &position) const;
173
178
189 std::vector<ParticleCell *> getNearbyHaloCells(const std::array<double, 3> &position, double allowedDistance) const {
190 using namespace autopas::utils::ArrayMath::literals;
191
192 const auto index3D = get3DIndexOfPosition(position);
193 const auto index1D = threeToOneD(index3D);
194
195 // these indices are (already) at least 0 and at most _cellsPerDimensionWithHalo[i]-1
196 const auto lowIndex3D = get3DIndexOfPosition(position - allowedDistance);
197 const auto highIndex3D = get3DIndexOfPosition(position + allowedDistance);
198
199 std::vector<ParticleCell *> closeHaloCells;
200 // This is an overestimation with the upper bound of possible number of cells in the vicinity.
201 // An overestimate is cheaper than many reallocations.
202 // If the size of the overallocation ever becomes a problem we can use vector::shrink_to_fit() before return.
203 const auto blockLength = highIndex3D - lowIndex3D;
204 const auto numInterestingCells = std::max(1ul, blockLength[0] * blockLength[1] * blockLength[2]);
205 closeHaloCells.reserve(numInterestingCells);
206
207 // always add the cell the particle is currently in first if it is a halo cell.
208 if ((*_cells)[index1D].getPossibleParticleOwnerships() == OwnershipState::halo) {
209 closeHaloCells.push_back(&getCell(index3D));
210 }
211
212 auto currentIndex3D = lowIndex3D;
213 for (currentIndex3D[0] = lowIndex3D[0]; currentIndex3D[0] <= highIndex3D[0]; ++currentIndex3D[0]) {
214 for (currentIndex3D[1] = lowIndex3D[1]; currentIndex3D[1] <= highIndex3D[1]; ++currentIndex3D[1]) {
215 for (currentIndex3D[2] = lowIndex3D[2]; currentIndex3D[2] <= highIndex3D[2]; ++currentIndex3D[2]) {
216 // we have already added the cell which normally would belong to the particle, so skip here:
217 if (currentIndex3D == index3D) {
218 continue;
219 }
220 // we need to return the cell is it is a halo cell.
221 const auto currentIndex1D = threeToOneD(currentIndex3D);
222 if ((*_cells)[currentIndex1D].getPossibleParticleOwnerships() == OwnershipState::halo) {
223 closeHaloCells.push_back(&getCell(currentIndex3D));
224 }
225 }
226 }
227 }
228
229 return closeHaloCells;
230 }
231
236 [[nodiscard]] const std::array<double, 3> &getHaloBoxMin() const { return _haloBoxMin; }
237
242 [[nodiscard]] const std::array<double, 3> &getHaloBoxMax() const { return _haloBoxMax; }
243
248 [[nodiscard]] unsigned long getCellsPerInteractionLength() const { return _cellsPerInteractionLength; }
249
254 [[nodiscard]] const std::array<double, 3> &getCellLength() const { return _cellLength; }
255
261
267
273
274 private:
280 [[nodiscard]] std::array<index_t, 3> oneToThreeD(index_t index1D) const;
281
287 [[nodiscard]] index_t threeToOneD(const std::array<index_t, 3> &index3D) const;
288
293 std::array<index_t, 3> _cellsPerDimensionWithHalo{};
294 index_t _firstOwnedCellIndex{};
295 index_t _lastOwnedCellIndex{};
296 std::vector<ParticleCell> *_cells;
297
298 std::array<double, 3> _boxMin, _boxMax;
299 std::array<double, 3> _haloBoxMin{}, _haloBoxMax{};
300
301 double _interactionLength{};
302
303 unsigned long _cellsPerInteractionLength{};
304
305 std::array<double, 3> _cellLength{};
306
311 std::array<double, 3> _cellLengthReciprocal{};
312};
313
314template <class ParticleCell>
316 const std::array<double, 3> &pos) const {
317 return threeToOneD(get3DIndexOfPosition(pos));
318}
319
320template <class ParticleCell>
321inline std::array<typename CellBlock3D<ParticleCell>::index_t, 3> CellBlock3D<ParticleCell>::get3DIndexOfPosition(
322 const std::array<double, 3> &pos) const {
323 std::array<typename CellBlock3D<ParticleCell>::index_t, 3> cellIndex{};
324
325 for (size_t dim = 0; dim < 3; dim++) {
326 // 0 <= cellIndex < cellsPerDimWithHalo
327 const long int value = (static_cast<long int>(std::floor((pos[dim] - _boxMin[dim]) * _cellLengthReciprocal[dim]))) +
328 _cellsPerInteractionLength;
329 const index_t nonNegativeValue = static_cast<index_t>(std::max(value, 0l));
330 const index_t nonLargerValue = std::min(nonNegativeValue, _cellsPerDimensionWithHalo[dim] - 1);
331 cellIndex[dim] = nonLargerValue;
332
333 // sanity checks and precautions for numerical instabilities around the edge of the container box,
334 // which would lead to doubling of particles
335 if (pos[dim] >= _boxMax[dim]) {
336 // pos is located outside the box. Make sure that cellIndex is at least in the positive halo range.
337 cellIndex[dim] = std::max(cellIndex[dim], _cellsPerDimensionWithHalo[dim] - _cellsPerInteractionLength);
338 } else if (pos[dim] < _boxMin[dim] and cellIndex[dim] == _cellsPerInteractionLength) {
339 // pos is located outside but the index resolved to be the last IN the box. Fix by decrementing the index.
340 --cellIndex[dim];
341 } else if (pos[dim] < _boxMax[dim] and
342 cellIndex[dim] == _cellsPerDimensionWithHalo[dim] - _cellsPerInteractionLength) {
343 // pos is located inside the box, but cellIndex resolves to be the first halo cell. Fix by decrementing the index.
344 --cellIndex[dim];
345 }
346 }
347
348 return cellIndex;
349}
350
351template <class ParticleCell>
352void CellBlock3D<ParticleCell>::reserve(size_t numParticles) {
353 const auto particlesPerCell = numParticles / _cells->size();
354 for (auto &cell : *_cells) {
355 cell.reserve(particlesPerCell);
356 }
357}
358
359template <class ParticleCell>
360inline void CellBlock3D<ParticleCell>::rebuild(std::vector<ParticleCell> &vec, const std::array<double, 3> &bMin,
361 const std::array<double, 3> &bMax, double interactionLength,
362 double cellSizeFactor) {
363 using namespace autopas::utils::ArrayMath::literals;
364
365 _cells = &vec;
366 _boxMin = bMin;
367 _boxMax = bMax;
368 _interactionLength = interactionLength;
369
370 if (cellSizeFactor >= 1.0) {
371 _cellsPerInteractionLength = 1;
372 } else {
373 _cellsPerInteractionLength = ceil(1.0 / cellSizeFactor);
374 }
375 // compute cell length and number of cells
376 index_t numCells = 1;
377 for (int d = 0; d < 3; ++d) {
378 const double boxLength = _boxMax[d] - _boxMin[d];
379 // The number of cells is rounded down because the cells will be stretched to fit.
380 // std::max to ensure there is at least one cell.
381 const auto cellsPerDim =
382 std::max(static_cast<index_t>(std::floor(boxLength / (_interactionLength * cellSizeFactor))), 1ul);
383
384 _cellsPerDimensionWithHalo[d] = cellsPerDim + 2 * _cellsPerInteractionLength;
385
386 _cellLength[d] = boxLength / static_cast<double>(cellsPerDim);
387
388 _cellLengthReciprocal[d] = static_cast<double>(cellsPerDim) / boxLength; // compute with least rounding possible
389
390 _haloBoxMin[d] = _boxMin[d] - _cellsPerInteractionLength * _cellLength[d];
391 _haloBoxMax[d] = _boxMax[d] + _cellsPerInteractionLength * _cellLength[d];
392
393 numCells *= _cellsPerDimensionWithHalo[d];
394 }
395 AutoPasLog(TRACE, "Box Length incl Halo : {}", autopas::utils::ArrayUtils::to_string(_haloBoxMax - _haloBoxMin));
396 AutoPasLog(TRACE, "Cells/Dim incl Halo : {}", autopas::utils::ArrayUtils::to_string(_cellsPerDimensionWithHalo));
397 AutoPasLog(TRACE, "Cell Length : {}", autopas::utils::ArrayUtils::to_string(_cellLength));
398 AutoPasLog(TRACE, "Interaction Length : {}", interactionLength);
399 AutoPasLog(TRACE, "Cell Size Factor : {}", cellSizeFactor);
400
401 _firstOwnedCellIndex = _cellsPerDimensionWithHalo[0] * _cellsPerDimensionWithHalo[1] * _cellsPerInteractionLength +
402 _cellsPerDimensionWithHalo[0] * _cellsPerInteractionLength + _cellsPerInteractionLength;
403 _lastOwnedCellIndex = numCells - 1 - _firstOwnedCellIndex;
404
405 // initialize cells
406 _cells->resize(numCells);
407
408 for (auto &cell : *_cells) {
409 cell.setCellLength(_cellLength);
410 }
411
412 // determine the OwnershipStates, each cell can contain. This is later used in the CellFunctor to skip calculations
413 for (int i = 0; i < numCells; i++) {
414 const bool canHaveHalos = cellCanContainHaloParticles(i);
415 const bool canHaveOwned = cellCanContainOwnedParticles(i);
416 if (canHaveHalos and canHaveOwned) {
417 (*_cells)[i].setPossibleParticleOwnerships(OwnershipState::owned | OwnershipState::halo);
418 } else if (canHaveHalos) {
419 (*_cells)[i].setPossibleParticleOwnerships(OwnershipState::halo);
420 } else if (canHaveOwned) {
421 (*_cells)[i].setPossibleParticleOwnerships(OwnershipState::owned);
422 } else {
423 (*_cells)[i].setPossibleParticleOwnerships(OwnershipState::dummy);
424 }
425 }
426}
427
428template <class ParticleCell>
430 return _cells->size();
431}
432
433template <class ParticleCell>
435 return _firstOwnedCellIndex;
436}
437
438template <class ParticleCell>
440 return _lastOwnedCellIndex;
441}
442
443template <class ParticleCell>
444inline std::pair<std::array<double, 3>, std::array<double, 3>> CellBlock3D<ParticleCell>::getCellBoundingBox(
445 const index_t index1d) const {
446 return this->getCellBoundingBox(this->oneToThreeD(index1d));
447}
448
449template <class ParticleCell>
450inline std::pair<std::array<double, 3>, std::array<double, 3>> CellBlock3D<ParticleCell>::getCellBoundingBox(
451 const std::array<index_t, 3> &index3d) const {
452 std::array<double, 3> boxmin{}, boxmax{};
453 for (int d = 0; d < 3; d++) {
454 // defaults
455 boxmin[d] = index3d[d] * this->_cellLength[d] + _haloBoxMin[d];
456
457 // stupid rounding errors. Snap values to the exact box values.
458 if (index3d[d] == 0) {
459 boxmin[d] = _haloBoxMin[d];
460 } else if (index3d[d] == _cellsPerInteractionLength) {
461 // Case: we are at the lower boundary of the non-halo box
462 boxmin[d] = _boxMin[d];
463 }
464
465 boxmax[d] = boxmin[d] + this->_cellLength[d];
466
467 // This must not be an else to the if block above as both cases can be true.
468 // e.g. if there is only one cell
469 if (index3d[d] == this->_cellsPerDimensionWithHalo[d] - _cellsPerInteractionLength - 1) {
470 boxmax[d] = _boxMax[d];
471 } else if (index3d[d] == this->_cellsPerDimensionWithHalo[d] - 1) {
472 boxmin[d] = _haloBoxMax[d] - this->_cellLength[d];
473 boxmax[d] = _haloBoxMax[d];
474 }
475 }
476 return {boxmin, boxmax};
477}
478
479template <class ParticleCell>
480inline ParticleCell &CellBlock3D<ParticleCell>::getContainingCell(const std::array<double, 3> &pos) const {
481 auto ind = get1DIndexOfPosition(pos);
482 return getCell(ind);
483}
484
485template <class ParticleCell>
487 return (*_cells)[index1d];
488}
489
490template <class ParticleCell>
491inline ParticleCell &CellBlock3D<ParticleCell>::getCell(const std::array<index_t, 3> &index3d) const {
492 return (*_cells)[threeToOneD(index3d)];
493}
494
495template <class ParticleCell>
496inline std::array<typename CellBlock3D<ParticleCell>::index_t, 3> CellBlock3D<ParticleCell>::oneToThreeD(
497 index_t index1D) const {
498 return utils::ThreeDimensionalMapping::oneToThreeD(index1D, _cellsPerDimensionWithHalo);
499}
500
501template <class ParticleCell>
502inline typename CellBlock3D<ParticleCell>::index_t CellBlock3D<ParticleCell>::threeToOneD(
503 const std::array<index_t, 3> &index3D) const {
504 return utils::ThreeDimensionalMapping::threeToOneD(index3D, _cellsPerDimensionWithHalo);
505}
506
507template <class ParticleCell>
508bool CellBlock3D<ParticleCell>::checkInHalo(const std::array<double, 3> &position) const {
509 return autopas::utils::inBox(position, _haloBoxMin, _haloBoxMax) &&
510 autopas::utils::notInBox(position, _boxMin, _boxMax);
511}
512
513template <class ParticleCell>
515 std::vector<index_t> haloSlices(2 * _cellsPerInteractionLength);
516 auto mid = haloSlices.begin() + _cellsPerInteractionLength;
517 std::iota(haloSlices.begin(), mid, 0);
518
519 // x: min and max of x
520 std::iota(mid, haloSlices.end(), _cellsPerDimensionWithHalo[0] - _cellsPerInteractionLength);
521 for (index_t i : haloSlices) {
522 for (index_t j = 0; j < _cellsPerDimensionWithHalo[1]; j++) {
523 for (index_t k = 0; k < _cellsPerDimensionWithHalo[2]; k++) {
524 index_t index = threeToOneD({i, j, k});
525 (*_cells)[index].clear();
526 }
527 }
528 }
529 // y: min and max of y
530 std::iota(mid, haloSlices.end(), _cellsPerDimensionWithHalo[1] - _cellsPerInteractionLength);
531 for (index_t i = 1; i < _cellsPerDimensionWithHalo[0] - 1; i++) { // 0 and cells-1 already done in previous loop
532 for (index_t j : haloSlices) {
533 for (index_t k = 0; k < _cellsPerDimensionWithHalo[2]; k++) {
534 index_t index = threeToOneD({i, j, k});
535 (*_cells)[index].clear();
536 }
537 }
538 }
539 // z: min and max of z
540 std::iota(mid, haloSlices.end(), _cellsPerDimensionWithHalo[2] - _cellsPerInteractionLength);
541 for (index_t i = 1; i < _cellsPerDimensionWithHalo[0] - 1; i++) { // 0 and cells-1 already done in previous loop
542 for (index_t j = 1; j < _cellsPerDimensionWithHalo[1] - 1; j++) { // 0 and cells-1 already done in previous loop
543 for (index_t k : haloSlices) {
544 index_t index = threeToOneD({i, j, k});
545 (*_cells)[index].clear();
546 }
547 }
548 }
549}
550} // namespace autopas::internal
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
Class for Cells of Particles.
Definition: ParticleCell.h:51
Class that manages a block of ParticleCells.
Definition: CellBlock3D.h:30
const std::array< double, 3 > & getHaloBoxMin() const
Get the lower corner of the halo region.
Definition: CellBlock3D.h:236
unsigned long getCellsPerInteractionLength() const
Get the number of cells per interaction length.
Definition: CellBlock3D.h:248
ParticleCell & getContainingCell(const std::array< double, 3 > &pos) const
Get the containing cell of a specified position.
Definition: CellBlock3D.h:480
const std::array< double, 3 > & getHaloBoxMax() const
Get the upper corner of the halo region.
Definition: CellBlock3D.h:242
const std::array< index_t, 3 > & getCellsPerDimensionWithHalo() const
Get the dimension of the cell block including the halo boxes.
Definition: CellBlock3D.h:163
bool cellCanContainHaloParticles(index_t index1d) const override
Checks if the cell with the one-dimensional index index1d can contain halo particles.
Definition: CellBlock3D.h:68
index_t getFirstOwnedCellIndex() const
1D id of the first cell that is not in the halo.
Definition: CellBlock3D.h:434
std::vector< ParticleCell * > getNearbyHaloCells(const std::array< double, 3 > &position, double allowedDistance) const
Get the halo cells around a given point.
Definition: CellBlock3D.h:189
index_t get1DIndexOfPosition(const std::array< double, 3 > &pos) const
Get the 1d index of the cell block for a given position.
Definition: CellBlock3D.h:315
bool cellCanContainOwnedParticles(index_t index1d) const override
Checks if the cell with the one-dimensional index index1d can contain owned particles.
Definition: CellBlock3D.h:84
index_t getNumCells() const
Get the number of cells in this block.
Definition: CellBlock3D.h:429
void reserve(size_t numParticles)
Reserve memory for a given number of particles.
Definition: CellBlock3D.h:352
void rebuild(std::vector< ParticleCell > &vec, const std::array< double, 3 > &bMin, const std::array< double, 3 > &bMax, double interactionLength, double cellSizeFactor)
Rebuild the cell block.
Definition: CellBlock3D.h:360
bool checkInHalo(const std::array< double, 3 > &position) const
Checks whether a given position is inside the halo region of the managed cell block.
Definition: CellBlock3D.h:508
void clearHaloCells()
Deletes all particles in the halo cells of the managed cell block.
Definition: CellBlock3D.h:514
ParticleCell & getCell(index_t index1d) const
get the ParticleCell of a specified 1d index.
Definition: CellBlock3D.h:486
CellBlock3D & operator=(const CellBlock3D)=delete
Deleted assignment operator.
CellBlock3D(const CellBlock3D &)=delete
Deleted copy constructor.
CellBlock3D(std::vector< ParticleCell > &vec, const std::array< double, 3 > &bMin, const std::array< double, 3 > &bMax, double interactionLength, double cellSizeFactor=1.0)
Constructor of CellBlock3D.
Definition: CellBlock3D.h:44
std::size_t index_t
The index type to access the particle cells.
Definition: CellBlock3D.h:35
ParticleCell & getCell(const std::array< index_t, 3 > &index3d) const
Get the ParticleCell of a specified 3d index.
Definition: CellBlock3D.h:491
std::pair< std::array< double, 3 >, std::array< double, 3 > > getCellBoundingBox(index_t index1d) const
Get the lower and upper corner of the cell at the 1d index index1d.
Definition: CellBlock3D.h:444
const std::array< double, 3 > & getCellLength() const
Get the cell lengths.
Definition: CellBlock3D.h:254
index_t getLastOwnedCellIndex() const
1D id of the last cell before there are only halo cells left.
Definition: CellBlock3D.h:439
std::pair< std::array< double, 3 >, std::array< double, 3 > > getCellBoundingBox(const std::array< index_t, 3 > &index3d) const
Get the lower and upper corner of the cell at the 3d index index3d.
Definition: CellBlock3D.h:450
std::array< index_t, 3 > get3DIndexOfPosition(const std::array< double, 3 > &pos) const
Get the 3d index of the cell block for a given position.
Definition: CellBlock3D.h:321
Interface class to handle cell borders and cell types of cells.
Definition: CellBorderAndFlagManager.h:17
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
This namespace is used for implementation specifics.
Definition: CellFunctor.h:14
void to_string(std::ostream &os, const Container &container, const std::string &delimiter, const std::array< std::string, 2 > &surround, Fun elemToString)
Generates a string representation of a container which fulfills the Container requirement (provide cb...
Definition: ArrayUtils.h:102
constexpr std::array< T, 3 > oneToThreeD(T ind, const std::array< T, 3 > &dims)
Convert a 1d index to a 3d index.
Definition: ThreeDimensionalMapping.h:55
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
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
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
@ dummy
Dummy or deleted state, a particle with this state is not an actual particle!
@ 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...