AutoPas  3.0.0
Loading...
Searching...
No Matches
LJFunctorAVX.h
Go to the documentation of this file.
1
7#pragma once
8#ifndef __AVX__
9#pragma message "Requested to compile LJFunctorAVX but AVX is not available!"
10#else
11#include <immintrin.h>
12#endif
13
14#include <array>
15
23#include "autopas/utils/inBox.h"
24
25namespace mdLib {
26
44template <class Particle_T, bool applyShift = false, bool useMixing = false,
45 autopas::FunctorN3Modes useNewton3 = autopas::FunctorN3Modes::Both, bool calculateGlobals = false,
46 bool countFLOPs = false, bool relevantForTuning = true>
48 : public autopas::PairwiseFunctor<Particle_T, LJFunctorAVX<Particle_T, applyShift, useMixing, useNewton3,
49 calculateGlobals, countFLOPs, relevantForTuning>> {
50 using SoAArraysType = typename Particle_T::SoAArraysType;
51
52 public:
56 LJFunctorAVX() = delete;
57
58 private:
64 explicit LJFunctorAVX(double cutoff, void * /*dummy*/)
65#ifdef __AVX__
66 : autopas::PairwiseFunctor<Particle_T, LJFunctorAVX<Particle_T, applyShift, useMixing, useNewton3,
67 calculateGlobals, countFLOPs, relevantForTuning>>(cutoff),
68 _cutoffSquared{_mm256_set1_pd(cutoff * cutoff)},
69 _cutoffSquaredAoS(cutoff * cutoff),
70 _potentialEnergySum{0.},
71 _virialSum{0., 0., 0.},
72 _aosThreadData(),
73 _postProcessed{false} {
74 if (calculateGlobals) {
75 _aosThreadData.resize(autopas::autopas_get_max_threads());
76 }
77 if constexpr (countFLOPs) {
78 AutoPasLog(DEBUG, "Using LJFunctorAVX with countFLOPs but FLOP counting is not implemented.");
79 }
80 }
81#else
82 : autopas::Functor<Particle_T, LJFunctorAVX<Particle_T, applyShift, useMixing, useNewton3, calculateGlobals,
83 relevantForTuning>>(cutoff) {
84 autopas::utils::ExceptionHandler::exception("AutoPas was compiled without AVX support!");
85 }
86#endif
87 public:
96 explicit LJFunctorAVX(double cutoff) : LJFunctorAVX(cutoff, nullptr) {
97 static_assert(not useMixing,
98 "Mixing without a ParticlePropertiesLibrary is not possible! Use a different constructor or set "
99 "mixing to false.");
100 }
101
108 explicit LJFunctorAVX(double cutoff, ParticlePropertiesLibrary<double, size_t> &particlePropertiesLibrary)
109 : LJFunctorAVX(cutoff, nullptr) {
110 static_assert(useMixing,
111 "Not using Mixing but using a ParticlePropertiesLibrary is not allowed! Use a different constructor "
112 "or set mixing to true.");
113 _PPLibrary = &particlePropertiesLibrary;
114 }
115
116 std::string getName() final { return "LJFunctorAVX"; }
117
118 bool isRelevantForTuning() final { return relevantForTuning; }
119
120 bool allowsNewton3() final {
121 return useNewton3 == autopas::FunctorN3Modes::Newton3Only or useNewton3 == autopas::FunctorN3Modes::Both;
122 }
123
124 bool allowsNonNewton3() final {
125 return useNewton3 == autopas::FunctorN3Modes::Newton3Off or useNewton3 == autopas::FunctorN3Modes::Both;
126 }
127
128 inline void AoSFunctor(Particle_T &i, Particle_T &j, bool newton3) final {
129 using namespace autopas::utils::ArrayMath::literals;
130 if (i.isDummy() or j.isDummy()) {
131 return;
132 }
133 auto sigmaSquared = _sigmaSquaredAoS;
134 auto epsilon24 = _epsilon24AoS;
135 auto shift6 = _shift6AoS;
136 if constexpr (useMixing) {
137 sigmaSquared = _PPLibrary->getMixingSigmaSquared(i.getTypeId(), j.getTypeId());
138 epsilon24 = _PPLibrary->getMixing24Epsilon(i.getTypeId(), j.getTypeId());
139 if constexpr (applyShift) {
140 shift6 = _PPLibrary->getMixingShift6(i.getTypeId(), j.getTypeId());
141 }
142 }
143 auto dr = i.getR() - j.getR();
144 double dr2 = autopas::utils::ArrayMath::dot(dr, dr);
145
146 if (dr2 > _cutoffSquaredAoS) {
147 return;
148 }
149
150 double invdr2 = 1. / dr2;
151 double lj6 = sigmaSquared * invdr2;
152 lj6 = lj6 * lj6 * lj6;
153 double lj12 = lj6 * lj6;
154 double lj12m6 = lj12 - lj6;
155 double fac = epsilon24 * (lj12 + lj12m6) * invdr2;
156 auto f = dr * fac;
157 i.addF(f);
158 if (newton3) {
159 // only if we use newton 3 here, we want to
160 j.subF(f);
161 }
162 if (calculateGlobals) {
163 // We always add the full contribution for each owned particle and divide the sums by 2 in endTraversal().
164 // Potential energy has an additional factor of 6, which is also handled in endTraversal().
165
166 auto virial = dr * f;
167 double potentialEnergy6 = epsilon24 * lj12m6 + shift6;
168
169 const int threadnum = autopas::autopas_get_thread_num();
170 if (i.isOwned()) {
171 _aosThreadData[threadnum].potentialEnergySum += potentialEnergy6;
172 _aosThreadData[threadnum].virialSum += virial;
173 }
174 // for non-newton3 the second particle will be considered in a separate calculation
175 if (newton3 and j.isOwned()) {
176 _aosThreadData[threadnum].potentialEnergySum += potentialEnergy6;
177 _aosThreadData[threadnum].virialSum += virial;
178 }
179 }
180 }
181
186 inline void SoAFunctorSingle(autopas::SoAView<SoAArraysType> soa, bool newton3) final { SoAFunctorSingleImpl(soa); }
187
192 const bool newton3) final {
193 if (newton3) {
194 SoAFunctorPairImpl<true>(soa1, soa2);
195 } else {
196 SoAFunctorPairImpl<false>(soa1, soa2);
197 }
198 }
199
200 private:
205 inline void SoAFunctorSingleImpl(autopas::SoAView<SoAArraysType> soa) {
206#ifdef __AVX__
207 if (soa.size() == 0) return;
208
209 const auto *const __restrict xptr = soa.template begin<Particle_T::AttributeNames::posX>();
210 const auto *const __restrict yptr = soa.template begin<Particle_T::AttributeNames::posY>();
211 const auto *const __restrict zptr = soa.template begin<Particle_T::AttributeNames::posZ>();
212
213 const auto *const __restrict ownedStatePtr = soa.template begin<Particle_T::AttributeNames::ownershipState>();
214
215 auto *const __restrict fxptr = soa.template begin<Particle_T::AttributeNames::forceX>();
216 auto *const __restrict fyptr = soa.template begin<Particle_T::AttributeNames::forceY>();
217 auto *const __restrict fzptr = soa.template begin<Particle_T::AttributeNames::forceZ>();
218
219 const auto *const __restrict typeIDptr = soa.template begin<Particle_T::AttributeNames::typeId>();
220
221 __m256d virialSumX = _mm256_setzero_pd();
222 __m256d virialSumY = _mm256_setzero_pd();
223 __m256d virialSumZ = _mm256_setzero_pd();
224 __m256d potentialEnergySum = _mm256_setzero_pd();
225
226 // reverse outer loop s.th. inner loop always beginns at aligned array start
227 // typecast to detect underflow
228 for (size_t i = soa.size() - 1; (long)i >= 0; --i) {
229 if (ownedStatePtr[i] == autopas::OwnershipState::dummy) {
230 // If the i-th particle is a dummy, skip this loop iteration.
231 continue;
232 }
233
234 static_assert(std::is_same_v<std::underlying_type_t<autopas::OwnershipState>, int64_t>,
235 "OwnershipStates underlying type should be int64_t!");
236 // ownedStatePtr contains int64_t, so we broadcast these to make an __m256i.
237 // _mm256_set1_epi64x broadcasts a 64-bit integer, we use this instruction to have 4 values!
238 __m256i ownedStateI = _mm256_set1_epi64x(static_cast<int64_t>(ownedStatePtr[i]));
239
240 __m256d fxacc = _mm256_setzero_pd();
241 __m256d fyacc = _mm256_setzero_pd();
242 __m256d fzacc = _mm256_setzero_pd();
243
244 const __m256d x1 = _mm256_broadcast_sd(&xptr[i]);
245 const __m256d y1 = _mm256_broadcast_sd(&yptr[i]);
246 const __m256d z1 = _mm256_broadcast_sd(&zptr[i]);
247
248 size_t j = 0;
249 // floor soa numParticles to multiple of vecLength
250 // If b is a power of 2 the following holds:
251 // a & ~(b -1) == a - (a mod b)
252 for (; j < (i & ~(vecLength - 1)); j += 4) {
253 SoAKernel<true, false>(j, ownedStateI, reinterpret_cast<const int64_t *>(ownedStatePtr), x1, y1, z1, xptr, yptr,
254 zptr, fxptr, fyptr, fzptr, &typeIDptr[i], &typeIDptr[j], fxacc, fyacc, fzacc,
255 &virialSumX, &virialSumY, &virialSumZ, &potentialEnergySum, 0);
256 }
257 // If b is a power of 2 the following holds:
258 // a & (b -1) == a mod b
259 const int rest = (int)(i & (vecLength - 1));
260 if (rest > 0) {
261 SoAKernel<true, true>(j, ownedStateI, reinterpret_cast<const int64_t *>(ownedStatePtr), x1, y1, z1, xptr, yptr,
262 zptr, fxptr, fyptr, fzptr, &typeIDptr[i], &typeIDptr[j], fxacc, fyacc, fzacc, &virialSumX,
263 &virialSumY, &virialSumZ, &potentialEnergySum, rest);
264 }
265
266 // horizontally reduce fDacc to sumfD
267 const __m256d hSumfxfy = _mm256_hadd_pd(fxacc, fyacc);
268 const __m256d hSumfz = _mm256_hadd_pd(fzacc, fzacc);
269
270 const __m128d hSumfxfyLow = _mm256_extractf128_pd(hSumfxfy, 0);
271 const __m128d hSumfzLow = _mm256_extractf128_pd(hSumfz, 0);
272
273 const __m128d hSumfxfyHigh = _mm256_extractf128_pd(hSumfxfy, 1);
274 const __m128d hSumfzHigh = _mm256_extractf128_pd(hSumfz, 1);
275
276 const __m128d sumfxfyVEC = _mm_add_pd(hSumfxfyLow, hSumfxfyHigh);
277 const __m128d sumfzVEC = _mm_add_pd(hSumfzLow, hSumfzHigh);
278
279 const double sumfx = sumfxfyVEC[0];
280 const double sumfy = sumfxfyVEC[1];
281 const double sumfz = _mm_cvtsd_f64(sumfzVEC);
282
283 fxptr[i] += sumfx;
284 fyptr[i] += sumfy;
285 fzptr[i] += sumfz;
286 }
287
288 if constexpr (calculateGlobals) {
289 const int threadnum = autopas::autopas_get_thread_num();
290
291 // horizontally reduce virialSumX and virialSumY
292 const __m256d hSumVirialxy = _mm256_hadd_pd(virialSumX, virialSumY);
293 const __m128d hSumVirialxyLow = _mm256_extractf128_pd(hSumVirialxy, 0);
294 const __m128d hSumVirialxyHigh = _mm256_extractf128_pd(hSumVirialxy, 1);
295 const __m128d hSumVirialxyVec = _mm_add_pd(hSumVirialxyHigh, hSumVirialxyLow);
296
297 // horizontally reduce virialSumZ and potentialEnergySum
298 const __m256d hSumVirialzPotentialEnergy = _mm256_hadd_pd(virialSumZ, potentialEnergySum);
299 const __m128d hSumVirialzPotentialEnergyLow = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 0);
300 const __m128d hSumVirialzPotentialEnergyHigh = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 1);
301 const __m128d hSumVirialzPotentialEnergyVec =
302 _mm_add_pd(hSumVirialzPotentialEnergyHigh, hSumVirialzPotentialEnergyLow);
303
304 // globals = {virialX, virialY, virialZ, potentialEnergy}
305 double globals[4];
306 _mm_store_pd(&globals[0], hSumVirialxyVec);
307 _mm_store_pd(&globals[2], hSumVirialzPotentialEnergyVec);
308
309 _aosThreadData[threadnum].virialSum[0] += globals[0];
310 _aosThreadData[threadnum].virialSum[1] += globals[1];
311 _aosThreadData[threadnum].virialSum[2] += globals[2];
312 _aosThreadData[threadnum].potentialEnergySum += globals[3];
313 }
314#endif
315 }
316
317 template <bool newton3>
318 inline void SoAFunctorPairImpl(autopas::SoAView<SoAArraysType> soa1, autopas::SoAView<SoAArraysType> soa2) {
319#ifdef __AVX__
320 if (soa1.size() == 0 || soa2.size() == 0) return;
321
322 const auto *const __restrict x1ptr = soa1.template begin<Particle_T::AttributeNames::posX>();
323 const auto *const __restrict y1ptr = soa1.template begin<Particle_T::AttributeNames::posY>();
324 const auto *const __restrict z1ptr = soa1.template begin<Particle_T::AttributeNames::posZ>();
325 const auto *const __restrict x2ptr = soa2.template begin<Particle_T::AttributeNames::posX>();
326 const auto *const __restrict y2ptr = soa2.template begin<Particle_T::AttributeNames::posY>();
327 const auto *const __restrict z2ptr = soa2.template begin<Particle_T::AttributeNames::posZ>();
328
329 const auto *const __restrict ownedStatePtr1 = soa1.template begin<Particle_T::AttributeNames::ownershipState>();
330 const auto *const __restrict ownedStatePtr2 = soa2.template begin<Particle_T::AttributeNames::ownershipState>();
331
332 auto *const __restrict fx1ptr = soa1.template begin<Particle_T::AttributeNames::forceX>();
333 auto *const __restrict fy1ptr = soa1.template begin<Particle_T::AttributeNames::forceY>();
334 auto *const __restrict fz1ptr = soa1.template begin<Particle_T::AttributeNames::forceZ>();
335 auto *const __restrict fx2ptr = soa2.template begin<Particle_T::AttributeNames::forceX>();
336 auto *const __restrict fy2ptr = soa2.template begin<Particle_T::AttributeNames::forceY>();
337 auto *const __restrict fz2ptr = soa2.template begin<Particle_T::AttributeNames::forceZ>();
338
339 const auto *const __restrict typeID1ptr = soa1.template begin<Particle_T::AttributeNames::typeId>();
340 const auto *const __restrict typeID2ptr = soa2.template begin<Particle_T::AttributeNames::typeId>();
341
342 __m256d virialSumX = _mm256_setzero_pd();
343 __m256d virialSumY = _mm256_setzero_pd();
344 __m256d virialSumZ = _mm256_setzero_pd();
345 __m256d potentialEnergySum = _mm256_setzero_pd();
346
347 for (unsigned int i = 0; i < soa1.size(); ++i) {
348 if (ownedStatePtr1[i] == autopas::OwnershipState::dummy) {
349 // If the i-th particle is a dummy, skip this loop iteration.
350 continue;
351 }
352
353 __m256d fxacc = _mm256_setzero_pd();
354 __m256d fyacc = _mm256_setzero_pd();
355 __m256d fzacc = _mm256_setzero_pd();
356
357 static_assert(std::is_same_v<std::underlying_type_t<autopas::OwnershipState>, int64_t>,
358 "OwnershipStates underlying type should be int64_t!");
359 // ownedStatePtr1 contains int64_t, so we broadcast these to make an __m256i.
360 // _mm256_set1_epi64x broadcasts a 64-bit integer, we use this instruction to have 4 values!
361 __m256i ownedStateI = _mm256_set1_epi64x(static_cast<int64_t>(ownedStatePtr1[i]));
362
363 const __m256d x1 = _mm256_broadcast_sd(&x1ptr[i]);
364 const __m256d y1 = _mm256_broadcast_sd(&y1ptr[i]);
365 const __m256d z1 = _mm256_broadcast_sd(&z1ptr[i]);
366
367 // floor soa2 numParticles to multiple of vecLength
368 unsigned int j = 0;
369 for (; j < (soa2.size() & ~(vecLength - 1)); j += 4) {
370 SoAKernel<newton3, false>(j, ownedStateI, reinterpret_cast<const int64_t *>(ownedStatePtr2), x1, y1, z1, x2ptr,
371 y2ptr, z2ptr, fx2ptr, fy2ptr, fz2ptr, &typeID1ptr[i], &typeID2ptr[j], fxacc, fyacc,
372 fzacc, &virialSumX, &virialSumY, &virialSumZ, &potentialEnergySum, 0);
373 }
374 const int rest = (int)(soa2.size() & (vecLength - 1));
375 if (rest > 0)
376 SoAKernel<newton3, true>(j, ownedStateI, reinterpret_cast<const int64_t *>(ownedStatePtr2), x1, y1, z1, x2ptr,
377 y2ptr, z2ptr, fx2ptr, fy2ptr, fz2ptr, &typeID1ptr[i], &typeID2ptr[j], fxacc, fyacc,
378 fzacc, &virialSumX, &virialSumY, &virialSumZ, &potentialEnergySum, rest);
379
380 // horizontally reduce fDacc to sumfD
381 const __m256d hSumfxfy = _mm256_hadd_pd(fxacc, fyacc);
382 const __m256d hSumfz = _mm256_hadd_pd(fzacc, fzacc);
383
384 const __m128d hSumfxfyLow = _mm256_extractf128_pd(hSumfxfy, 0);
385 const __m128d hSumfzLow = _mm256_extractf128_pd(hSumfz, 0);
386
387 const __m128d hSumfxfyHigh = _mm256_extractf128_pd(hSumfxfy, 1);
388 const __m128d hSumfzHigh = _mm256_extractf128_pd(hSumfz, 1);
389
390 const __m128d sumfxfyVEC = _mm_add_pd(hSumfxfyLow, hSumfxfyHigh);
391 const __m128d sumfzVEC = _mm_add_pd(hSumfzLow, hSumfzHigh);
392
393 const double sumfx = sumfxfyVEC[0];
394 const double sumfy = sumfxfyVEC[1];
395 const double sumfz = _mm_cvtsd_f64(sumfzVEC);
396
397 fx1ptr[i] += sumfx;
398 fy1ptr[i] += sumfy;
399 fz1ptr[i] += sumfz;
400 }
401
402 if constexpr (calculateGlobals) {
403 const int threadnum = autopas::autopas_get_thread_num();
404
405 // horizontally reduce virialSumX and virialSumY
406 const __m256d hSumVirialxy = _mm256_hadd_pd(virialSumX, virialSumY);
407 const __m128d hSumVirialxyLow = _mm256_extractf128_pd(hSumVirialxy, 0);
408 const __m128d hSumVirialxyHigh = _mm256_extractf128_pd(hSumVirialxy, 1);
409 const __m128d hSumVirialxyVec = _mm_add_pd(hSumVirialxyHigh, hSumVirialxyLow);
410
411 // horizontally reduce virialSumZ and potentialEnergySum
412 const __m256d hSumVirialzPotentialEnergy = _mm256_hadd_pd(virialSumZ, potentialEnergySum);
413 const __m128d hSumVirialzPotentialEnergyLow = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 0);
414 const __m128d hSumVirialzPotentialEnergyHigh = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 1);
415 const __m128d hSumVirialzPotentialEnergyVec =
416 _mm_add_pd(hSumVirialzPotentialEnergyHigh, hSumVirialzPotentialEnergyLow);
417
418 // globals = {virialX, virialY, virialZ, potentialEnergy}
419 double globals[4];
420 _mm_store_pd(&globals[0], hSumVirialxyVec);
421 _mm_store_pd(&globals[2], hSumVirialzPotentialEnergyVec);
422
423 _aosThreadData[threadnum].virialSum[0] += globals[0];
424 _aosThreadData[threadnum].virialSum[1] += globals[1];
425 _aosThreadData[threadnum].virialSum[2] += globals[2];
426 _aosThreadData[threadnum].potentialEnergySum += globals[3];
427 }
428#endif
429 }
430
431#ifdef __AVX__
461 template <bool newton3, bool remainderIsMasked>
462 inline void SoAKernel(const size_t j, const __m256i ownedStateI, const int64_t *const __restrict ownedStatePtr2,
463 const __m256d &x1, const __m256d &y1, const __m256d &z1, const double *const __restrict x2ptr,
464 const double *const __restrict y2ptr, const double *const __restrict z2ptr,
465 double *const __restrict fx2ptr, double *const __restrict fy2ptr,
466 double *const __restrict fz2ptr, const size_t *const typeID1ptr, const size_t *const typeID2ptr,
467 __m256d &fxacc, __m256d &fyacc, __m256d &fzacc, __m256d *virialSumX, __m256d *virialSumY,
468 __m256d *virialSumZ, __m256d *potentialEnergySum, const unsigned int rest = 0) {
469 __m256d epsilon24s = _epsilon24;
470 __m256d sigmaSquareds = _sigmaSquared;
471 __m256d shift6s = _shift6;
472 if (useMixing) {
473 // the first argument for set lands in the last bits of the register
474 epsilon24s = _mm256_set_pd(
475 not remainderIsMasked or rest > 3 ? _PPLibrary->getMixing24Epsilon(*typeID1ptr, *(typeID2ptr + 3)) : 0,
476 not remainderIsMasked or rest > 2 ? _PPLibrary->getMixing24Epsilon(*typeID1ptr, *(typeID2ptr + 2)) : 0,
477 not remainderIsMasked or rest > 1 ? _PPLibrary->getMixing24Epsilon(*typeID1ptr, *(typeID2ptr + 1)) : 0,
478 _PPLibrary->getMixing24Epsilon(*typeID1ptr, *(typeID2ptr + 0)));
479 sigmaSquareds = _mm256_set_pd(
480 not remainderIsMasked or rest > 3 ? _PPLibrary->getMixingSigmaSquared(*typeID1ptr, *(typeID2ptr + 3)) : 0,
481 not remainderIsMasked or rest > 2 ? _PPLibrary->getMixingSigmaSquared(*typeID1ptr, *(typeID2ptr + 2)) : 0,
482 not remainderIsMasked or rest > 1 ? _PPLibrary->getMixingSigmaSquared(*typeID1ptr, *(typeID2ptr + 1)) : 0,
483 _PPLibrary->getMixingSigmaSquared(*typeID1ptr, *(typeID2ptr + 0)));
484 if constexpr (applyShift) {
485 shift6s = _mm256_set_pd(
486 (not remainderIsMasked or rest > 3) ? _PPLibrary->getMixingShift6(*typeID1ptr, *(typeID2ptr + 3)) : 0,
487 (not remainderIsMasked or rest > 2) ? _PPLibrary->getMixingShift6(*typeID1ptr, *(typeID2ptr + 2)) : 0,
488 (not remainderIsMasked or rest > 1) ? _PPLibrary->getMixingShift6(*typeID1ptr, *(typeID2ptr + 1)) : 0,
489 _PPLibrary->getMixingShift6(*typeID1ptr, *(typeID2ptr + 0)));
490 }
491 }
492
493 const __m256d x2 = remainderIsMasked ? _mm256_maskload_pd(&x2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&x2ptr[j]);
494 const __m256d y2 = remainderIsMasked ? _mm256_maskload_pd(&y2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&y2ptr[j]);
495 const __m256d z2 = remainderIsMasked ? _mm256_maskload_pd(&z2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&z2ptr[j]);
496
497 const __m256d drx = _mm256_sub_pd(x1, x2);
498 const __m256d dry = _mm256_sub_pd(y1, y2);
499 const __m256d drz = _mm256_sub_pd(z1, z2);
500
501 const __m256d drx2 = _mm256_mul_pd(drx, drx);
502 const __m256d dry2 = _mm256_mul_pd(dry, dry);
503 const __m256d drz2 = _mm256_mul_pd(drz, drz);
504
505 const __m256d dr2PART = _mm256_add_pd(drx2, dry2);
506 const __m256d dr2 = _mm256_add_pd(dr2PART, drz2);
507
508 // _CMP_LE_OS == Less-Equal-then (ordered, signaling)
509 // signaling = throw error if NaN is encountered
510 // dr2 <= _cutoffSquared ? 0xFFFFFFFFFFFFFFFF : 0
511 const __m256d cutoffMask = _mm256_cmp_pd(dr2, _cutoffSquared, _CMP_LE_OS);
512
513 // This requires that dummy is zero (otherwise when loading using a mask the owned state will not be zero)
514 const __m256i ownedStateJ = remainderIsMasked
515 ? _mm256_castpd_si256(_mm256_maskload_pd(
516 reinterpret_cast<double const *>(&ownedStatePtr2[j]), _masks[rest - 1]))
517 : _mm256_loadu_si256(reinterpret_cast<const __m256i *>(&ownedStatePtr2[j]));
518 // This requires that dummy is the first entry in OwnershipState!
519 const __m256d dummyMask = _mm256_cmp_pd(_mm256_castsi256_pd(ownedStateJ), _zero, _CMP_NEQ_UQ);
520 const __m256d cutoffDummyMask = _mm256_and_pd(cutoffMask, dummyMask);
521
522 // if everything is masked away return from this function.
523 if (_mm256_movemask_pd(cutoffDummyMask) == 0) {
524 return;
525 }
526
527 const __m256d invdr2 = _mm256_div_pd(_one, dr2);
528 const __m256d lj2 = _mm256_mul_pd(sigmaSquareds, invdr2);
529 const __m256d lj4 = _mm256_mul_pd(lj2, lj2);
530 const __m256d lj6 = _mm256_mul_pd(lj2, lj4);
531 const __m256d lj12 = _mm256_mul_pd(lj6, lj6);
532 const __m256d lj12m6 = _mm256_sub_pd(lj12, lj6);
533 const __m256d lj12m6alj12 = _mm256_add_pd(lj12m6, lj12);
534 const __m256d lj12m6alj12e = _mm256_mul_pd(lj12m6alj12, epsilon24s);
535 const __m256d fac = _mm256_mul_pd(lj12m6alj12e, invdr2);
536
537 const __m256d facMasked =
538 remainderIsMasked ? _mm256_and_pd(fac, _mm256_and_pd(cutoffDummyMask, _mm256_castsi256_pd(_masks[rest - 1])))
539 : _mm256_and_pd(fac, cutoffDummyMask);
540
541 const __m256d fx = _mm256_mul_pd(drx, facMasked);
542 const __m256d fy = _mm256_mul_pd(dry, facMasked);
543 const __m256d fz = _mm256_mul_pd(drz, facMasked);
544
545 fxacc = _mm256_add_pd(fxacc, fx);
546 fyacc = _mm256_add_pd(fyacc, fy);
547 fzacc = _mm256_add_pd(fzacc, fz);
548
549 // if newton 3 is used subtract fD from particle j
550 if constexpr (newton3) {
551 const __m256d fx2 =
552 remainderIsMasked ? _mm256_maskload_pd(&fx2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&fx2ptr[j]);
553 const __m256d fy2 =
554 remainderIsMasked ? _mm256_maskload_pd(&fy2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&fy2ptr[j]);
555 const __m256d fz2 =
556 remainderIsMasked ? _mm256_maskload_pd(&fz2ptr[j], _masks[rest - 1]) : _mm256_loadu_pd(&fz2ptr[j]);
557
558 const __m256d fx2new = _mm256_sub_pd(fx2, fx);
559 const __m256d fy2new = _mm256_sub_pd(fy2, fy);
560 const __m256d fz2new = _mm256_sub_pd(fz2, fz);
561
562 remainderIsMasked ? _mm256_maskstore_pd(&fx2ptr[j], _masks[rest - 1], fx2new)
563 : _mm256_storeu_pd(&fx2ptr[j], fx2new);
564 remainderIsMasked ? _mm256_maskstore_pd(&fy2ptr[j], _masks[rest - 1], fy2new)
565 : _mm256_storeu_pd(&fy2ptr[j], fy2new);
566 remainderIsMasked ? _mm256_maskstore_pd(&fz2ptr[j], _masks[rest - 1], fz2new)
567 : _mm256_storeu_pd(&fz2ptr[j], fz2new);
568 }
569
570 if constexpr (calculateGlobals) {
571 // Global Virial
572 const __m256d virialX = _mm256_mul_pd(fx, drx);
573 const __m256d virialY = _mm256_mul_pd(fy, dry);
574 const __m256d virialZ = _mm256_mul_pd(fz, drz);
575
576 // Global Potential
577 const __m256d potentialEnergy = wrapperFMA(epsilon24s, lj12m6, shift6s);
578
579 const __m256d potentialEnergyMasked =
580 remainderIsMasked
581 ? _mm256_and_pd(potentialEnergy, _mm256_and_pd(cutoffDummyMask, _mm256_castsi256_pd(_masks[rest - 1])))
582 : _mm256_and_pd(potentialEnergy, cutoffDummyMask);
583
584 __m256d ownedMaskI =
585 _mm256_cmp_pd(_mm256_castsi256_pd(ownedStateI), _mm256_castsi256_pd(_ownedStateOwnedMM256i), _CMP_EQ_UQ);
586 __m256d energyFactor = _mm256_blendv_pd(_zero, _one, ownedMaskI);
587 if constexpr (newton3) {
588 __m256d ownedMaskJ =
589 _mm256_cmp_pd(_mm256_castsi256_pd(ownedStateJ), _mm256_castsi256_pd(_ownedStateOwnedMM256i), _CMP_EQ_UQ);
590 energyFactor = _mm256_add_pd(energyFactor, _mm256_blendv_pd(_zero, _one, ownedMaskJ));
591 }
592 *potentialEnergySum = wrapperFMA(energyFactor, potentialEnergyMasked, *potentialEnergySum);
593 *virialSumX = wrapperFMA(energyFactor, virialX, *virialSumX);
594 *virialSumY = wrapperFMA(energyFactor, virialY, *virialSumY);
595 *virialSumZ = wrapperFMA(energyFactor, virialZ, *virialSumZ);
596 }
597 }
598#endif
599
600 public:
604 inline void SoAFunctorVerlet(autopas::SoAView<SoAArraysType> soa, const size_t indexFirst,
605 std::span<const size_t> neighborList, bool newton3) final {
606 if (soa.size() == 0 or neighborList.empty()) return;
607 if (newton3) {
608 SoAFunctorVerletImpl<true>(soa, indexFirst, neighborList);
609 } else {
610 SoAFunctorVerletImpl<false>(soa, indexFirst, neighborList);
611 }
612 }
613
614 private:
615 template <bool newton3>
616 inline void SoAFunctorVerletImpl(autopas::SoAView<SoAArraysType> soa, const size_t indexFirst,
617 std::span<const size_t> neighborList) {
618#ifdef __AVX__
619 const auto *const __restrict ownedStatePtr = soa.template begin<Particle_T::AttributeNames::ownershipState>();
620 if (ownedStatePtr[indexFirst] == autopas::OwnershipState::dummy) {
621 return;
622 }
623
624 const auto *const __restrict xptr = soa.template begin<Particle_T::AttributeNames::posX>();
625 const auto *const __restrict yptr = soa.template begin<Particle_T::AttributeNames::posY>();
626 const auto *const __restrict zptr = soa.template begin<Particle_T::AttributeNames::posZ>();
627
628 auto *const __restrict fxptr = soa.template begin<Particle_T::AttributeNames::forceX>();
629 auto *const __restrict fyptr = soa.template begin<Particle_T::AttributeNames::forceY>();
630 auto *const __restrict fzptr = soa.template begin<Particle_T::AttributeNames::forceZ>();
631
632 const auto *const __restrict typeIDptr = soa.template begin<Particle_T::AttributeNames::typeId>();
633
634 // accumulators
635 __m256d virialSumX = _mm256_setzero_pd();
636 __m256d virialSumY = _mm256_setzero_pd();
637 __m256d virialSumZ = _mm256_setzero_pd();
638 __m256d potentialEnergySum = _mm256_setzero_pd();
639 __m256d fxacc = _mm256_setzero_pd();
640 __m256d fyacc = _mm256_setzero_pd();
641 __m256d fzacc = _mm256_setzero_pd();
642
643 // broadcast particle 1
644 const auto x1 = _mm256_broadcast_sd(&xptr[indexFirst]);
645 const auto y1 = _mm256_broadcast_sd(&yptr[indexFirst]);
646 const auto z1 = _mm256_broadcast_sd(&zptr[indexFirst]);
647 // ownedStatePtr contains int64_t, so we broadcast these to make an __m256i.
648 // _mm256_set1_epi64x broadcasts a 64-bit integer, we use this instruction to have 4 values!
649 __m256i ownedStateI = _mm256_set1_epi64x(static_cast<int64_t>(ownedStatePtr[indexFirst]));
650
651 alignas(64) std::array<double, vecLength> x2tmp{};
652 alignas(64) std::array<double, vecLength> y2tmp{};
653 alignas(64) std::array<double, vecLength> z2tmp{};
654 alignas(64) std::array<double, vecLength> fx2tmp{};
655 alignas(64) std::array<double, vecLength> fy2tmp{};
656 alignas(64) std::array<double, vecLength> fz2tmp{};
657 alignas(64) std::array<size_t, vecLength> typeID2tmp{};
658 alignas(64) std::array<autopas::OwnershipState, vecLength> ownedStates2tmp{};
659
660 // load 4 neighbors
661 size_t j = 0;
662 // Loop over all neighbors as long as we can fill full vectors
663 // (until `neighborListSize - neighborListSize % vecLength`)
664 //
665 // If b is a power of 2 the following holds:
666 // a & ~(b - 1) == a - (a mod b)
667 const size_t neighborListSize = neighborList.size();
668 for (; j < (neighborListSize & ~(vecLength - 1)); j += vecLength) {
669 // AVX2 variant:
670 // create buffer for 4 interaction particles
671 // and fill buffers via gathering
672 // const __m256d x2tmp = _mm256_i64gather_pd(&xptr[j], _vindex, 1);
673 // const __m256d y2tmp = _mm256_i64gather_pd(&yptr[j], _vindex, 1);
674 // const __m256d z2tmp = _mm256_i64gather_pd(&zptr[j], _vindex, 1);
675 // const __m256d fx2tmp = _mm256_i64gather_pd(&fxptr[j], _vindex, 1);
676 // const __m256d fy2tmp = _mm256_i64gather_pd(&fyptr[j], _vindex, 1);
677 // const __m256d fz2tmp = _mm256_i64gather_pd(&fzptr[j], _vindex, 1);
678 // const __m256i typeID2tmp = _mm256_i64gather_epi64(&typeIDptr[j], _vindex, 1);
679
680 for (size_t vecIndex = 0; vecIndex < vecLength; ++vecIndex) {
681 x2tmp[vecIndex] = xptr[neighborList[j + vecIndex]];
682 y2tmp[vecIndex] = yptr[neighborList[j + vecIndex]];
683 z2tmp[vecIndex] = zptr[neighborList[j + vecIndex]];
684 if constexpr (newton3) {
685 fx2tmp[vecIndex] = fxptr[neighborList[j + vecIndex]];
686 fy2tmp[vecIndex] = fyptr[neighborList[j + vecIndex]];
687 fz2tmp[vecIndex] = fzptr[neighborList[j + vecIndex]];
688 }
689 typeID2tmp[vecIndex] = typeIDptr[neighborList[j + vecIndex]];
690 ownedStates2tmp[vecIndex] = ownedStatePtr[neighborList[j + vecIndex]];
691 }
692
693 SoAKernel<newton3, false>(0, ownedStateI, reinterpret_cast<const int64_t *>(ownedStates2tmp.data()), x1, y1, z1,
694 x2tmp.data(), y2tmp.data(), z2tmp.data(), fx2tmp.data(), fy2tmp.data(), fz2tmp.data(),
695 &typeIDptr[indexFirst], typeID2tmp.data(), fxacc, fyacc, fzacc, &virialSumX,
696 &virialSumY, &virialSumZ, &potentialEnergySum, 0);
697
698 if constexpr (newton3) {
699 for (size_t vecIndex = 0; vecIndex < vecLength; ++vecIndex) {
700 fxptr[neighborList[j + vecIndex]] = fx2tmp[vecIndex];
701 fyptr[neighborList[j + vecIndex]] = fy2tmp[vecIndex];
702 fzptr[neighborList[j + vecIndex]] = fz2tmp[vecIndex];
703 }
704 }
705 }
706 // Remainder loop
707 // If b is a power of 2 the following holds:
708 // a & (b - 1) == a mod b
709 const auto rest = static_cast<int>(neighborListSize & (vecLength - 1));
710 if (rest > 0) {
711 // AVX2 variant:
712 // create buffer for 4 interaction particles
713 // and fill buffers via gathering
714 // TODO: use masked load because there will not be enough data left for the whole gather
715 // const __m256d x2tmp = _mm256_i64gather_pd(&xptr[j], _vindex, 1);
716 // const __m256d y2tmp = _mm256_i64gather_pd(&yptr[j], _vindex, 1);
717 // const __m256d z2tmp = _mm256_i64gather_pd(&zptr[j], _vindex, 1);
718 // const __m256d fx2tmp = _mm256_i64gather_pd(&fxptr[j], _vindex, 1);
719 // const __m256d fy2tmp = _mm256_i64gather_pd(&fyptr[j], _vindex, 1);
720 // const __m256d fz2tmp = _mm256_i64gather_pd(&fzptr[j], _vindex, 1);
721 // const __m256d typeID2tmp = _mm256_i64gather_pd(&typeIDptr[j], _vindex, 1);
722
723 for (size_t vecIndex = 0; vecIndex < rest; ++vecIndex) {
724 x2tmp[vecIndex] = xptr[neighborList[j + vecIndex]];
725 y2tmp[vecIndex] = yptr[neighborList[j + vecIndex]];
726 z2tmp[vecIndex] = zptr[neighborList[j + vecIndex]];
727 // if newton3 is used we need to load f of particle j so the kernel can update it too
728 if constexpr (newton3) {
729 fx2tmp[vecIndex] = fxptr[neighborList[j + vecIndex]];
730 fy2tmp[vecIndex] = fyptr[neighborList[j + vecIndex]];
731 fz2tmp[vecIndex] = fzptr[neighborList[j + vecIndex]];
732 }
733 typeID2tmp[vecIndex] = typeIDptr[neighborList[j + vecIndex]];
734 ownedStates2tmp[vecIndex] = ownedStatePtr[neighborList[j + vecIndex]];
735 }
736
737 SoAKernel<newton3, true>(0, ownedStateI, reinterpret_cast<const int64_t *>(ownedStates2tmp.data()), x1, y1, z1,
738 x2tmp.data(), y2tmp.data(), z2tmp.data(), fx2tmp.data(), fy2tmp.data(), fz2tmp.data(),
739 &typeIDptr[indexFirst], typeID2tmp.data(), fxacc, fyacc, fzacc, &virialSumX, &virialSumY,
740 &virialSumZ, &potentialEnergySum, rest);
741
742 if constexpr (newton3) {
743 for (size_t vecIndex = 0; vecIndex < rest; ++vecIndex) {
744 fxptr[neighborList[j + vecIndex]] = fx2tmp[vecIndex];
745 fyptr[neighborList[j + vecIndex]] = fy2tmp[vecIndex];
746 fzptr[neighborList[j + vecIndex]] = fz2tmp[vecIndex];
747 }
748 }
749 }
750
751 // horizontally reduce fDacc to sumfD
752 const __m256d hSumfxfy = _mm256_hadd_pd(fxacc, fyacc);
753 const __m256d hSumfz = _mm256_hadd_pd(fzacc, fzacc);
754
755 const __m128d hSumfxfyLow = _mm256_extractf128_pd(hSumfxfy, 0);
756 const __m128d hSumfzLow = _mm256_extractf128_pd(hSumfz, 0);
757
758 const __m128d hSumfxfyHigh = _mm256_extractf128_pd(hSumfxfy, 1);
759 const __m128d hSumfzHigh = _mm256_extractf128_pd(hSumfz, 1);
760
761 const __m128d sumfxfyVEC = _mm_add_pd(hSumfxfyLow, hSumfxfyHigh);
762 const __m128d sumfzVEC = _mm_add_pd(hSumfzLow, hSumfzHigh);
763
764 const double sumfx = sumfxfyVEC[0];
765 const double sumfy = sumfxfyVEC[1];
766 const double sumfz = _mm_cvtsd_f64(sumfzVEC);
767
768 fxptr[indexFirst] += sumfx;
769 fyptr[indexFirst] += sumfy;
770 fzptr[indexFirst] += sumfz;
771
772 if constexpr (calculateGlobals) {
773 const int threadnum = autopas::autopas_get_thread_num();
774
775 // horizontally reduce virialSumX and virialSumY
776 const __m256d hSumVirialxy = _mm256_hadd_pd(virialSumX, virialSumY);
777 const __m128d hSumVirialxyLow = _mm256_extractf128_pd(hSumVirialxy, 0);
778 const __m128d hSumVirialxyHigh = _mm256_extractf128_pd(hSumVirialxy, 1);
779 const __m128d hSumVirialxyVec = _mm_add_pd(hSumVirialxyHigh, hSumVirialxyLow);
780
781 // horizontally reduce virialSumZ and potentialEnergySum
782 const __m256d hSumVirialzPotentialEnergy = _mm256_hadd_pd(virialSumZ, potentialEnergySum);
783 const __m128d hSumVirialzPotentialEnergyLow = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 0);
784 const __m128d hSumVirialzPotentialEnergyHigh = _mm256_extractf128_pd(hSumVirialzPotentialEnergy, 1);
785 const __m128d hSumVirialzPotentialEnergyVec =
786 _mm_add_pd(hSumVirialzPotentialEnergyHigh, hSumVirialzPotentialEnergyLow);
787
788 // globals = {virialX, virialY, virialZ, potentialEnergy}
789 double globals[4];
790 _mm_store_pd(&globals[0], hSumVirialxyVec);
791 _mm_store_pd(&globals[2], hSumVirialzPotentialEnergyVec);
792
793 _aosThreadData[threadnum].virialSum[0] += globals[0];
794 _aosThreadData[threadnum].virialSum[1] += globals[1];
795 _aosThreadData[threadnum].virialSum[2] += globals[2];
796 _aosThreadData[threadnum].potentialEnergySum += globals[3];
797 }
798 // interact with i with 4 neighbors
799#endif // __AVX__
800 }
801
802 public:
806 constexpr static auto getNeededAttr() {
807 return std::array<typename Particle_T::AttributeNames, 9>{Particle_T::AttributeNames::id,
808 Particle_T::AttributeNames::posX,
809 Particle_T::AttributeNames::posY,
810 Particle_T::AttributeNames::posZ,
811 Particle_T::AttributeNames::forceX,
812 Particle_T::AttributeNames::forceY,
813 Particle_T::AttributeNames::forceZ,
814 Particle_T::AttributeNames::typeId,
815 Particle_T::AttributeNames::ownershipState};
816 }
817
821 constexpr static auto getNeededAttr(std::false_type) {
822 return std::array<typename Particle_T::AttributeNames, 6>{
823 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
824 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
825 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
826 }
827
831 constexpr static auto getComputedAttr() {
832 return std::array<typename Particle_T::AttributeNames, 3>{
833 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY, Particle_T::AttributeNames::forceZ};
834 }
835
840 constexpr static bool getMixing() { return useMixing; }
841
846 void initTraversal() final {
847 _potentialEnergySum = 0.;
848 _virialSum = {0., 0., 0.};
849 _postProcessed = false;
850 for (size_t i = 0; i < _aosThreadData.size(); ++i) {
851 _aosThreadData[i].setZero();
852 }
853 }
854
859 void endTraversal(bool newton3) final {
860 using namespace autopas::utils::ArrayMath::literals;
861
862 if (_postProcessed) {
864 "Already postprocessed, endTraversal(bool newton3) was called twice without calling initTraversal().");
865 }
866 if (calculateGlobals) {
867 for (size_t i = 0; i < _aosThreadData.size(); ++i) {
868 _potentialEnergySum += _aosThreadData[i].potentialEnergySum;
869 _virialSum += _aosThreadData[i].virialSum;
870 }
871 // For each interaction, we added the full contribution for both particles. Divide by 2 here, so that each
872 // contribution is only counted once per pair.
873 _potentialEnergySum *= 0.5;
874 _virialSum *= 0.5;
875
876 // We have always calculated 6*potentialEnergy, so we divide by 6 here!
877 _potentialEnergySum /= 6.;
878 _postProcessed = true;
879
880 AutoPasLog(DEBUG, "Final potential energy {}", _potentialEnergySum);
881 AutoPasLog(DEBUG, "Final virial {}", _virialSum[0] + _virialSum[1] + _virialSum[2]);
882 }
883 }
884
890 if (not calculateGlobals) {
892 "Trying to get potential energy even though calculateGlobals is false. If you want this functor to calculate "
893 "global "
894 "values, please specify calculateGlobals to be true.");
895 }
896 if (not _postProcessed) {
898 "Cannot get potential energy, because endTraversal was not called.");
899 }
900 return _potentialEnergySum;
901 }
902
907 double getVirial() {
908 if (not calculateGlobals) {
910 "Trying to get virial even though calculateGlobals is false. If you want this functor to calculate global "
911 "values, please specify calculateGlobals to be true.");
912 }
913 if (not _postProcessed) {
915 "Cannot get virial, because endTraversal was not called.");
916 }
917 return _virialSum[0] + _virialSum[1] + _virialSum[2];
918 }
919
928 void setParticleProperties(double epsilon24, double sigmaSquared) {
929#ifdef __AVX__
930 _epsilon24 = _mm256_set1_pd(epsilon24);
931 _sigmaSquared = _mm256_set1_pd(sigmaSquared);
932 if constexpr (applyShift) {
933 _shift6 = _mm256_set1_pd(
934 ParticlePropertiesLibrary<double, size_t>::calcShift6(epsilon24, sigmaSquared, _cutoffSquared[0]));
935 } else {
936 _shift6 = _mm256_setzero_pd();
937 }
938#endif
939
940 _epsilon24AoS = epsilon24;
941 _sigmaSquaredAoS = sigmaSquared;
942 if constexpr (applyShift) {
943 _shift6AoS = ParticlePropertiesLibrary<double, size_t>::calcShift6(epsilon24, sigmaSquared, _cutoffSquaredAoS);
944 } else {
945 _shift6AoS = 0.;
946 }
947 }
948
949 private:
950#ifdef __AVX__
958 inline __m256d wrapperFMA(const __m256d &factorA, const __m256d &factorB, const __m256d &summandC) {
959#ifdef __FMA__
960 return _mm256_fmadd_pd(factorA, factorB, summandC);
961#elif __AVX__
962 const __m256d tmp = _mm256_mul_pd(factorA, factorB);
963 return _mm256_add_pd(summandC, tmp);
964#else
965 // dummy return. If no vectorization is available this whole class is pointless anyways.
966 return __m256d();
967#endif
968 }
969#endif
970
974 class AoSThreadData {
975 public:
976 AoSThreadData() : virialSum{0., 0., 0.}, potentialEnergySum{0.}, __remainingTo64{} {}
977 void setZero() {
978 virialSum = {0., 0., 0.};
979 potentialEnergySum = 0.;
980 }
981
982 // variables
983 std::array<double, 3> virialSum;
984 double potentialEnergySum;
985
986 private:
987 // dummy parameter to get the right size (64 bytes)
988 double __remainingTo64[(64 - 4 * sizeof(double)) / sizeof(double)];
989 };
990 // make sure of the size of AoSThreadData
991 static_assert(sizeof(AoSThreadData) % 64 == 0, "AoSThreadData has wrong size");
992
993#ifdef __AVX__
994 const __m256d _zero{_mm256_set1_pd(0.)};
995 const __m256d _one{_mm256_set1_pd(1.)};
996 const __m256i _vindex = _mm256_set_epi64x(0, 1, 3, 4);
997 const __m256i _masks[3]{
998 _mm256_set_epi64x(0, 0, 0, -1),
999 _mm256_set_epi64x(0, 0, -1, -1),
1000 _mm256_set_epi64x(0, -1, -1, -1),
1001 };
1002 const __m256i _ownedStateDummyMM256i{0x0};
1003 const __m256i _ownedStateOwnedMM256i{_mm256_set1_epi64x(static_cast<int64_t>(autopas::OwnershipState::owned))};
1004 const __m256d _cutoffSquared{};
1005 __m256d _shift6 = _mm256_setzero_pd();
1006 __m256d _epsilon24{};
1007 __m256d _sigmaSquared{};
1008#endif
1009
1010 const double _cutoffSquaredAoS = 0;
1011 double _epsilon24AoS, _sigmaSquaredAoS, _shift6AoS = 0;
1012
1013 ParticlePropertiesLibrary<double, size_t> *_PPLibrary = nullptr;
1014
1015 // sum of the potential energy, only calculated if calculateGlobals is true
1016 double _potentialEnergySum;
1017
1018 // sum of the virial, only calculated if calculateGlobals is true
1019 std::array<double, 3> _virialSum;
1020
1021 // thread buffer for aos
1022 std::vector<AoSThreadData> _aosThreadData;
1023
1024 // defines whether or whether not the global values are already preprocessed
1025 bool _postProcessed;
1026
1027 // number of double values that fit into a vector register.
1028 // MUST be power of 2 because some optimizations make this assumption
1029 constexpr static size_t vecLength = 4;
1030};
1031} // namespace mdLib
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:74
This class stores the (physical) properties of molecule types, and, in the case of multi-site molecul...
Definition: ParticlePropertiesLibrary.h:28
floatType getMixingShift6(intType i, intType j) const
Returns precomputed mixed shift * 6 for one pair of site types.
Definition: ParticlePropertiesLibrary.h:262
floatType getMixingSigmaSquared(intType i, intType j) const
Returns precomputed mixed squared sigma for one pair of site types.
Definition: ParticlePropertiesLibrary.h:252
floatType getMixing24Epsilon(intType i, intType j) const
Returns the precomputed mixed epsilon * 24.
Definition: ParticlePropertiesLibrary.h:224
static double calcShift6(double epsilon24, double sigmaSquared, double cutoffSquared)
Calculate the shift multiplied 6 of the lennard jones potential from given cutoff,...
Definition: ParticlePropertiesLibrary.h:576
Functor base class.
Definition: Functor.h:41
PairwiseFunctor class.
Definition: PairwiseFunctor.h:66
View on a fixed part of a SoA between a start index and an end index.
Definition: SoAView.h:25
size_t size() const
Returns the number of particles in the view.
Definition: SoAView.h:85
Default exception class for autopas exceptions.
Definition: ExceptionHandler.h:116
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:64
A functor to handle lennard-jones interactions between two particles (molecules).
Definition: LJFunctorAVX.h:49
bool allowsNewton3() final
Specifies whether the functor is capable of Newton3-like functors.
Definition: LJFunctorAVX.h:120
void initTraversal() final
Reset the global values.
Definition: LJFunctorAVX.h:846
LJFunctorAVX(double cutoff, ParticlePropertiesLibrary< double, size_t > &particlePropertiesLibrary)
Constructor for Functor with mixing active.
Definition: LJFunctorAVX.h:108
void SoAFunctorVerlet(autopas::SoAView< SoAArraysType > soa, const size_t indexFirst, std::span< const size_t > neighborList, bool newton3) final
PairwiseFunctor for structure of arrays (SoA) for neighbor lists.
Definition: LJFunctorAVX.h:604
std::string getName() final
Returns name of functor.
Definition: LJFunctorAVX.h:116
double getVirial()
Get the virial.
Definition: LJFunctorAVX.h:907
double getPotentialEnergy()
Get the potential Energy.
Definition: LJFunctorAVX.h:889
static constexpr auto getComputedAttr()
Get attributes computed by this functor.
Definition: LJFunctorAVX.h:831
LJFunctorAVX(double cutoff)
Constructor for Functor with mixing disabled.
Definition: LJFunctorAVX.h:96
static constexpr bool getMixing()
Definition: LJFunctorAVX.h:840
void SoAFunctorSingle(autopas::SoAView< SoAArraysType > soa, bool newton3) final
PairwiseFunctor for structure of arrays (SoA)
Definition: LJFunctorAVX.h:186
void AoSFunctor(Particle_T &i, Particle_T &j, bool newton3) final
PairwiseFunctor for arrays of structures (AoS).
Definition: LJFunctorAVX.h:128
void SoAFunctorPair(autopas::SoAView< SoAArraysType > soa1, autopas::SoAView< SoAArraysType > soa2, const bool newton3) final
PairwiseFunctor for structure of arrays (SoA)
Definition: LJFunctorAVX.h:191
bool allowsNonNewton3() final
Specifies whether the functor is capable of non-Newton3-like functors.
Definition: LJFunctorAVX.h:124
static constexpr auto getNeededAttr()
Get attributes needed for computation.
Definition: LJFunctorAVX.h:806
static constexpr auto getNeededAttr(std::false_type)
Get attributes needed for computation without N3 optimization.
Definition: LJFunctorAVX.h:821
bool isRelevantForTuning() final
Specifies whether the functor should be considered for the auto-tuning process.
Definition: LJFunctorAVX.h:118
void setParticleProperties(double epsilon24, double sigmaSquared)
Sets the particle properties constants for this functor.
Definition: LJFunctorAVX.h:928
LJFunctorAVX()=delete
Deleted default constructor.
void endTraversal(bool newton3) final
Accumulates global values, e.g.
Definition: LJFunctorAVX.h:859
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
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
@ dummy
Dummy or deleted state, a particle with this state is not an actual particle!
@ owned
Owned state, a particle with this state is an actual particle and owned by the current AutoPas object...
FunctorN3Modes
Newton 3 modes for the Functor.
Definition: Functor.h:23
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132