AutoPas  3.0.0
Loading...
Searching...
No Matches
RemainderPairwiseInteractionHandler.h
Go to the documentation of this file.
1
7#pragma once
8
9#include <array>
10#include <cmath>
11#include <memory>
12#include <mutex>
13#include <vector>
14
21
22namespace autopas {
23
30template <typename Particle_T>
32 public:
38 std::vector<std::vector<std::vector<std::unique_ptr<std::mutex>>>> &spatialLocks)
39 : _spatialLocks(spatialLocks) {}
40
62 template <bool newton3, class ContainerType, class PairwiseFunctor>
63 void computeRemainderInteractions(PairwiseFunctor *f, ContainerType &container,
64 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
65 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA) {
66 // The following part performs the main remainder traversal. The actual calculation is done in 4 steps carried out
67 // in three helper functions.
68
69 // TraceTimers only run with log level TRACE
70 utils::TraceTimer timerBufferContainer;
71 utils::TraceTimer timerPBufferPBuffer;
72 utils::TraceTimer timerPBufferHBuffer;
73 utils::TraceTimer timerBufferSoAConversion;
74 timerBufferContainer.start();
75
76 // steps 1 & 2.
77 // particleBuffer with all particles close in container
78 // and haloParticleBuffer with owned, close particles in container.
79 // This is always AoS-based because the container particles are found with region iterators,
80 // which don't have an SoA interface.
81 remainderHelperBufferContainerAoS<newton3>(f, container, particleBuffers, haloParticleBuffers);
82
83 timerBufferContainer.stop();
84 timerBufferSoAConversion.start();
85
86 if (useSoA) {
87 // All (halo-)buffer interactions shall happen vectorized, hence, load all buffer data into SoAs
88 for (auto &buffer : particleBuffers) {
89 f->SoALoader(buffer, buffer._particleSoABuffer, 0, false);
90 }
91 for (auto &buffer : haloParticleBuffers) {
92 f->SoALoader(buffer, buffer._particleSoABuffer, 0, false);
93 }
94 }
95
96 timerBufferSoAConversion.stop();
97 timerPBufferPBuffer.start();
98
99 // step 3. particleBuffer with itself and all other buffers
100 remainderHelperBufferBuffer<newton3>(f, particleBuffers, useSoA);
101
102 timerPBufferPBuffer.stop();
103 timerPBufferHBuffer.start();
104
105 // step 4. particleBuffer with haloParticleBuffer
106 remainderHelperBufferHaloBuffer(f, particleBuffers, haloParticleBuffers, useSoA);
107
108 timerPBufferHBuffer.stop();
109 timerBufferSoAConversion.start();
110
111 // unpack particle SoAs. Halo data is not interesting
112 if (useSoA) {
113 for (auto &buffer : particleBuffers) f->SoAExtractor(buffer, buffer._particleSoABuffer, 0);
114 }
115
116 timerBufferSoAConversion.stop();
117
118 AutoPasLog(TRACE, "Timer Buffers <-> Container (1+2): {}", timerBufferContainer.getTotalTime());
119 AutoPasLog(TRACE, "Timer PBuffers<-> PBuffer ( 3): {}", timerPBufferPBuffer.getTotalTime());
120 AutoPasLog(TRACE, "Timer PBuffers<-> HBuffer ( 4): {}", timerPBufferHBuffer.getTotalTime());
121 AutoPasLog(TRACE, "Timer Load and extract SoA buffers: {}", timerBufferSoAConversion.getTotalTime());
122
123 // Note: haloParticleBuffer with itself is NOT needed, as interactions between halo particles are unneeded!
124 }
125
126 private:
130 std::vector<std::vector<std::vector<std::unique_ptr<std::mutex>>>> &_spatialLocks;
131
145 template <bool newton3, class ContainerType, class PairwiseFunctor>
146 void remainderHelperBufferContainerAoS(PairwiseFunctor *f, ContainerType &container,
147 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
148 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
150 using namespace autopas::utils::ArrayMath::literals;
151
152 // Bunch of shorthands
153 const auto cutoff = container.getCutoff();
154 const auto interactionLength = container.getInteractionLength();
155 const auto haloBoxMin = container.getBoxMin() - interactionLength;
156 const auto totalBoxLengthInv = 1. / (container.getBoxMax() + interactionLength - haloBoxMin);
157 const std::array<size_t, 3> spacialLocksPerDim{_spatialLocks.size(), _spatialLocks[0].size(),
158 _spatialLocks[0][0].size()};
159
160 // Helper function to obtain the lock responsible for a given position.
161 // Implemented as lambda because it can reuse a lot of information that is known in this context.
162 const auto getSpacialLock = [&](const std::array<double, 3> &pos) -> std::mutex & {
163 const auto posDistFromLowerCorner = pos - haloBoxMin;
164 const auto relativePos = posDistFromLowerCorner * totalBoxLengthInv;
165 // Lock coordinates are the position scaled by the number of locks
166 const auto lockCoords =
167 static_cast_copy_array<size_t>(static_cast_copy_array<double>(spacialLocksPerDim) * relativePos);
168 return *_spatialLocks[lockCoords[0]][lockCoords[1]][lockCoords[2]];
169 };
170
171 // one halo and particle buffer pair per thread
172 AUTOPAS_OPENMP(parallel for schedule(static, 1) default(shared))
173 for (int bufferId = 0; bufferId < particleBuffers.size(); ++bufferId) {
174 auto &particleBuffer = particleBuffers[bufferId];
175 auto &haloParticleBuffer = haloParticleBuffers[bufferId];
176
177 // 1. particleBuffer with all close particles in container
178 for (auto &&p1 : particleBuffer) {
179 const auto min = p1.getR() - cutoff;
180 const auto max = p1.getR() + cutoff;
181 container.forEachInRegion(
182 [&](auto &p2) {
183 if constexpr (newton3) {
184 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
185 f->AoSFunctor(p1, p2, true);
186 } else {
187 f->AoSFunctor(p1, p2, false);
188 // no need to calculate force enacted on a halo
189 if (not p2.isHalo()) {
190 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
191 f->AoSFunctor(p2, p1, false);
192 }
193 }
194 },
195 min, max, IteratorBehavior::ownedOrHalo);
196 }
197
198 // 2. haloParticleBuffer with owned, close particles in container
199 for (auto &&p1halo : haloParticleBuffer) {
200 const auto min = p1halo.getR() - cutoff;
201 const auto max = p1halo.getR() + cutoff;
202 container.forEachInRegion(
203 [&](auto &p2) {
204 // No need to apply anything to p1halo
205 // -> AoSFunctor(p1, p2, false) not needed as it neither adds force nor Upot (potential energy)
206 // -> newton3 argument needed for correct globals
207 const std::lock_guard<std::mutex> lock(getSpacialLock(p2.getR()));
208 f->AoSFunctor(p2, p1halo, newton3);
209 },
210 min, max, IteratorBehavior::owned);
211 }
212 }
213 }
214
224 template <bool newton3, class PairwiseFunctor>
225 void remainderHelperBufferBuffer(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
226 bool useSoA) {
227 if (useSoA)
228 remainderHelperBufferBufferSoA<newton3>(f, particleBuffers);
229 else
230 remainderHelperBufferBufferAoS<newton3>(f, particleBuffers);
231 }
232
240 template <bool newton3, class PairwiseFunctor>
241 void remainderHelperBufferBufferAoS(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers) {
242 // For all interactions between different buffers we turn newton3 always off,
243 // which ensures that only one thread at a time is writing to a buffer.
244 // This saves expensive locks.
245
246 // We can not use collapse here without locks, otherwise races would occur.
247 AUTOPAS_OPENMP(parallel for)
248 for (size_t bufferIdxI = 0; bufferIdxI < particleBuffers.size(); ++bufferIdxI) {
249 for (size_t bufferIdxJOffset = 0; bufferIdxJOffset < particleBuffers.size(); ++bufferIdxJOffset) {
250 // Let each bufferI use a different starting point for bufferJ to minimize false sharing
251 const auto bufferIdxJ = (bufferIdxI + bufferIdxJOffset) % particleBuffers.size();
252
253 // interact the two buffers
254 if (bufferIdxI == bufferIdxJ) {
255 // CASE Same buffer
256 // Only use Newton3 if it is allowed, and we are working on only one buffer. This avoids data races.
257 const bool useNewton3 = newton3;
258 auto &bufferRef = particleBuffers[bufferIdxI];
259 const auto bufferSize = bufferRef.size();
260 for (auto i = 0; i < bufferSize; ++i) {
261 auto &p1 = bufferRef[i];
262 // If Newton3 is disabled run over the whole buffer, otherwise only what is ahead
263 for (auto j = useNewton3 ? i + 1 : 0; j < bufferSize; ++j) {
264 if (i == j) {
265 continue;
266 }
267 auto &p2 = bufferRef[j];
268 f->AoSFunctor(p1, p2, useNewton3);
269 }
270 }
271 } else {
272 // CASE: Two buffers
273 for (auto &p1 : particleBuffers[bufferIdxI]) {
274 for (auto &p2 : particleBuffers[bufferIdxJ]) {
275 f->AoSFunctor(p1, p2, false);
276 }
277 }
278 }
279 }
280 }
281 }
282
290 template <bool newton3, class PairwiseFunctor>
291 void remainderHelperBufferBufferSoA(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers) {
292 // we can not use collapse here without locks, otherwise races would occur.
293 AUTOPAS_OPENMP(parallel for)
294 for (size_t i = 0; i < particleBuffers.size(); ++i) {
295 for (size_t jj = 0; jj < particleBuffers.size(); ++jj) {
296 auto *particleBufferSoAA = &particleBuffers[i]._particleSoABuffer;
297 // instead of starting every (parallel) iteration i at j == 0 offset them by i to minimize waiting times at
298 // locks
299 const auto j = (i + jj) % particleBuffers.size();
300 if (i == j) {
301 // For buffer interactions where bufferA == bufferB we can always enable newton3 if it is allowed.
302 f->SoAFunctorSingle(*particleBufferSoAA, newton3);
303 } else {
304 // For all interactions between different buffers we turn newton3 always off,
305 // which ensures that only one thread at a time is writing to a buffer. This saves expensive locks.
306 auto *particleBufferSoAB = &particleBuffers[j]._particleSoABuffer;
307 f->SoAFunctorPair(*particleBufferSoAA, *particleBufferSoAB, false);
308 }
309 }
310 }
311 }
312
326 template <class PairwiseFunctor>
327 void remainderHelperBufferHaloBuffer(PairwiseFunctor *f, std::vector<FullParticleCell<Particle_T>> &particleBuffers,
328 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers, bool useSoA) {
329 if (useSoA)
330 remainderHelperBufferHaloBufferSoA(f, particleBuffers, haloParticleBuffers);
331 else
332 remainderHelperBufferHaloBufferAoS(f, particleBuffers, haloParticleBuffers);
333 }
334
342 template <class PairwiseFunctor>
343 void remainderHelperBufferHaloBufferAoS(PairwiseFunctor *f,
344 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
345 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
346 // Here, phase / color based parallelism turned out to be more efficient than tasks
347 AUTOPAS_OPENMP(parallel)
348 for (int interactionOffset = 0; interactionOffset < haloParticleBuffers.size(); ++interactionOffset) {
349 AUTOPAS_OPENMP(for)
350 for (size_t i = 0; i < particleBuffers.size(); ++i) {
351 auto &particleBuffer = particleBuffers[i];
352 auto &haloBuffer = haloParticleBuffers[(i + interactionOffset) % haloParticleBuffers.size()];
353
354 for (auto &p1 : particleBuffer) {
355 for (auto &p2 : haloBuffer) {
356 f->AoSFunctor(p1, p2, false);
357 }
358 }
359 }
360 }
361 }
362
370 template <class PairwiseFunctor>
371 void remainderHelperBufferHaloBufferSoA(PairwiseFunctor *f,
372 std::vector<FullParticleCell<Particle_T>> &particleBuffers,
373 std::vector<FullParticleCell<Particle_T>> &haloParticleBuffers) {
374 // Here, phase / color based parallelism turned out to be more efficient than tasks
375 AUTOPAS_OPENMP(parallel)
376 for (int interactionOffset = 0; interactionOffset < haloParticleBuffers.size(); ++interactionOffset) {
377 AUTOPAS_OPENMP(for)
378 for (size_t i = 0; i < particleBuffers.size(); ++i) {
379 auto &particleBufferSoA = particleBuffers[i]._particleSoABuffer;
380 auto &haloBufferSoA =
381 haloParticleBuffers[(i + interactionOffset) % haloParticleBuffers.size()]._particleSoABuffer;
382 f->SoAFunctorPair(particleBufferSoA, haloBufferSoA, false);
383 }
384 }
385 }
386};
387} // namespace autopas
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
#define AUTOPAS_OPENMP(args)
Empty macro to throw away any arguments.
Definition: WrapOpenMP.h:126
This class handles the storage of particles in their full form.
Definition: FullParticleCell.h:26
void SoALoader(ParticleCell &cell, SoA< SoAArraysType > &soa, size_t offset, bool skipSoAResize)
Copies the AoS data of the given cell in the given soa.
Definition: Functor.h:127
void SoAExtractor(ParticleCell &cell, SoA< SoAArraysType > &soa, size_t offset)
Copies the data stored in the soa back into the cell.
Definition: Functor.h:141
PairwiseFunctor class.
Definition: PairwiseFunctor.h:45
virtual void AoSFunctor(Particle_T &i, Particle_T &j, bool newton3)
PairwiseFunctor for arrays of structures (AoS).
Definition: PairwiseFunctor.h:70
virtual void SoAFunctorPair(SoAView< SoAArraysType > soa1, SoAView< SoAArraysType > soa2, bool newton3)
PairwiseFunctor for structure of arrays (SoA)
Definition: PairwiseFunctor.h:116
virtual void SoAFunctorSingle(SoAView< SoAArraysType > soa, bool newton3)
PairwiseFunctor for structure of arrays (SoA)
Definition: PairwiseFunctor.h:84
Handles pairwise interactions involving particle buffers (particles not yet inserted into the main co...
Definition: RemainderPairwiseInteractionHandler.h:31
void computeRemainderInteractions(PairwiseFunctor *f, ContainerType &container, std::vector< FullParticleCell< Particle_T > > &particleBuffers, std::vector< FullParticleCell< Particle_T > > &haloParticleBuffers, bool useSoA)
Performs the interactions ParticleContainer::computeInteractions() did not cover.
Definition: RemainderPairwiseInteractionHandler.h:63
RemainderPairwiseInteractionHandler(std::vector< std::vector< std::vector< std::unique_ptr< std::mutex > > > > &spatialLocks)
Constructor for RemainderPairwiseInteractionHandler.
Definition: RemainderPairwiseInteractionHandler.h:37
A wrapper around autopas::utils::Timer that only compiles implementation logic if the SPDLOG_ACTIVE_L...
Definition: TraceTimer.h:20
long getTotalTime() const
Get total accumulated time.
Definition: TraceTimer.h:63
void start()
start the timer.
Definition: TraceTimer.h:25
long stop()
Stops the timer and returns the time elapsed in nanoseconds since the last call to start.
Definition: TraceTimer.h:34
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 > min(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Takes elementwise minimum, returns the result.
Definition: ArrayMath.h:62
constexpr std::array< output_t, SIZE > static_cast_copy_array(const std::array< input_t, SIZE > &a)
Creates a new array by performing an element-wise static_cast<>.
Definition: ArrayUtils.h:33
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:34