AutoPas  3.0.0
Loading...
Searching...
No Matches
VLCAllCellsNeighborList.h
Go to the documentation of this file.
1
12#pragma once
13
17
18namespace autopas {
19
25template <class ParticleCell>
26class TraversalSelector;
27
28template <class Particle_T, class NeighborList>
29class VLCTraversalInterface;
30
36template <class Particle_T>
38 public:
43
47 using SoAPairOfParticleAndList = std::pair<size_t, std::vector<size_t, autopas::AlignedAllocator<size_t>>>;
48
53 using SoAListType = typename std::vector<std::vector<SoAPairOfParticleAndList>>;
54
58 [[nodiscard]] ContainerOption getContainerType() const override { return ContainerOption::verletListsCells; }
59
63 void buildAoSNeighborList(TraversalOption vlcTraversalOpt, LinkedCells<Particle_T> &linkedCells, bool useNewton3) {
64 using namespace utils::ArrayMath::literals;
65 // Sanity check.
66 if (linkedCells.getCellBlock().getCellsPerInteractionLength() > 1) {
68 "VLCAllCellsNeighborLists::buildAoSNeighborList() was called with a CSF < 1 but it only supports CSF>=1.");
69 }
70 // Define some aliases
71 auto &neighborLists = getAoSNeighborList();
72 auto &cells = linkedCells.getCells();
73 const auto interactionLength = linkedCells.getInteractionLength();
74 const auto interactionLengthSquared = interactionLength * interactionLength;
75 const auto boxSizeWithHalo = linkedCells.getBoxMax() - linkedCells.getBoxMin() +
76 std::array<double, 3>{interactionLength, interactionLength, interactionLength} * 2.;
77 // Create an estimate for the average length of a neighbor list.
78 // This assumes homogeneous distribution and some overestimation.
79 const auto listLengthEstimate = VerletListsCellsHelpers::estimateListLength(
80 linkedCells.getNumberOfParticles(IteratorBehavior::ownedOrHalo), boxSizeWithHalo, interactionLength, 1.3);
81
82 // Reset lists. Don't free any memory, only mark as unused.
83 this->setLinkedCellsPointer(&linkedCells);
84 for (auto &cellLists : neighborLists) {
85 for (auto &[particlePtr, neighbors] : cellLists) {
86 particlePtr = nullptr;
87 neighbors.clear();
88 }
89 }
90 neighborLists.resize(cells.size());
91
92 /* This must not be a doc comment (with two **) to not confuse doxygen.
93 * Helper function to insert a pointer into a list of the base cell.
94 * It considers the cases that neither particle is in the base cell
95 * and in that case finds or creates the appropriate list.
96 *
97 * @param p1 Reference to source particle.
98 * @param p1Index Index of p1 in its cell.
99 * @param p2 Reference to target particle.
100 * @param cellIndex1 Index of cell where p1 resides.
101 * @param cellIndexBase Index of the base cell of the c08 step.
102 * @param neighborList Reference to the list where the particle pair should be stored.
103 */
104 auto insert = [&](auto &p1, auto p1Index, auto &p2, auto cellIndex1, auto cellIndexBase, auto &neighborList) {
105 // Easy case: cell1 is the base cell
106 if (cellIndexBase == cellIndex1) {
107 neighborList[p1Index].second.push_back(&p2);
108 } else {
109 // Otherwise, check if the base cell already has a list for p1
110 auto iter = std::find_if(neighborList.begin(), neighborList.end(), [&](const auto &pair) {
111 const auto &[particlePtr, list] = pair;
112 return particlePtr == &p1;
113 });
114 // If yes, append p2 to it.
115 if (iter != neighborList.end()) {
116 iter->second.push_back(&p2);
117 } else {
118 // If no, create one (or reuse an empty pair), reserve space for the list and emplace p2
119 if (auto insertLoc = std::find_if(neighborList.begin(), neighborList.end(),
120 [&](const auto &pair) {
121 const auto &[particlePtr, list] = pair;
122 return particlePtr == nullptr;
123 });
124 insertLoc != neighborList.end()) {
125 auto &[particlePtr, neighbors] = *insertLoc;
126 particlePtr = &p1;
127 neighbors.reserve(listLengthEstimate);
128 neighbors.push_back(&p2);
129 } else {
130 neighborList.emplace_back(&p1, std::vector<Particle_T *>{});
131 neighborList.back().second.reserve(listLengthEstimate);
132 neighborList.back().second.push_back(&p2);
133 }
134 }
135 }
136 };
137
138 const auto &cellsPerDim =
139 utils::ArrayUtils::static_cast_copy_array<int>(linkedCells.getCellBlock().getCellsPerDimensionWithHalo());
140 // Vector of offsets from the base cell for the base step corresponding to the traversal
141 // and respective factors for the fraction of particles per cell that need neighbor lists in the base cell.
142 const auto offsets = VerletListsCellsHelpers::buildBaseStep(cellsPerDim, vlcTraversalOpt);
143
144 int xEnd{};
145 int yEnd{};
146 int zEnd{};
147
148 switch (vlcTraversalOpt) {
149 case TraversalOption::vlc_c08:
150 // cellsPerDim includes halo cells, so outermost cells are halo.
151 // Only owned-halo interactions are needed, not halo-halo.
152 // c08 is forward-looking: halo base cells only interact with other halo cells -> can skip.
153 // Other traversals like C01 and C18 are backward-looking: halo base cells may interact with owned cells -> must
154 // keep.
155 xEnd = cellsPerDim[0] - 1;
156 yEnd = cellsPerDim[1] - 1;
157 zEnd = cellsPerDim[2] - 1;
158 break;
159 default:
160 xEnd = cellsPerDim[0];
161 yEnd = cellsPerDim[1];
162 zEnd = cellsPerDim[2];
163 break;
164 }
165
166 // Since there are no loop dependencies merge all for loops and create 10 chunks per thread.
167 AUTOPAS_OPENMP(parallel for collapse(3) schedule(dynamic, std::max(cells.size() / (autopas::autopas_get_max_threads() * 10), 1ul)))
168 for (int z = 0; z < zEnd; ++z) {
169 for (int y = 0; y < yEnd; ++y) {
170 for (int x = 0; x < xEnd; ++x) {
171 // aliases
172 const auto cellIndexBase = utils::ThreeDimensionalMapping::threeToOneD(x, y, z, cellsPerDim);
173 auto &baseCell = cells[cellIndexBase];
174 auto &baseCellsLists = neighborLists[cellIndexBase];
175
176 // Allocate memory for ptr-list pairs for this cell.
177 baseCellsLists.resize(VerletListsCellsHelpers::estimateNumLists(
178 cellIndexBase, useNewton3, cells, offsets,
179 utils::ArrayUtils::static_cast_copy_array<size_t>(cellsPerDim)));
180 // Re-initialize neighbor lists for all particles in the cell, or for as many lists as currently exist if this
181 // is smaller. By "re-initialize", we mean reserving the estimated list length.
182 const size_t minNumParticlesVsNumLists = std::min(baseCell.size(), baseCellsLists.size());
183 for (size_t i = 0; i < minNumParticlesVsNumLists; ++i) {
184 auto &[particlePtr, neighbors] = baseCellsLists[i];
185 particlePtr = &baseCell[i];
186 neighbors.reserve(listLengthEstimate);
187 }
188 // For any remaining particles without (size non-zero) neighbor lists, reserve the estimated list length.
189 for (size_t i = minNumParticlesVsNumLists; i < baseCell.size(); ++i) {
190 baseCellsLists.emplace_back(&baseCell[i], std::vector<Particle_T *>{});
191 baseCellsLists.back().second.reserve(listLengthEstimate);
192 }
193
194 // Build lists for this base step according to predefined cell pairs
195 for (const auto &[offset1, offset2, _] : offsets) {
196 const auto cell1Index1D = cellIndexBase + offset1;
197 const auto cell2Index1D = cellIndexBase + offset2;
198
199 // For all traversals ensures the partner cell is not outside the boundary
200 const auto cell2Index3D = utils::ThreeDimensionalMapping::oneToThreeD(cell2Index1D, cellsPerDim);
201 if (cell2Index3D[0] >= cellsPerDim[0] or cell2Index3D[0] < 0 or cell2Index3D[1] >= cellsPerDim[1] or
202 cell2Index3D[1] < 0 or cell2Index3D[2] >= cellsPerDim[2] or cell2Index3D[2] < 0) {
203 continue;
204 }
205
206 // Skip if both cells only contain halos or dummys
207 if (not(cells[cell1Index1D].getPossibleParticleOwnerships() == OwnershipState::owned) and
208 not(cells[cell2Index1D].getPossibleParticleOwnerships() == OwnershipState::owned)) {
209 continue;
210 }
211
212 // Go over all particle pairs in the two cells and insert close pairs into their respective lists
213 for (size_t particleIndexCell1 = 0; particleIndexCell1 < cells[cell1Index1D].size(); ++particleIndexCell1) {
214 auto &p1 = cells[cell1Index1D][particleIndexCell1];
215
216 // Determine the starting index for the second particle in the pair.
217 // If both cells are the same and the traversal is newton3 compatible, this is the next particle in the
218 // cell. If the cells are different or the traversal is not newton3 compatible, this is index 0. For
219 // non-newton 3 compatible traversals (vlc_c01) we have to consider the interaction in both directions, so
220 // we always start from index 0.
221 size_t startIndexCell2 = 0;
222 if (cell1Index1D == cell2Index1D and vlcTraversalOpt != TraversalOption::vlc_c01) {
223 startIndexCell2 = particleIndexCell1 + 1;
224 }
225
226 for (size_t particleIndexCell2 = startIndexCell2; particleIndexCell2 < cells[cell2Index1D].size();
227 ++particleIndexCell2) {
228 auto &p2 = cells[cell2Index1D][particleIndexCell2];
229 // Ignore dummies and self interaction
230 if (&p1 == &p2 or p1.isDummy() or p2.isDummy()) {
231 continue;
232 }
233
234 // If the distance is less than interaction length add the pair to the list
235 const auto distVec = p2.getR() - p1.getR();
236 const auto distSquared = utils::ArrayMath::dot(distVec, distVec);
237 if (distSquared < interactionLengthSquared) {
238 insert(p1, particleIndexCell1, p2, cell1Index1D, cellIndexBase, baseCellsLists);
239 // If the traversal is Newton3 compatible, Newton3 is used for building regardless of if the actual
240 // interactions will use Newton3. In the case that Newton3 will not be used, the inverse interaction
241 // also needs to be stored in p2's list. If the traversal is Newton3 incompatible, the insertion into
242 // p2's list will occur when the p2 particle is p1. This is ensured above by the startIndexCell2.
243 if (not useNewton3 and not(vlcTraversalOpt == TraversalOption::vlc_c01)) {
244 insert(p2, particleIndexCell2, p1, cell2Index1D, cellIndexBase, baseCellsLists);
245 }
246 }
247 }
248 }
249 }
250 }
251 }
252 }
253
254 // Cleanup: Remove any unused ptr-list pairs to avoid accessing nullptr
255 for (auto &cellLists : neighborLists) {
256 cellLists.erase(std::remove_if(cellLists.begin(), cellLists.end(),
257 [](const auto &pair) {
258 const auto &[particlePtr, neighbors] = pair;
259 return particlePtr == nullptr;
260 }),
261 cellLists.end());
262 }
263 }
264
268 size_t getNumberOfPartners(const Particle_T *particle) const override {
269 for (const auto &cellsLists : _aosNeighborList) {
270 for (const auto &particlesLists : cellsLists) {
271 if (particlesLists.first == particle) {
272 return particlesLists.second.size();
273 }
274 }
275 }
276 return 0lu;
277 }
278
284 return _aosNeighborList;
285 }
286
291 auto &getSoANeighborList() { return _soaNeighborList; }
292
296 void generateSoAFromAoS(LinkedCells<Particle_T> &linkedCells) override {
297 _soaNeighborList.clear();
298
299 // particle pointer to global index of particle
300 std::unordered_map<Particle_T *, size_t> particlePtrToIndex;
301 particlePtrToIndex.reserve(linkedCells.size());
302 size_t i = 0;
303 for (auto iter = linkedCells.begin(IteratorBehavior::ownedOrHaloOrDummy); iter.isValid(); ++iter, ++i) {
304 particlePtrToIndex[&(*iter)] = i;
305 }
306
307 _soaNeighborList.resize(linkedCells.getCells().size());
308
309 // iterate over cells and for each create the soa lists from the aos lists
310 for (size_t firstCellIndex = 0; firstCellIndex < _aosNeighborList.size(); ++firstCellIndex) {
311 const auto &aosLists = _aosNeighborList[firstCellIndex];
312 auto &soaLists = _soaNeighborList[firstCellIndex];
313 soaLists.reserve(aosLists.size());
314
315 // iterate over pairs of particle and neighbor list
316 for (const auto &[particlePtr, neighbors] : aosLists) {
317 // global index of current particle
318 size_t currentParticleGlobalIndex = particlePtrToIndex.at(particlePtr);
319
320 // create SoA neighbor list for current particle
321 std::vector<size_t, autopas::AlignedAllocator<size_t>> currentSoANeighborList{};
322 currentSoANeighborList.reserve(neighbors.size());
323
324 // fill the SoA neighbor list with the indices of the particles from the corresponding AoS neighbor list
325 for (const auto &neighborOfCurrentParticle : neighbors) {
326 currentSoANeighborList.emplace_back(particlePtrToIndex.at(neighborOfCurrentParticle));
327 }
328
329 // add the newly constructed pair of particle index and SoA neighbor list to cell
330 soaLists.emplace_back(currentParticleGlobalIndex, currentSoANeighborList);
331 }
332 }
333 }
334
335 void setUpTraversal(TraversalInterface *traversal) override {
336 auto vTraversal = dynamic_cast<VLCTraversalInterface<Particle_T, VLCAllCellsNeighborList<Particle_T>> *>(traversal);
337
338 if (vTraversal) {
339 vTraversal->setVerletList(*this);
340 } else {
342 "Trying to use a traversal of wrong type in VerletListCells.h. TraversalID: {}",
343 traversal->getTraversalType());
344 }
345 }
346
347 private:
352
357 SoAListType _soaNeighborList{};
358};
359} // namespace autopas
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
double getInteractionLength() const final
Return the interaction length (cutoff+skin) of the container.
Definition: CellBasedParticleContainer.h:89
size_t getNumberOfParticles(IteratorBehavior behavior) const override
Get the number of particles with respect to the specified IteratorBehavior.
Definition: CellBasedParticleContainer.h:113
size_t size() const override
Get the total number of particles saved in the container (owned + halo + dummy).
Definition: CellBasedParticleContainer.h:131
const std::array< double, 3 > & getBoxMin() const final
Get the lower corner of the container without halo.
Definition: CellBasedParticleContainer.h:74
const std::array< double, 3 > & getBoxMax() const final
Get the upper corner of the container without halo.
Definition: CellBasedParticleContainer.h:69
LinkedCells class.
Definition: LinkedCells.h:40
internal::CellBlock3D< ParticleCell > & getCellBlock()
Get the cell block, not supposed to be used except by verlet lists.
Definition: LinkedCells.h:458
ContainerIterator< ParticleType, true, false > begin(IteratorBehavior behavior=autopas::IteratorBehavior::ownedOrHalo, typename ContainerIterator< ParticleType, true, false >::ParticleVecType *additionalVectors=nullptr) override
Iterate over all particles using for(auto iter = container.begin(); iter.isValid(); ++iter) .
Definition: LinkedCells.h:307
std::vector< ParticleCell > & getCells()
Returns a non-const reference to the cell data structure.
Definition: LinkedCells.h:470
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
virtual TraversalOption getTraversalType() const =0
Return a enum representing the name of the traversal class.
Neighbor list to be used with VerletListsCells container.
Definition: VLCAllCellsNeighborList.h:37
void generateSoAFromAoS(LinkedCells< Particle_T > &linkedCells) override
Generates neighbor list in SoA layout from available neighbor list in AoS layout.
Definition: VLCAllCellsNeighborList.h:296
size_t getNumberOfPartners(const Particle_T *particle) const override
Gets the number of neighbors over all neighbor lists that belong to this particle.
Definition: VLCAllCellsNeighborList.h:268
void buildAoSNeighborList(TraversalOption vlcTraversalOpt, LinkedCells< Particle_T > &linkedCells, bool useNewton3)
Builds AoS neighbor list from underlying linked cells object.
Definition: VLCAllCellsNeighborList.h:63
ContainerOption getContainerType() const override
Returns the container type of this neighbor list and the container it belongs to.
Definition: VLCAllCellsNeighborList.h:58
void setUpTraversal(TraversalInterface *traversal) override
Assigns the current traversal to the correct traversal interface.
Definition: VLCAllCellsNeighborList.h:335
VerletListsCellsHelpers::AllCellsNeighborListsType< Particle_T > & getAoSNeighborList()
Returns the neighbor list in AoS layout.
Definition: VLCAllCellsNeighborList.h:283
std::pair< size_t, std::vector< size_t, autopas::AlignedAllocator< size_t > > > SoAPairOfParticleAndList
Helper type definition.
Definition: VLCAllCellsNeighborList.h:47
typename VerletListsCellsHelpers::AllCellsNeighborListsType< Particle_T > ListType
Type of the data structure used to save the neighbor lists.
Definition: VLCAllCellsNeighborList.h:42
typename std::vector< std::vector< SoAPairOfParticleAndList > > SoAListType
Helper type definition.
Definition: VLCAllCellsNeighborList.h:53
auto & getSoANeighborList()
Returns the neighbor list in SoA layout.
Definition: VLCAllCellsNeighborList.h:291
Interface of neighbor lists to be used with VerletListsCells container.
Definition: VLCNeighborListInterface.h:19
void setLinkedCellsPointer(LinkedCells< Particle_T > *linkedCells)
Set the Linked Cells Pointer for this List.
Definition: VLCNeighborListInterface.h:110
This class provides the Traversal Interface for the verlet lists cells container.
Definition: VLCTraversalInterface.h:27
virtual void setVerletList(NeighborList &verlet)
Sets the verlet list for the traversal to iterate over.
Definition: VLCTraversalInterface.h:41
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
std::vector< std::vector< std::pair< Particle_T *, std::vector< Particle_T * > > > > AllCellsNeighborListsType
Cell wise verlet lists for neighbors from all adjacent cells: For every cell, a vector of pairs.
Definition: VerletListsCellsHelpers.h:30
size_t estimateNumLists(size_t baseCellIndex, bool useNewton3, const Cells &cells, const std::vector< BaseStepOffsets > &offsetsC08, const std::array< size_t, 3 > cellsPerDim)
Function to estimate the number of neighbor lists for one base step.
Definition: VerletListsCellsHelpers.h:127
size_t estimateListLength(size_t numParticles, const std::array< double, 3 > &boxSize, double interactionLength, double correctionFactor)
Simple heuristic to calculate the average number of particles per verlet list assuming particles are ...
Definition: VerletListsCellsHelpers.cpp:16
std::vector< BaseStepOffsets > buildBaseStep(const std::array< int, 3 > &cellsPerDim, const TraversalOption traversal)
Builds the list of offsets from the base cell for the c01, c08, and c18 base step.
Definition: VerletListsCellsHelpers.cpp:26
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, 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
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:32
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
@ owned
Owned state, a particle with this state is an actual particle and owned by the current AutoPas object...