AutoPas  3.0.0
Loading...
Searching...
No Matches
VerletListsCells.h
Go to the documentation of this file.
1
7#pragma once
8
21
22namespace autopas {
23
35template <class Particle_T, class NeighborList>
36class VerletListsCells : public VerletListsLinkedBase<Particle_T> {
38
39 public:
52 VerletListsCells(const std::array<double, 3> &boxMin, const std::array<double, 3> &boxMax, const double cutoff,
53 const double skin = 0, const unsigned int rebuildFrequency = 2, const double cellSizeFactor = 1.0,
55 typename VerletListsCellsHelpers::VLCBuildType dataLayoutDuringListRebuild =
56 VerletListsCellsHelpers::VLCBuildType::soaBuild)
57 : VerletListsLinkedBase<Particle_T>(boxMin, boxMax, cutoff, skin, rebuildFrequency,
58 compatibleTraversals::allVLCCompatibleTraversals(), cellSizeFactor),
59 _loadEstimator(loadEstimator),
60 _dataLayoutDuringListRebuild(dataLayoutDuringListRebuild) {}
61
65 [[nodiscard]] ContainerOption getContainerType() const override { return _neighborList.getContainerType(); }
66
72 // (Explicit) static cast required for Apple Clang (last tested version: 17.0.0)
73 switch (static_cast<LoadEstimatorOption::Value>(this->_loadEstimator)) {
75 return [&](const std::array<unsigned long, 3> &cellsPerDimension,
76 const std::array<unsigned long, 3> &lowerCorner, const std::array<unsigned long, 3> &upperCorner) {
77 return loadEstimators::squaredParticlesPerCell((this->_linkedCells).getCells(), cellsPerDimension,
78 lowerCorner, upperCorner);
79 };
80 }
82 return [&](const std::array<unsigned long, 3> &cellsPerDimension,
83 const std::array<unsigned long, 3> &lowerCorner, const std::array<unsigned long, 3> &upperCorner) {
84 return loadEstimators::neighborListLength<Particle_T, NeighborList>(_neighborList, cellsPerDimension,
85 lowerCorner, upperCorner);
86 };
87 }
88
90 [[fallthrough]];
91 default: {
92 return
93 [&](const std::array<unsigned long, 3> &cellsPerDimension, const std::array<unsigned long, 3> &lowerCorner,
94 const std::array<unsigned long, 3> &upperCorner) { return 1; };
95 }
96 }
97 }
98
99 void computeInteractions(TraversalInterface *traversal) override {
100 // Check if traversal is allowed for this container and give it the data it needs.
101 _neighborList.setUpTraversal(traversal);
102 if (auto *balancedTraversal = dynamic_cast<BalancedTraversal *>(traversal)) {
103 balancedTraversal->setLoadEstimator(getLoadEstimatorFunction());
104 }
105
106 traversal->initTraversal();
107 traversal->traverseParticles();
108 traversal->endTraversal();
109 }
110
116 size_t getNumberOfPartners(const Particle_T *particle) const { return _neighborList.getNumberOfPartners(particle); }
117
123 using namespace utils::ArrayMath::literals;
124 // Sanity check.
125 if (this->_linkedCells.getCellBlock().getCellsPerInteractionLength() > 1) {
127 "VerletListsCells::rebuildNeighborListsC08() was called with a CSF < 1 but it only supports CSF>=1.");
128 }
129 // Define some aliases
130 auto &neighborLists = _neighborList.getAoSNeighborList();
131 auto &cells = this->_linkedCells.getCells();
132 const auto interactionLength = this->getInteractionLength();
133 const auto interactionLengthSquared = interactionLength * interactionLength;
134 const auto boxSizeWithHalo = this->_linkedCells.getBoxMax() - this->_linkedCells.getBoxMin() +
135 std::array<double, 3>{interactionLength, interactionLength, interactionLength} * 2.;
136 // Create an estimate for the average length of a neighbor list.
137 // This assumes homogeneous distribution and some overestimation.
138 const auto listLengthEstimate = VerletListsCellsHelpers::estimateListLength(
139 this->_linkedCells.getNumberOfParticles(IteratorBehavior::ownedOrHalo), boxSizeWithHalo, interactionLength,
140 1.3);
141
142 // Reset lists. Don't free any memory, only mark as unused.
143 _neighborList.setLinkedCellsPointer(&this->_linkedCells);
144 for (auto &cellLists : neighborLists) {
145 for (auto &[particlePtr, neighbors] : cellLists) {
146 particlePtr = nullptr;
147 neighbors.clear();
148 }
149 }
150 neighborLists.resize(cells.size());
151
152 /* This must not be a doc comment (with two **) to not confuse doxygen.
153 * Helper function to insert a pointer into a list of the base cell.
154 * It considers the cases that neither particle is in the base cell
155 * and in that case finds or creates the appropriate list.
156 *
157 * @param p1 Reference to source particle.
158 * @param p1Index Index of p1 in its cell.
159 * @param p2 Reference to target particle.
160 * @param cellIndex1 Index of cell where p1 resides.
161 * @param cellIndexBase Index of the base cell of the c08 step.
162 * @param neighborList Reference to the list where the particle pair should be stored.
163 */
164 auto insert = [&](auto &p1, auto p1Index, auto &p2, auto cellIndex1, auto cellIndexBase, auto &neighborList) {
165 // Easy case: cell1 is the base cell
166 if (cellIndexBase == cellIndex1) {
167 neighborList[p1Index].second.push_back(&p2);
168 } else {
169 // Otherwise, check if the base cell already has a list for p1
170 auto iter = std::find_if(neighborList.begin(), neighborList.end(), [&](const auto &pair) {
171 const auto &[particlePtr, list] = pair;
172 return particlePtr == &p1;
173 });
174 // If yes, append p2 to it.
175 if (iter != neighborList.end()) {
176 iter->second.push_back(&p2);
177 } else {
178 // If no, create one (or reuse an empty pair), reserve space for the list and emplace p2
179 if (auto insertLoc = std::find_if(neighborList.begin(), neighborList.end(),
180 [&](const auto &pair) {
181 const auto &[particlePtr, list] = pair;
182 return particlePtr == nullptr;
183 });
184 insertLoc != neighborList.end()) {
185 auto &[particlePtr, neighbors] = *insertLoc;
186 particlePtr = &p1;
187 neighbors.reserve(listLengthEstimate);
188 neighbors.push_back(&p2);
189 } else {
190 neighborList.emplace_back(&p1, std::vector<Particle_T *>{});
191 neighborList.back().second.reserve(listLengthEstimate);
192 neighborList.back().second.push_back(&p2);
193 }
194 }
195 }
196 };
197
198 const auto &cellsPerDim = utils::ArrayUtils::static_cast_copy_array<int>(
199 this->_linkedCells.getCellBlock().getCellsPerDimensionWithHalo());
200 // Vector of offsets from the base cell for the c08 base step
201 // and respective factors for the fraction of particles per cell that need neighbor lists in the base cell.
202 const auto offsetsC08 = VerletListsCellsHelpers::buildC08BaseStep(cellsPerDim);
203
204 // Go over all cells except the very last layer and create lists per base step.
205 // Since there are no loop dependencies merge all for loops and create 10 chunks per thread.
206 AUTOPAS_OPENMP(parallel for collapse(3) schedule(dynamic, std::max(cells.size() / (autopas::autopas_get_max_threads() * 10), 1ul)))
207 for (int z = 0; z < cellsPerDim[2] - 1; ++z) {
208 for (int y = 0; y < cellsPerDim[1] - 1; ++y) {
209 for (int x = 0; x < cellsPerDim[0] - 1; ++x) {
210 // aliases
211 const auto cellIndexBase = utils::ThreeDimensionalMapping::threeToOneD(x, y, z, cellsPerDim);
212 auto &baseCell = cells[cellIndexBase];
213 auto &baseCellsLists = neighborLists[cellIndexBase];
214
215 // Allocate memory for ptr-list pairs for this cell.
216 baseCellsLists.resize(
217 VerletListsCellsHelpers::estimateNumLists(cellIndexBase, this->_verletBuiltNewton3, cells, offsetsC08));
218 // Re-initialize a neighbor list for all particles in the cell but at most for as many as there are lists
219 const size_t minCellSizeVsNumLists = std::min(baseCell.size(), baseCellsLists.size());
220 for (size_t i = 0; i < minCellSizeVsNumLists; ++i) {
221 auto &[particlePtr, neighbors] = baseCellsLists[i];
222 particlePtr = &baseCell[i];
223 neighbors.reserve(listLengthEstimate);
224 }
225 // For any remaining particles create a new list.
226 // This case can only happen if estimateNumLists can return values smaller than baseCell.size()
227 for (size_t i = minCellSizeVsNumLists; i < baseCell.size(); ++i) {
228 baseCellsLists.emplace_back(&baseCell[i], std::vector<Particle_T *>{});
229 baseCellsLists.back().second.reserve(listLengthEstimate);
230 }
231
232 // Build c08 lists for this base step according to predefined cell pairs
233 for (const auto &[offset1, offset2, _] : offsetsC08) {
234 const auto cellIndex1 = cellIndexBase + offset1;
235 const auto cellIndex2 = cellIndexBase + offset2;
236
237 // Skip if both cells only contain halos
238 if (not(cells[cellIndex1].getPossibleParticleOwnerships() == OwnershipState::owned) and
239 not(cells[cellIndex2].getPossibleParticleOwnerships() == OwnershipState::owned)) {
240 continue;
241 }
242
243 // Go over all particle pairs in the two cells and insert close pairs into their respective lists
244 for (size_t particleIndexCell1 = 0; particleIndexCell1 < cells[cellIndex1].size(); ++particleIndexCell1) {
245 auto &p1 = cells[cellIndex1][particleIndexCell1];
246 for (size_t particleIndexCell2 = (cellIndex1 == cellIndex2) ? particleIndexCell1 + 1 : 0;
247 particleIndexCell2 < cells[cellIndex2].size(); ++particleIndexCell2) {
248 auto &p2 = cells[cellIndex2][particleIndexCell2];
249 // Ignore dummies and self interaction
250 if (&p1 == &p2 or p1.isDummy() or p2.isDummy()) {
251 continue;
252 }
253
254 // If the distance is less than interaction length add the pair to the list
255 const auto distVec = p2.getR() - p1.getR();
256 const auto distSquared = utils::ArrayMath::dot(distVec, distVec);
257 if (distSquared < interactionLengthSquared) {
258 insert(p1, particleIndexCell1, p2, cellIndex1, cellIndexBase, baseCellsLists);
259 // If the traversal does not use Newton3 the inverse interaction also needs to be stored in p2's list
260 if (not this->_verletBuiltNewton3) {
261 insert(p2, particleIndexCell2, p1, cellIndex2, cellIndexBase, baseCellsLists);
262 }
263 }
264 }
265 }
266 }
267 }
268 }
269 }
270
271 // Cleanup: Remove any unused ptr-list pairs to avoid accessing nullptr
272 for (auto &cellLists : neighborLists) {
273 cellLists.erase(std::remove_if(cellLists.begin(), cellLists.end(),
274 [](const auto &pair) {
275 const auto &[particlePtr, neighbors] = pair;
276 return particlePtr == nullptr;
277 }),
278 cellLists.end());
279 }
280 }
281
282 void rebuildNeighborLists(TraversalInterface *traversal) override {
283 this->_verletBuiltNewton3 = traversal->getUseNewton3();
284
285 // VLP needs constexpr special case because the types and thus interfaces are slightly different
286 if constexpr (std::is_same_v<NeighborList, VLCCellPairNeighborList<Particle_T>>) {
287 _neighborList.buildAoSNeighborList(this->_linkedCells, this->_verletBuiltNewton3, this->getCutoff(),
288 this->getVerletSkin(), this->getInteractionLength(),
289 traversal->getTraversalType(), _dataLayoutDuringListRebuild);
290
291 } else {
292 // Switch to test different implementations for vlc_c08 list generation
293 if (traversal->getTraversalType() == TraversalOption::vlc_c08) {
295 } else {
296 _neighborList.buildAoSNeighborList(this->_linkedCells, this->_verletBuiltNewton3, this->getCutoff(),
297 this->getVerletSkin(), this->getInteractionLength(),
298 traversal->getTraversalType(), _dataLayoutDuringListRebuild);
299 }
300 }
301
302 if (traversal->getDataLayout() == DataLayoutOption::soa) {
303 _neighborList.generateSoAFromAoS(this->_linkedCells);
304 }
305
306 // the neighbor list is now valid
307 this->_neighborListIsValid.store(true, std::memory_order_relaxed);
308 }
309
314 [[nodiscard]] const std::array<double, 3> &getCellLength() const {
315 return this->_linkedCells.getCellBlock().getCellLength();
316 }
317
318 private:
322 NeighborList _neighborList;
323
327 autopas::LoadEstimatorOption _loadEstimator;
328
332 typename VerletListsCellsHelpers::VLCBuildType _dataLayoutDuringListRebuild;
333};
334} // namespace autopas
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
Base class for traversals utilising load balancing.
Definition: BalancedTraversal.h:19
std::function< unsigned long(const std::array< unsigned long, 3 > &, const std::array< unsigned long, 3 > &, const std::array< unsigned long, 3 > &)> EstimatorFunction
Type signature for load estimators.
Definition: BalancedTraversal.h:26
This class handles the storage of particles in their full form.
Definition: FullParticleCell.h:26
Class representing the load estimator choices.
Definition: LoadEstimatorOption.h:18
Value
Possible choices for the load estimation algorithm.
Definition: LoadEstimatorOption.h:23
@ squaredParticlesPerCell
Number of particles per cell squared.
Definition: LoadEstimatorOption.h:31
@ neighborListLength
Sum of neighbor list lengths.
Definition: LoadEstimatorOption.h:35
@ none
No load estimator.
Definition: LoadEstimatorOption.h:27
This interface serves as a common parent class for all traversals.
Definition: TraversalInterface.h:18
virtual void endTraversal()=0
Finalizes the traversal.
virtual TraversalOption getTraversalType() const =0
Return a enum representing the name of the traversal class.
virtual void traverseParticles()=0
Traverse the particles by pairs, triplets etc.
virtual void initTraversal()=0
Initializes the traversal.
DataLayoutOption getDataLayout() const
Return the data layout option.
Definition: TraversalInterface.h:69
bool getUseNewton3() const
Return whether the traversal uses newton 3.
Definition: TraversalInterface.h:63
Linked Cells with Verlet Lists container.
Definition: VerletListsCells.h:36
void rebuildNeighborLists(TraversalInterface *traversal) override
Rebuilds the neighbor lists for the next traversals.
Definition: VerletListsCells.h:282
VerletListsCells(const std::array< double, 3 > &boxMin, const std::array< double, 3 > &boxMax, const double cutoff, const double skin=0, const unsigned int rebuildFrequency=2, const double cellSizeFactor=1.0, const LoadEstimatorOption loadEstimator=LoadEstimatorOption::squaredParticlesPerCell, typename VerletListsCellsHelpers::VLCBuildType dataLayoutDuringListRebuild=VerletListsCellsHelpers::VLCBuildType::soaBuild)
Constructor of the VerletListsCells class.
Definition: VerletListsCells.h:52
const std::array< double, 3 > & getCellLength() const
Return the cell length of the underlying linked cells structure, normally needed only for unit tests.
Definition: VerletListsCells.h:314
ContainerOption getContainerType() const override
Get the ContainerType.
Definition: VerletListsCells.h:65
size_t getNumberOfPartners(const Particle_T *particle) const
Gets the number of neighbors over all neighbor lists that belong to this particle.
Definition: VerletListsCells.h:116
BalancedTraversal::EstimatorFunction getLoadEstimatorFunction()
Generates the load estimation function depending on _loadEstimator.
Definition: VerletListsCells.h:71
void computeInteractions(TraversalInterface *traversal) override
Iterates over all particle multiples (e.g.
Definition: VerletListsCells.h:99
void rebuildNeighborListsC08()
Special case of building the neighbor lists in c08 style where all lists that belong to one base step...
Definition: VerletListsCells.h:122
Base class for Verlet lists which use an underlying linked cells container.
Definition: VerletListsLinkedBase.h:26
bool _verletBuiltNewton3
specifies if the current verlet list was built for newton3
Definition: VerletListsLinkedBase.h:329
double getVerletSkin() const final
Return the verletSkin of the container verletSkin.
Definition: VerletListsLinkedBase.h:314
double getInteractionLength() const final
Return the interaction length (cutoff+skin) of the container.
Definition: VerletListsLinkedBase.h:319
std::atomic< bool > _neighborListIsValid
specifies if the neighbor list is currently valid
Definition: VerletListsLinkedBase.h:326
LinkedCells< Particle_T > _linkedCells
internal linked cells storage, handles Particle storage and used to build verlet lists
Definition: VerletListsLinkedBase.h:323
double getCutoff() const final
Return the cutoff of the container.
Definition: VerletListsLinkedBase.h:304
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
size_t estimateNumLists(size_t baseCellIndex, bool useNewton3, const Cells &cells, const std::vector< BaseStepOffsets > &offsetsC08)
Function to estimate the number of neighbor lists for one base step.
Definition: VerletListsCellsHelpers.h:122
std::vector< BaseStepOffsets > buildC08BaseStep(const std::array< int, 3 > &cellsPerDim)
Builds the list of offsets from the base cell for the c08 base step.
Definition: VerletListsCellsHelpers.cpp:26
VLCBuildType
Indicates which build functor should be used for the generation of the neighbor list.
Definition: VerletListsCellsHelpers.h:44
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
unsigned long squaredParticlesPerCell(const std::vector< ParticleCell > &cells, const std::array< unsigned long, 3 > &cellsPerDimension, const std::array< unsigned long, 3 > &lowerCorner, const std::array< unsigned long, 3 > &upperCorner)
Sums up the squared number of particles for all cells within region.
Definition: LoadEstimators.h:31
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 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...