AutoPas  3.0.0
Loading...
Searching...
No Matches
LJMultisiteFunctor.h
Go to the documentation of this file.
1
7#pragma once
8
9#include "MoleculeLJ.h"
10#include "MultisiteMoleculeLJ.h"
17#include "autopas/utils/Math.h"
19#include "autopas/utils/SoA.h"
22#include "autopas/utils/inBox.h"
23
24namespace mdLib {
25
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, LJMultisiteFunctor<Particle_T, applyShift, useMixing, useNewton3,
49 calculateGlobals, relevantForTuning, countFLOPs>> {
53 using SoAArraysType = typename Particle_T::SoAArraysType;
54
58 using SoAFloatPrecision = typename Particle_T::ParticleSoAFloatPrecision;
59
63 const double _cutoffSquared;
64
68 double _epsilon24;
69
73 double _sigmaSquared;
74
78 double _shift6 = 0;
79
83 const std::vector<std::array<double, 3>> _sitePositionsLJ{};
84
89
93 double _potentialEnergySum;
94
98 std::array<double, 3> _virialSum;
99
103 bool _postProcessed;
104
105 public:
110
111 private:
117 explicit LJMultisiteFunctor(SoAFloatPrecision cutoff, void * /*dummy*/)
118 : autopas::PairwiseFunctor<Particle_T, LJMultisiteFunctor<Particle_T, applyShift, useMixing, useNewton3,
119 calculateGlobals, relevantForTuning, countFLOPs>>(
120 cutoff),
121 _cutoffSquared{cutoff * cutoff},
122 _potentialEnergySum{0.},
123 _virialSum{0., 0., 0.},
124 _aosThreadData(),
125 _postProcessed{false} {
126 if constexpr (calculateGlobals) {
127 _aosThreadData.resize(autopas::autopas_get_max_threads());
128 }
129 if constexpr (countFLOPs) {
130 AutoPasLog(DEBUG, "FLOP counting is enabled but is not supported for multi-site functors yet!");
131 }
132 }
133
134 public:
140 explicit LJMultisiteFunctor(double cutoff) : LJMultisiteFunctor(cutoff, nullptr) {
141 static_assert(not useMixing,
142 "Mixing without a ParticlePropertiesLibrary is not possible! Use a different constructor or set "
143 "mixing to false.");
144 AutoPasLog(WARN, "Using LJMultisiteFunctor with mixing disabled is untested!");
145 }
146
154 explicit LJMultisiteFunctor(double cutoff, ParticlePropertiesLibrary<double, size_t> &particlePropertiesLibrary)
155 : LJMultisiteFunctor(cutoff, nullptr) {
156 static_assert(useMixing,
157 "Not using Mixing but using a ParticlePropertiesLibrary is not allowed! Use a different constructor "
158 "or set mixing to true.");
159 _PPLibrary = &particlePropertiesLibrary;
160 }
161
162 std::string getName() final { return "LJMultisiteFunctor"; }
163
164 bool isRelevantForTuning() final { return relevantForTuning; }
165
166 bool allowsNewton3() final {
167 return useNewton3 == autopas::FunctorN3Modes::Newton3Only or useNewton3 == autopas::FunctorN3Modes::Both;
168 }
169
170 bool allowsNonNewton3() final {
171 return useNewton3 == autopas::FunctorN3Modes::Newton3Off or useNewton3 == autopas::FunctorN3Modes::Both;
172 }
173
181 void AoSFunctor(Particle_T &particleA, Particle_T &particleB, bool newton3) final {
182 using namespace autopas::utils::ArrayMath::literals;
183 if (particleA.isDummy() or particleB.isDummy()) {
184 return;
185 }
186
187 // Don't calculate force if particleB outside cutoff of particleA
188 const auto displacementCoM = autopas::utils::ArrayMath::sub(particleA.getR(), particleB.getR());
189 const auto distanceSquaredCoM = autopas::utils::ArrayMath::dot(displacementCoM, displacementCoM);
190
191 if (distanceSquaredCoM > _cutoffSquared) {
192 return;
193 }
194
195 // get number of sites
196 const size_t numSitesA = useMixing ? _PPLibrary->getNumSites(particleA.getTypeId()) : _sitePositionsLJ.size();
197 const size_t numSitesB = useMixing ? _PPLibrary->getNumSites(particleB.getTypeId()) : _sitePositionsLJ.size();
198
199 // get siteIds
200 const std::vector<size_t> siteIdsA =
201 useMixing ? _PPLibrary->getSiteTypes(particleA.getTypeId()) : std::vector<unsigned long>();
202 const std::vector<size_t> siteIdsB =
203 useMixing ? _PPLibrary->getSiteTypes(particleB.getTypeId()) : std::vector<unsigned long>();
204
205 // get unrotated relative site positions
206 const std::vector<std::array<double, 3>> unrotatedSitePositionsA =
207 useMixing ? _PPLibrary->getSitePositions(particleA.getTypeId()) : _sitePositionsLJ;
208 const std::vector<std::array<double, 3>> unrotatedSitePositionsB =
209 useMixing ? _PPLibrary->getSitePositions(particleB.getTypeId()) : _sitePositionsLJ;
210
211 // calculate correctly rotated relative site positions
212 const auto rotatedSitePositionsA =
213 autopas::utils::quaternion::rotateVectorOfPositions(particleA.getQuaternion(), unrotatedSitePositionsA);
214 const auto rotatedSitePositionsB =
215 autopas::utils::quaternion::rotateVectorOfPositions(particleB.getQuaternion(), unrotatedSitePositionsB);
216
217 for (int i = 0; i < numSitesA; i++) {
218 for (int j = 0; j < numSitesB; j++) {
219 const auto displacement = autopas::utils::ArrayMath::add(
220 autopas::utils::ArrayMath::sub(displacementCoM, rotatedSitePositionsB[j]), rotatedSitePositionsA[i]);
221 const auto distanceSquared = autopas::utils::ArrayMath::dot(displacement, displacement);
222
223 const auto sigmaSquared =
224 useMixing ? _PPLibrary->getMixingSigmaSquared(siteIdsA[i], siteIdsB[j]) : _sigmaSquared;
225 const auto epsilon24 = useMixing ? _PPLibrary->getMixing24Epsilon(siteIdsA[i], siteIdsB[j]) : _epsilon24;
226 const auto shift6 =
227 applyShift ? (useMixing ? _PPLibrary->getMixingShift6(siteIdsA[i], siteIdsB[j]) : _shift6) : 0;
228
229 // clang-format off
230 // Calculate potential between sites and thus force
231 // Force = 24 * epsilon * (2*(sigma/distance)^12 - (sigma/distance)^6) * (1/distance)^2 * [x_displacement, y_displacement, z_displacement]
232 // { scalarMultiple } * { displacement }
233 // clang-format on
234 const auto invDistSquared = 1. / distanceSquared;
235 const auto lj2 = sigmaSquared * invDistSquared;
236 const auto lj6 = lj2 * lj2 * lj2;
237 const auto lj12 = lj6 * lj6;
238 const auto lj12m6 = lj12 - lj6; // = LJ potential / (4x epsilon)
239 const auto scalarMultiple = epsilon24 * (lj12 + lj12m6) * invDistSquared;
240 const auto force = autopas::utils::ArrayMath::mulScalar(displacement, scalarMultiple);
241
242 // Add force on site to net force
243 particleA.addF(force);
244 if (newton3) {
245 particleB.subF(force);
246 }
247
248 // Add torque applied by force
249 particleA.addTorque(autopas::utils::ArrayMath::cross(rotatedSitePositionsA[i], force));
250 if (newton3) {
251 particleB.subTorque(autopas::utils::ArrayMath::cross(rotatedSitePositionsB[j], force));
252 }
253
254 if (calculateGlobals) {
255 // We always add the full contribution for each owned particle and divide the sums by 2 in endTraversal().
256 // Potential energy has an additional factor of 6, which is also handled in endTraversal().
257 const auto potentialEnergy6 = epsilon24 * lj12m6 + shift6;
258 const auto virial = displacement * force;
259
260 const auto threadNum = autopas::autopas_get_thread_num();
261
262 if (particleA.isOwned()) {
263 _aosThreadData[threadNum].potentialEnergySum += potentialEnergy6;
264 _aosThreadData[threadNum].virialSum += virial;
265 }
266 // for non-newton3 the second particle will be considered in a separate calculation
267 if (newton3 and particleB.isOwned()) {
268 _aosThreadData[threadNum].potentialEnergySum += potentialEnergy6;
269 _aosThreadData[threadNum].virialSum += virial;
270 }
271 }
272 }
273 }
274 }
275
281 void SoAFunctorSingle(autopas::SoAView<SoAArraysType> soa, bool newton3) final {
282 if (soa.size() == 0) return;
283
284 const auto *const __restrict xptr = soa.template begin<Particle_T::AttributeNames::posX>();
285 const auto *const __restrict yptr = soa.template begin<Particle_T::AttributeNames::posY>();
286 const auto *const __restrict zptr = soa.template begin<Particle_T::AttributeNames::posZ>();
287
288 const auto *const __restrict ownedStatePtr = soa.template begin<Particle_T::AttributeNames::ownershipState>();
289
290 const auto *const __restrict q0ptr = soa.template begin<Particle_T::AttributeNames::quaternion0>();
291 const auto *const __restrict q1ptr = soa.template begin<Particle_T::AttributeNames::quaternion1>();
292 const auto *const __restrict q2ptr = soa.template begin<Particle_T::AttributeNames::quaternion2>();
293 const auto *const __restrict q3ptr = soa.template begin<Particle_T::AttributeNames::quaternion3>();
294
295 SoAFloatPrecision *const __restrict fxptr = soa.template begin<Particle_T::AttributeNames::forceX>();
296 SoAFloatPrecision *const __restrict fyptr = soa.template begin<Particle_T::AttributeNames::forceY>();
297 SoAFloatPrecision *const __restrict fzptr = soa.template begin<Particle_T::AttributeNames::forceZ>();
298
299 SoAFloatPrecision *const __restrict txptr = soa.template begin<Particle_T::AttributeNames::torqueX>();
300 SoAFloatPrecision *const __restrict typtr = soa.template begin<Particle_T::AttributeNames::torqueY>();
301 SoAFloatPrecision *const __restrict tzptr = soa.template begin<Particle_T::AttributeNames::torqueZ>();
302
303 [[maybe_unused]] auto *const __restrict typeptr = soa.template begin<Particle_T::AttributeNames::typeId>();
304
305 SoAFloatPrecision potentialEnergySum = 0.;
306 SoAFloatPrecision virialSumX = 0.;
307 SoAFloatPrecision virialSumY = 0.;
308 SoAFloatPrecision virialSumZ = 0.;
309
310 // the local redeclaration of the following values helps the SoAFloatPrecision-generation of various compilers.
311 const SoAFloatPrecision cutoffSquared = _cutoffSquared;
312
313 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> sigmaSquareds;
314 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> epsilon24s;
315 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> shift6s;
316
317 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionX;
318 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionY;
319 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionZ;
320
321 // we require arrays for forces for sites to maintain SIMD in site-site calculations
322 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceX;
323 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceY;
324 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceZ;
325
326 std::vector<size_t, autopas::AlignedAllocator<size_t>> siteTypes;
327 std::vector<char, autopas::AlignedAllocator<char>> isSiteOwned;
328
329 const SoAFloatPrecision const_sigmaSquared = _sigmaSquared;
330 const SoAFloatPrecision const_epsilon24 = _epsilon24;
331 const SoAFloatPrecision const_shift6 = _shift6;
332
333 const auto const_unrotatedSitePositions = _sitePositionsLJ;
334
335 // count number of sites in SoA
336 size_t siteCount = 0;
337 if constexpr (useMixing) {
338 for (size_t mol = 0; mol < soa.size(); ++mol) {
339 siteCount += _PPLibrary->getNumSites(typeptr[mol]);
340 }
341 } else {
342 siteCount = const_unrotatedSitePositions.size() * soa.size();
343 }
344
345 // pre-reserve site std::vectors
346 exactSitePositionX.reserve(siteCount);
347 exactSitePositionY.reserve(siteCount);
348 exactSitePositionZ.reserve(siteCount);
349
350 if constexpr (useMixing) {
351 siteTypes.reserve(siteCount);
352 }
353
354 siteForceX.reserve((siteCount));
355 siteForceY.reserve((siteCount));
356 siteForceZ.reserve((siteCount));
357
358 if constexpr (calculateGlobals) {
359 // this is only needed for vectorization when calculating globals
360 isSiteOwned.reserve(siteCount);
361 }
362
363 // Fill site-wise std::vectors for SIMD
364 if constexpr (useMixing) {
365 size_t siteIndex = 0;
366 for (size_t mol = 0; mol < soa.size(); ++mol) {
367 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
368 {q0ptr[mol], q1ptr[mol], q2ptr[mol], q3ptr[mol]}, _PPLibrary->getSitePositions(typeptr[mol]));
369 const auto siteTypesOfMol = _PPLibrary->getSiteTypes(typeptr[mol]);
370
371 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[mol]); ++site) {
372 exactSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[mol];
373 exactSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[mol];
374 exactSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[mol];
375 siteTypes[siteIndex] = siteTypesOfMol[site];
376 siteForceX[siteIndex] = 0.;
377 siteForceY[siteIndex] = 0.;
378 siteForceZ[siteIndex] = 0.;
379 if (calculateGlobals) {
380 isSiteOwned[siteIndex] = ownedStatePtr[mol] == autopas::OwnershipState::owned;
381 }
382 ++siteIndex;
383 }
384 }
385 } else {
386 size_t siteIndex = 0;
387 for (size_t mol = 0; mol < soa.size(); mol++) {
388 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
389 {q0ptr[mol], q1ptr[mol], q2ptr[mol], q3ptr[mol]}, const_unrotatedSitePositions);
390 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
391 exactSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[mol];
392 exactSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[mol];
393 exactSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[mol];
394 siteForceX[siteIndex] = 0.;
395 siteForceY[siteIndex] = 0.;
396 siteForceZ[siteIndex] = 0.;
397 if (calculateGlobals) {
398 isSiteOwned[siteIndex] = ownedStatePtr[mol] == autopas::OwnershipState::owned;
399 }
400 ++siteIndex;
401 }
402 }
403 }
404
405 // main force calculation loop
406 size_t siteIndexMolA = 0; // index of first site in molA
407 for (size_t molA = 0; molA < soa.size(); ++molA) {
408 const size_t noSitesInMolA = useMixing ? _PPLibrary->getNumSites(typeptr[molA])
409 : const_unrotatedSitePositions.size(); // Number of sites in molecule A
410
411 const auto ownedStateA = ownedStatePtr[molA];
412 if (ownedStateA == autopas::OwnershipState::dummy) {
413 siteIndexMolA += noSitesInMolA;
414 continue;
415 }
416
417 const size_t siteIndexMolB = siteIndexMolA + noSitesInMolA; // index of first site in molB
418 const size_t noSitesB = (siteCount - siteIndexMolB); // Number of sites in molecules that A interacts with
419
420 // create mask over every mol 'above' molA (char to keep arrays aligned)
421 std::vector<char, autopas::AlignedAllocator<char>> molMask;
422 molMask.reserve(soa.size() - (molA + 1));
423
424#pragma omp simd
425 for (size_t molB = molA + 1; molB < soa.size(); ++molB) {
426 const auto ownedStateB = ownedStatePtr[molB];
427
428 const auto displacementCoMX = xptr[molA] - xptr[molB];
429 const auto displacementCoMY = yptr[molA] - yptr[molB];
430 const auto displacementCoMZ = zptr[molA] - zptr[molB];
431
432 const auto distanceSquaredCoMX = displacementCoMX * displacementCoMX;
433 const auto distanceSquaredCoMY = displacementCoMY * displacementCoMY;
434 const auto distanceSquaredCoMZ = displacementCoMZ * displacementCoMZ;
435
436 const auto distanceSquaredCoM = distanceSquaredCoMX + distanceSquaredCoMY + distanceSquaredCoMZ;
437
438 // mask sites of molecules beyond cutoff or if molecule is a dummy
439 molMask[molB - (molA + 1)] =
440 distanceSquaredCoM <= cutoffSquared and ownedStateB != autopas::OwnershipState::dummy;
441 }
442
443 // generate mask for each site in the mols 'above' molA from molecular mask
444 std::vector<char, autopas::AlignedAllocator<char>> siteMask;
445 siteMask.reserve(noSitesB);
446
447 for (size_t molB = molA + 1; molB < soa.size(); ++molB) {
448 for (size_t siteB = 0; siteB < _PPLibrary->getNumSites(typeptr[molB]); ++siteB) {
449 siteMask.emplace_back(molMask[molB - (molA + 1)]);
450 }
451 }
452
453 // calculate LJ forces
454 for (size_t siteA = siteIndexMolA; siteA < siteIndexMolB; ++siteA) {
455 if (useMixing) {
456 // preload sigmas, epsilons, and shifts
457 sigmaSquareds.reserve(noSitesB);
458 epsilon24s.reserve(noSitesB);
459 if constexpr (applyShift) {
460 shift6s.reserve(noSitesB);
461 }
462
463 for (size_t siteB = 0; siteB < siteCount - (siteIndexMolB); ++siteB) {
464 const auto mixingData = _PPLibrary->getLJMixingData(siteTypes[siteA], siteTypes[siteIndexMolB + siteB]);
465 sigmaSquareds[siteB] = mixingData.sigmaSquared;
466 epsilon24s[siteB] = mixingData.epsilon24;
467 if (applyShift) {
468 shift6s[siteB] = mixingData.shift6;
469 }
470 }
471 }
472 // sums used for siteA
473 SoAFloatPrecision forceSumX = 0.;
474 SoAFloatPrecision forceSumY = 0.;
475 SoAFloatPrecision forceSumZ = 0.;
476 SoAFloatPrecision torqueSumX = 0.;
477 SoAFloatPrecision torqueSumY = 0.;
478 SoAFloatPrecision torqueSumZ = 0.;
479
480#pragma omp simd reduction (+ : forceSumX, forceSumY, forceSumZ, torqueSumX, torqueSumY, torqueSumZ, potentialEnergySum, virialSumX, virialSumY, virialSumZ)
481 for (size_t siteB = 0; siteB < noSitesB; ++siteB) {
482 const size_t globalSiteBIndex = siteB + siteIndexMolB;
483
484 const SoAFloatPrecision sigmaSquared = useMixing ? sigmaSquareds[siteB] : const_sigmaSquared;
485 const SoAFloatPrecision epsilon24 = useMixing ? epsilon24s[siteB] : const_epsilon24;
486 const SoAFloatPrecision shift6 = applyShift ? (useMixing ? shift6s[siteB] : const_shift6) : 0;
487
488 const auto isSiteBOwned = !calculateGlobals || isSiteOwned[globalSiteBIndex];
489
490 const auto displacementX = exactSitePositionX[siteA] - exactSitePositionX[globalSiteBIndex];
491 const auto displacementY = exactSitePositionY[siteA] - exactSitePositionY[globalSiteBIndex];
492 const auto displacementZ = exactSitePositionZ[siteA] - exactSitePositionZ[globalSiteBIndex];
493
494 const auto distanceSquaredX = displacementX * displacementX;
495 const auto distanceSquaredY = displacementY * displacementY;
496 const auto distanceSquaredZ = displacementZ * displacementZ;
497
498 const auto distanceSquared = distanceSquaredX + distanceSquaredY + distanceSquaredZ;
499
500 const auto invDistSquared = 1. / distanceSquared;
501 const auto lj2 = sigmaSquared * invDistSquared;
502 const auto lj6 = lj2 * lj2 * lj2;
503 const auto lj12 = lj6 * lj6;
504 const auto lj12m6 = lj12 - lj6;
505 const auto scalarMultiple = siteMask[siteB] ? epsilon24 * (lj12 + lj12m6) * invDistSquared : 0.;
506
507 // calculate forces
508 const auto forceX = scalarMultiple * displacementX;
509 const auto forceY = scalarMultiple * displacementY;
510 const auto forceZ = scalarMultiple * displacementZ;
511
512 forceSumX += forceX;
513 forceSumY += forceY;
514 forceSumZ += forceZ;
515
516 // newton's third law
517 siteForceX[globalSiteBIndex] -= forceX;
518 siteForceY[globalSiteBIndex] -= forceY;
519 siteForceZ[globalSiteBIndex] -= forceZ;
520
521 if constexpr (calculateGlobals) {
522 const auto virialX = displacementX * forceX;
523 const auto virialY = displacementY * forceY;
524 const auto virialZ = displacementZ * forceZ;
525 const auto potentialEnergy6 = siteMask[siteB] ? (epsilon24 * lj12m6 + shift6) : 0.;
526
527 // We add 6 times the potential energy for each owned particle. The total sum is corrected in
528 // endTraversal().
529 const auto ownershipMask =
530 (ownedStateA == autopas::OwnershipState::owned ? 1. : 0.) + (isSiteBOwned ? 1. : 0.);
531 potentialEnergySum += potentialEnergy6 * ownershipMask;
532 virialSumX += virialX * ownershipMask;
533 virialSumY += virialY * ownershipMask;
534 virialSumZ += virialZ * ownershipMask;
535 }
536 }
537 // sum forces on single site in mol A
538 siteForceX[siteA] += forceSumX;
539 siteForceY[siteA] += forceSumY;
540 siteForceZ[siteA] += forceSumZ;
541 }
542 siteIndexMolA += noSitesInMolA;
543 }
544
545 // reduce the forces on individual sites to forces & torques on whole molecules.
546 if constexpr (useMixing) {
547 size_t siteIndex = 0;
548 for (size_t mol = 0; mol < soa.size(); ++mol) {
549 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
550 {q0ptr[mol], q1ptr[mol], q2ptr[mol], q3ptr[mol]}, _PPLibrary->getSitePositions(typeptr[mol]));
551 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[mol]); ++site) {
552 fxptr[mol] += siteForceX[siteIndex];
553 fyptr[mol] += siteForceY[siteIndex];
554 fzptr[mol] += siteForceZ[siteIndex];
555 txptr[mol] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
556 rotatedSitePositions[site][2] * siteForceY[siteIndex];
557 typtr[mol] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
558 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
559 tzptr[mol] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
560 rotatedSitePositions[site][1] * siteForceX[siteIndex];
561 ++siteIndex;
562 }
563 }
564 } else {
565 size_t siteIndex = 0;
566 for (size_t mol = 0; mol < soa.size(); mol++) {
567 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
568 {q0ptr[mol], q1ptr[mol], q2ptr[mol], q3ptr[mol]}, const_unrotatedSitePositions);
569 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
570 fxptr[mol] += siteForceX[siteIndex];
571 fyptr[mol] += siteForceY[siteIndex];
572 fzptr[mol] += siteForceZ[siteIndex];
573 txptr[mol] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
574 rotatedSitePositions[site][2] * siteForceY[siteIndex];
575 typtr[mol] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
576 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
577 tzptr[mol] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
578 rotatedSitePositions[site][1] * siteForceX[siteIndex];
579 ++siteIndex;
580 }
581 }
582 }
583
584 if constexpr (calculateGlobals) {
585 const auto threadNum = autopas::autopas_get_thread_num();
586
587 _aosThreadData[threadNum].potentialEnergySum += potentialEnergySum;
588 _aosThreadData[threadNum].virialSum[0] += virialSumX;
589 _aosThreadData[threadNum].virialSum[1] += virialSumY;
590 _aosThreadData[threadNum].virialSum[2] += virialSumZ;
591 }
592 }
597 const bool newton3) final {
598 if (newton3) {
599 SoAFunctorPairImpl<true>(soa1, soa2);
600 } else {
601 SoAFunctorPairImpl<false>(soa1, soa2);
602 }
603 }
604
605 // clang-format off
609 // clang-format on
610 void SoAFunctorVerlet(autopas::SoAView<SoAArraysType> soa, const size_t indexFirst,
611 const std::vector<size_t, autopas::AlignedAllocator<size_t>> &neighborList,
612 bool newton3) final {
613 if (soa.size() == 0 or neighborList.empty()) return;
614 if (newton3) {
615 SoAFunctorVerletImpl<true>(soa, indexFirst, neighborList);
616 } else {
617 SoAFunctorVerletImpl<false>(soa, indexFirst, neighborList);
618 }
619 }
620
629 void setParticleProperties(SoAFloatPrecision epsilon24, SoAFloatPrecision sigmaSquared,
630 std::vector<std::array<SoAFloatPrecision, 3>> sitePositionsLJ) {
631 _epsilon24 = epsilon24;
632 _sigmaSquared = sigmaSquared;
633 if (applyShift) {
634 _shift6 = ParticlePropertiesLibrary<double, size_t>::calcShift6(_epsilon24, _sigmaSquared, _cutoffSquared);
635 } else {
636 _shift6 = 0;
637 }
638 _sitePositionsLJ = sitePositionsLJ;
639 }
640
644 constexpr static auto getNeededAttr() {
645 return std::array<typename Particle_T::AttributeNames, 16>{
646 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
647 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
648 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY,
649 Particle_T::AttributeNames::forceZ, Particle_T::AttributeNames::quaternion0,
650 Particle_T::AttributeNames::quaternion1, Particle_T::AttributeNames::quaternion2,
651 Particle_T::AttributeNames::quaternion3, Particle_T::AttributeNames::torqueX,
652 Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ,
653 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
654 }
655
659 constexpr static auto getNeededAttr(std::false_type) {
660 return std::array<typename Particle_T::AttributeNames, 16>{
661 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
662 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
663 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY,
664 Particle_T::AttributeNames::forceZ, Particle_T::AttributeNames::quaternion0,
665 Particle_T::AttributeNames::quaternion1, Particle_T::AttributeNames::quaternion2,
666 Particle_T::AttributeNames::quaternion3, Particle_T::AttributeNames::torqueX,
667 Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ,
668 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
669 }
670
674 constexpr static auto getComputedAttr() {
675 return std::array<typename Particle_T::AttributeNames, 6>{
676 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY, Particle_T::AttributeNames::forceZ,
677 Particle_T::AttributeNames::torqueX, Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ};
678 }
679
683 constexpr static bool getMixing() { return useMixing; }
684
695 unsigned long getNumFlopsPerKernelCall(size_t molAType, size_t molBType, bool newton3) {
696 // Site-to-site displacement: 6 (3 in the SoA case, but this requires O(N) precomputing site positions)
697 // Site-to-site distance squared: 4
698 // Compute scale: 9
699 // Apply scale to force: With newton3: 6, Without: 3
700 // Apply scale to torque: With newton3 18, Without: 9 (0 in SoA case, with O(N) post computing)
701 // Site-to-site total: With newton3: 33, Without: 26
702 // (SoA total: With N3L: 22, Without N3L: 19)
703 // Above multiplied by number sites of i * number sites of j
704 const unsigned long siteToSiteFlops = newton3 ? 33ul : 26ul;
705 return _PPLibrary->getNumSites(molAType) * _PPLibrary->getNumSites(molBType) * siteToSiteFlops;
706 }
707
712 void initTraversal() final {
713 _potentialEnergySum = 0;
714 _virialSum = {0., 0., 0.};
715 _postProcessed = false;
716 for (size_t i = 0; i < _aosThreadData.size(); i++) {
717 _aosThreadData[i].setZero();
718 }
719 }
720
725 void endTraversal(bool newton3) final {
726 using namespace autopas::utils::ArrayMath::literals;
727
728 if (_postProcessed) {
730 "Already postprocessed, endTraversal(bool newton3) was called twice without calling initTraversal().");
731 }
732 if (calculateGlobals) {
733 for (size_t i = 0; i < _aosThreadData.size(); ++i) {
734 _potentialEnergySum += _aosThreadData[i].potentialEnergySum;
735 _virialSum += _aosThreadData[i].virialSum;
736 }
737
738 // For each interaction, we added the full contribution for both particles. Divide by 2 here, so that each
739 // contribution is only counted once per pair.
740 _potentialEnergySum *= 0.5;
741 _virialSum *= 0.5;
742
743 // We have always calculated 6*potentialEnergy, so we divide by 6 here!
744 _potentialEnergySum /= 6.;
745 _postProcessed = true;
746
747 AutoPasLog(DEBUG, "Final potential energy {}", _potentialEnergySum);
748 AutoPasLog(DEBUG, "Final virial {}", _virialSum[0] + _virialSum[1] + _virialSum[2]);
749 }
750 }
751
758 if (not calculateGlobals) {
760 "Trying to get potential energy even though calculateGlobals is false. If you want this functor to calculate "
761 "global "
762 "values, please specify calculateGlobals to be true.");
763 }
764 if (not _postProcessed) {
766 "Cannot get potential energy, because endTraversal was not called.");
767 }
768 return _potentialEnergySum;
769 }
770
775 double getVirial() {
776 if (not calculateGlobals) {
778 "Trying to get virial even though calculateGlobals is false. If you want this functor to calculate global "
779 "values, please specify calculateGlobals to be true.");
780 }
781 if (not _postProcessed) {
783 "Cannot get virial, because endTraversal was not called.");
784 }
785 return _virialSum[0] + _virialSum[1] + _virialSum[2];
786 }
787
788 private:
795 template <bool newton3>
796 void SoAFunctorPairImpl(autopas::SoAView<SoAArraysType> soaA, autopas::SoAView<SoAArraysType> soaB) {
797 if (soaA.size() == 0 || soaB.size() == 0) return;
798
799 const auto *const __restrict xAptr = soaA.template begin<Particle_T::AttributeNames::posX>();
800 const auto *const __restrict yAptr = soaA.template begin<Particle_T::AttributeNames::posY>();
801 const auto *const __restrict zAptr = soaA.template begin<Particle_T::AttributeNames::posZ>();
802 const auto *const __restrict xBptr = soaB.template begin<Particle_T::AttributeNames::posX>();
803 const auto *const __restrict yBptr = soaB.template begin<Particle_T::AttributeNames::posY>();
804 const auto *const __restrict zBptr = soaB.template begin<Particle_T::AttributeNames::posZ>();
805
806 const auto *const __restrict ownedStatePtrA = soaA.template begin<Particle_T::AttributeNames::ownershipState>();
807 const auto *const __restrict ownedStatePtrB = soaB.template begin<Particle_T::AttributeNames::ownershipState>();
808
809 const auto *const __restrict q0Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion0>();
810 const auto *const __restrict q1Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion1>();
811 const auto *const __restrict q2Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion2>();
812 const auto *const __restrict q3Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion3>();
813 const auto *const __restrict q0Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion0>();
814 const auto *const __restrict q1Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion1>();
815 const auto *const __restrict q2Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion2>();
816 const auto *const __restrict q3Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion3>();
817
818 SoAFloatPrecision *const __restrict fxAptr = soaA.template begin<Particle_T::AttributeNames::forceX>();
819 SoAFloatPrecision *const __restrict fyAptr = soaA.template begin<Particle_T::AttributeNames::forceY>();
820 SoAFloatPrecision *const __restrict fzAptr = soaA.template begin<Particle_T::AttributeNames::forceZ>();
821 SoAFloatPrecision *const __restrict fxBptr = soaB.template begin<Particle_T::AttributeNames::forceX>();
822 SoAFloatPrecision *const __restrict fyBptr = soaB.template begin<Particle_T::AttributeNames::forceY>();
823 SoAFloatPrecision *const __restrict fzBptr = soaB.template begin<Particle_T::AttributeNames::forceZ>();
824
825 SoAFloatPrecision *const __restrict txAptr = soaA.template begin<Particle_T::AttributeNames::torqueX>();
826 SoAFloatPrecision *const __restrict tyAptr = soaA.template begin<Particle_T::AttributeNames::torqueY>();
827 SoAFloatPrecision *const __restrict tzAptr = soaA.template begin<Particle_T::AttributeNames::torqueZ>();
828 SoAFloatPrecision *const __restrict txBptr = soaB.template begin<Particle_T::AttributeNames::torqueX>();
829 SoAFloatPrecision *const __restrict tyBptr = soaB.template begin<Particle_T::AttributeNames::torqueY>();
830 SoAFloatPrecision *const __restrict tzBptr = soaB.template begin<Particle_T::AttributeNames::torqueZ>();
831
832 [[maybe_unused]] auto *const __restrict typeptrA = soaA.template begin<Particle_T::AttributeNames::typeId>();
833 [[maybe_unused]] auto *const __restrict typeptrB = soaB.template begin<Particle_T::AttributeNames::typeId>();
834
835 SoAFloatPrecision potentialEnergySum = 0.;
836 SoAFloatPrecision virialSumX = 0.;
837 SoAFloatPrecision virialSumY = 0.;
838 SoAFloatPrecision virialSumZ = 0.;
839
840 // local redeclarations to help compilers
841 const SoAFloatPrecision cutoffSquared = _cutoffSquared;
842
843 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> sigmaSquareds;
844 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> epsilon24s;
845 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> shift6s;
846
847 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBx;
848 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBy;
849 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBz;
850
851 // we require arrays for forces for sites to maintain SIMD in site-site calculations
852 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBx;
853 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBy;
854 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBz;
855
856 std::vector<size_t, autopas::AlignedAllocator<size_t>> siteTypesB;
857 std::vector<char, autopas::AlignedAllocator<char>> isSiteOwnedBArr;
858
859 const SoAFloatPrecision const_sigmaSquared = _sigmaSquared;
860 const SoAFloatPrecision const_epsilon24 = _epsilon24;
861 const SoAFloatPrecision const_shift6 = _shift6;
862
863 const auto const_unrotatedSitePositions = _sitePositionsLJ;
864
865 // count number of sites in both SoAs
866 size_t siteCountB = 0;
867 if constexpr (useMixing) {
868 for (size_t mol = 0; mol < soaB.size(); ++mol) {
869 siteCountB += _PPLibrary->getNumSites(typeptrB[mol]);
870 }
871 } else {
872 siteCountB = const_unrotatedSitePositions.size() * soaB.size();
873 }
874
875 // pre-reserve std::vectors
876 exactSitePositionBx.reserve(siteCountB);
877 exactSitePositionBy.reserve(siteCountB);
878 exactSitePositionBz.reserve(siteCountB);
879
880 if constexpr (useMixing) {
881 siteTypesB.reserve(siteCountB);
882 }
883
884 siteForceBx.reserve(siteCountB);
885 siteForceBy.reserve(siteCountB);
886 siteForceBz.reserve(siteCountB);
887
888 if constexpr (calculateGlobals) {
889 // this is only needed for vectorization when calculating globals
890 isSiteOwnedBArr.reserve(siteCountB);
891 }
892
893 if constexpr (useMixing) {
894 siteTypesB.reserve(siteCountB);
895 sigmaSquareds.reserve(siteCountB);
896 epsilon24s.reserve(siteCountB);
897 if constexpr (applyShift) {
898 shift6s.reserve(siteCountB);
899 }
900 }
901
902 // Fill site-wise std::vectors for SIMD
903 if constexpr (useMixing) {
904 size_t siteIndex = 0;
905 for (size_t mol = 0; mol < soaB.size(); ++mol) {
906 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
907 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, _PPLibrary->getSitePositions(typeptrB[mol]));
908 const auto siteTypesOfMol = _PPLibrary->getSiteTypes(typeptrB[mol]);
909
910 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptrB[mol]); ++site) {
911 exactSitePositionBx[siteIndex] = rotatedSitePositions[site][0] + xBptr[mol];
912 exactSitePositionBy[siteIndex] = rotatedSitePositions[site][1] + yBptr[mol];
913 exactSitePositionBz[siteIndex] = rotatedSitePositions[site][2] + zBptr[mol];
914 siteTypesB[siteIndex] = siteTypesOfMol[site];
915 siteForceBx[siteIndex] = 0.;
916 siteForceBy[siteIndex] = 0.;
917 siteForceBz[siteIndex] = 0.;
918 if (calculateGlobals) {
919 isSiteOwnedBArr[siteIndex] = ownedStatePtrB[mol] == autopas::OwnershipState::owned;
920 }
921 ++siteIndex;
922 }
923 }
924 } else {
925 size_t siteIndex = 0;
926 for (size_t mol = 0; mol < soaB.size(); mol++) {
927 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
928 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, const_unrotatedSitePositions);
929 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
930 exactSitePositionBx[siteIndex] = rotatedSitePositions[site][0] + xBptr[mol];
931 exactSitePositionBy[siteIndex] = rotatedSitePositions[site][1] + yBptr[mol];
932 exactSitePositionBz[siteIndex] = rotatedSitePositions[site][2] + zBptr[mol];
933 siteForceBx[siteIndex] = 0.;
934 siteForceBy[siteIndex] = 0.;
935 siteForceBz[siteIndex] = 0.;
936 if (calculateGlobals) {
937 isSiteOwnedBArr[siteIndex] = ownedStatePtrB[mol] == autopas::OwnershipState::owned;
938 }
939 ++siteIndex;
940 }
941 }
942 }
943
944 // main force calculation loop
945 for (size_t molA = 0; molA < soaA.size(); ++molA) {
946 const auto ownedStateA = ownedStatePtrA[molA];
947 if (ownedStateA == autopas::OwnershipState::dummy) {
948 continue;
949 }
950
951 const auto noSitesInMolA =
952 useMixing ? _PPLibrary->getNumSites(typeptrA[molA]) : const_unrotatedSitePositions.size();
953 const auto unrotatedSitePositionsA =
954 useMixing ? _PPLibrary->getSitePositions(typeptrA[molA]) : const_unrotatedSitePositions;
955
956 const auto rotatedSitePositionsA = autopas::utils::quaternion::rotateVectorOfPositions(
957 {q0Aptr[molA], q1Aptr[molA], q2Aptr[molA], q3Aptr[molA]}, unrotatedSitePositionsA);
958
959 // create mask over every mol in cell B (char to keep arrays aligned)
960 std::vector<char, autopas::AlignedAllocator<char>> molMask;
961 molMask.reserve(soaB.size());
962
963#pragma omp simd
964 for (size_t molB = 0; molB < soaB.size(); ++molB) {
965 const auto ownedStateB = ownedStatePtrB[molB];
966
967 const auto displacementCoMX = xAptr[molA] - xBptr[molB];
968 const auto displacementCoMY = yAptr[molA] - yBptr[molB];
969 const auto displacementCoMZ = zAptr[molA] - zBptr[molB];
970
971 const auto distanceSquaredCoMX = displacementCoMX * displacementCoMX;
972 const auto distanceSquaredCoMY = displacementCoMY * displacementCoMY;
973 const auto distanceSquaredCoMZ = displacementCoMZ * displacementCoMZ;
974
975 const auto distanceSquaredCoM = distanceSquaredCoMX + distanceSquaredCoMY + distanceSquaredCoMZ;
976
977 // mask sites of molecules beyond cutoff or if molecule is a dummy
978 molMask[molB] = distanceSquaredCoM <= cutoffSquared and ownedStateB != autopas::OwnershipState::dummy;
979 }
980
981 // generate mask for each site in cell B from molecular mask
982 std::vector<char, autopas::AlignedAllocator<char>> siteMask;
983 siteMask.reserve(siteCountB);
984
985 for (size_t molB = 0; molB < soaB.size(); ++molB) {
986 for (size_t siteB = 0; siteB < _PPLibrary->getNumSites(typeptrB[molB]); ++siteB) {
987 siteMask.emplace_back(molMask[molB]);
988 }
989 }
990
991 // sums used for molA
992 SoAFloatPrecision forceSumX = 0.;
993 SoAFloatPrecision forceSumY = 0.;
994 SoAFloatPrecision forceSumZ = 0.;
995 SoAFloatPrecision torqueSumX = 0.;
996 SoAFloatPrecision torqueSumY = 0.;
997 SoAFloatPrecision torqueSumZ = 0.;
998
999 for (size_t siteA = 0; siteA < noSitesInMolA; ++siteA) {
1000 if (useMixing) {
1001 // preload sigmas, epsilons, and shifts
1002 for (size_t siteB = 0; siteB < siteCountB; ++siteB) {
1003 const auto mixingData =
1004 _PPLibrary->getLJMixingData(_PPLibrary->getSiteTypes(typeptrA[molA])[siteA], siteTypesB[siteB]);
1005 sigmaSquareds[siteB] = mixingData.sigmaSquared;
1006 epsilon24s[siteB] = mixingData.epsilon24;
1007 if (applyShift) {
1008 shift6s[siteB] = mixingData.shift6;
1009 }
1010 }
1011 }
1012
1013 const auto rotatedSitePositionAx = rotatedSitePositionsA[siteA][0];
1014 const auto rotatedSitePositionAy = rotatedSitePositionsA[siteA][1];
1015 const auto rotatedSitePositionAz = rotatedSitePositionsA[siteA][2];
1016
1017 const auto exactSitePositionAx = rotatedSitePositionAx + xAptr[molA];
1018 const auto exactSitePositionAy = rotatedSitePositionAy + yAptr[molA];
1019 const auto exactSitePositionAz = rotatedSitePositionAz + zAptr[molA];
1020
1021#pragma omp simd reduction (+ : forceSumX, forceSumY, forceSumZ, torqueSumX, torqueSumY, torqueSumZ, potentialEnergySum, virialSumX, virialSumY, virialSumZ)
1022 for (size_t siteB = 0; siteB < siteCountB; ++siteB) {
1023 const SoAFloatPrecision sigmaSquared = useMixing ? sigmaSquareds[siteB] : const_sigmaSquared;
1024 const SoAFloatPrecision epsilon24 = useMixing ? epsilon24s[siteB] : const_epsilon24;
1025 const SoAFloatPrecision shift6 = applyShift ? (useMixing ? shift6s[siteB] : const_shift6) : 0;
1026
1027 const auto isSiteOwnedB = !calculateGlobals || isSiteOwnedBArr[siteB];
1028
1029 const auto displacementX = exactSitePositionAx - exactSitePositionBx[siteB];
1030 const auto displacementY = exactSitePositionAy - exactSitePositionBy[siteB];
1031 const auto displacementZ = exactSitePositionAz - exactSitePositionBz[siteB];
1032
1033 const auto distanceSquaredX = displacementX * displacementX;
1034 const auto distanceSquaredY = displacementY * displacementY;
1035 const auto distanceSquaredZ = displacementZ * displacementZ;
1036
1037 const auto distanceSquared = distanceSquaredX + distanceSquaredY + distanceSquaredZ;
1038
1039 const auto invDistSquared = 1. / distanceSquared;
1040 const auto lj2 = sigmaSquared * invDistSquared;
1041 const auto lj6 = lj2 * lj2 * lj2;
1042 const auto lj12 = lj6 * lj6;
1043 const auto lj12m6 = lj12 - lj6;
1044 const auto scalarMultiple = siteMask[siteB] ? epsilon24 * (lj12 + lj12m6) * invDistSquared : 0.;
1045
1046 // calculate forces
1047 const auto forceX = scalarMultiple * displacementX;
1048 const auto forceY = scalarMultiple * displacementY;
1049 const auto forceZ = scalarMultiple * displacementZ;
1050
1051 forceSumX += forceX;
1052 forceSumY += forceY;
1053 forceSumZ += forceZ;
1054
1055 torqueSumX += rotatedSitePositionAy * forceZ - rotatedSitePositionAz * forceY;
1056 torqueSumY += rotatedSitePositionAz * forceX - rotatedSitePositionAx * forceZ;
1057 torqueSumZ += rotatedSitePositionAx * forceY - rotatedSitePositionAy * forceX;
1058
1059 // N3L ( total molecular forces + torques to be determined later )
1060 if constexpr (newton3) {
1061 siteForceBx[siteB] -= forceX;
1062 siteForceBy[siteB] -= forceY;
1063 siteForceBz[siteB] -= forceZ;
1064 }
1065
1066 // globals
1067 if constexpr (calculateGlobals) {
1068 const auto potentialEnergy6 = siteMask[siteB] ? (epsilon24 * lj12m6 + shift6) : 0.;
1069 const auto virialX = displacementX * forceX;
1070 const auto virialY = displacementY * forceY;
1071 const auto virialZ = displacementZ * forceZ;
1072
1073 // We add 6 times the potential energy for each owned particle. The total sum is corrected in
1074 // endTraversal().
1075 const auto ownershipFactor =
1076 newton3 ? (ownedStateA == autopas::OwnershipState::owned ? 1. : 0.) + (isSiteOwnedB ? 1. : 0.)
1077 : (ownedStateA == autopas::OwnershipState::owned ? 1. : 0.);
1078 potentialEnergySum += potentialEnergy6 * ownershipFactor;
1079 virialSumX += virialX * ownershipFactor;
1080 virialSumY += virialY * ownershipFactor;
1081 virialSumZ += virialZ * ownershipFactor;
1082 }
1083 }
1084 }
1085 fxAptr[molA] += forceSumX;
1086 fyAptr[molA] += forceSumY;
1087 fzAptr[molA] += forceSumZ;
1088 txAptr[molA] += torqueSumX;
1089 tyAptr[molA] += torqueSumY;
1090 tzAptr[molA] += torqueSumZ;
1091 }
1092
1093 // reduce the forces on individual sites in SoA B to total forces & torques on whole molecules
1094 if constexpr (useMixing) {
1095 size_t siteIndex = 0;
1096 for (size_t mol = 0; mol < soaB.size(); ++mol) {
1097 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1098 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, _PPLibrary->getSitePositions(typeptrB[mol]));
1099 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptrB[mol]); ++site) {
1100 fxBptr[mol] += siteForceBx[siteIndex];
1101 fyBptr[mol] += siteForceBy[siteIndex];
1102 fzBptr[mol] += siteForceBz[siteIndex];
1103 txBptr[mol] += rotatedSitePositions[site][1] * siteForceBz[siteIndex] -
1104 rotatedSitePositions[site][2] * siteForceBy[siteIndex];
1105 tyBptr[mol] += rotatedSitePositions[site][2] * siteForceBx[siteIndex] -
1106 rotatedSitePositions[site][0] * siteForceBz[siteIndex];
1107 tzBptr[mol] += rotatedSitePositions[site][0] * siteForceBy[siteIndex] -
1108 rotatedSitePositions[site][1] * siteForceBx[siteIndex];
1109 ++siteIndex;
1110 }
1111 }
1112 } else {
1113 size_t siteIndex = 0;
1114 for (size_t mol = 0; mol < soaB.size(); ++mol) {
1115 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1116 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, const_unrotatedSitePositions);
1117 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1118 fxBptr[mol] += siteForceBx[siteIndex];
1119 fyBptr[mol] += siteForceBy[siteIndex];
1120 fzBptr[mol] += siteForceBz[siteIndex];
1121 txBptr[mol] += rotatedSitePositions[site][1] * siteForceBz[siteIndex] -
1122 rotatedSitePositions[site][2] * siteForceBy[siteIndex];
1123 tyBptr[mol] += rotatedSitePositions[site][2] * siteForceBx[siteIndex] -
1124 rotatedSitePositions[site][0] * siteForceBz[siteIndex];
1125 tzBptr[mol] += rotatedSitePositions[site][0] * siteForceBy[siteIndex] -
1126 rotatedSitePositions[site][1] * siteForceBx[siteIndex];
1127 ++siteIndex;
1128 }
1129 }
1130 }
1131 if constexpr (calculateGlobals) {
1132 const auto threadNum = autopas::autopas_get_thread_num();
1133 // SoAFunctorPairImpl obtains the potential energy * 12. For non-newton3, this sum is divided by 12 in
1134 // post-processing. For newton3, this sum is only divided by 6 in post-processing, so must be divided by 2 here.
1135 _aosThreadData[threadNum].potentialEnergySum += potentialEnergySum;
1136 _aosThreadData[threadNum].virialSum[0] += virialSumX;
1137 _aosThreadData[threadNum].virialSum[1] += virialSumY;
1138 _aosThreadData[threadNum].virialSum[2] += virialSumZ;
1139 }
1140 }
1141
1142 template <bool newton3>
1143 void SoAFunctorVerletImpl(autopas::SoAView<SoAArraysType> soa, const size_t indexPrime,
1144 const std::vector<size_t, autopas::AlignedAllocator<size_t>> &neighborList) {
1145 const auto *const __restrict ownedStatePtr = soa.template begin<Particle_T::AttributeNames::ownershipState>();
1146
1147 // Skip if primary particle is dummy
1148 const auto ownedStatePrime = ownedStatePtr[indexPrime];
1149 if (ownedStatePrime == autopas::OwnershipState::dummy) {
1150 return;
1151 }
1152
1153 const auto *const __restrict xptr = soa.template begin<Particle_T::AttributeNames::posX>();
1154 const auto *const __restrict yptr = soa.template begin<Particle_T::AttributeNames::posY>();
1155 const auto *const __restrict zptr = soa.template begin<Particle_T::AttributeNames::posZ>();
1156
1157 const auto *const __restrict q0ptr = soa.template begin<Particle_T::AttributeNames::quaternion0>();
1158 const auto *const __restrict q1ptr = soa.template begin<Particle_T::AttributeNames::quaternion1>();
1159 const auto *const __restrict q2ptr = soa.template begin<Particle_T::AttributeNames::quaternion2>();
1160 const auto *const __restrict q3ptr = soa.template begin<Particle_T::AttributeNames::quaternion3>();
1161
1162 SoAFloatPrecision *const __restrict fxptr = soa.template begin<Particle_T::AttributeNames::forceX>();
1163 SoAFloatPrecision *const __restrict fyptr = soa.template begin<Particle_T::AttributeNames::forceY>();
1164 SoAFloatPrecision *const __restrict fzptr = soa.template begin<Particle_T::AttributeNames::forceZ>();
1165
1166 SoAFloatPrecision *const __restrict txptr = soa.template begin<Particle_T::AttributeNames::torqueX>();
1167 SoAFloatPrecision *const __restrict typtr = soa.template begin<Particle_T::AttributeNames::torqueY>();
1168 SoAFloatPrecision *const __restrict tzptr = soa.template begin<Particle_T::AttributeNames::torqueZ>();
1169
1170 [[maybe_unused]] auto *const __restrict typeptr = soa.template begin<Particle_T::AttributeNames::typeId>();
1171
1172 SoAFloatPrecision potentialEnergySum = 0.;
1173 SoAFloatPrecision virialSumX = 0.;
1174 SoAFloatPrecision virialSumY = 0.;
1175 SoAFloatPrecision virialSumZ = 0.;
1176
1177 // the local redeclaration of the following values helps the SoAFloatPrecision-generation of various compilers.
1178 const SoAFloatPrecision cutoffSquared = _cutoffSquared;
1179 const auto const_unrotatedSitePositions = _sitePositionsLJ;
1180
1181 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> sigmaSquareds;
1182 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> epsilon24s;
1183 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> shift6s;
1184
1185 const auto const_sigmaSquared = _sigmaSquared;
1186 const auto const_epsilon24 = _epsilon24;
1187 const auto const_shift6 = _shift6;
1188
1189 const size_t neighborListSize = neighborList.size();
1190 const size_t *const __restrict neighborListPtr = neighborList.data();
1191
1192 // Count sites
1193 const size_t siteCountMolPrime =
1194 useMixing ? _PPLibrary->getNumSites(typeptr[indexPrime]) : const_unrotatedSitePositions.size();
1195
1196 size_t siteCountNeighbors = 0; // site count of neighbours of primary molecule
1197 if constexpr (useMixing) {
1198 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1199 siteCountNeighbors += _PPLibrary->getNumSites(typeptr[neighborList[neighborMol]]);
1200 }
1201 } else {
1202 siteCountNeighbors = const_unrotatedSitePositions.size() * neighborListSize;
1203 }
1204
1205 // initialize site-wise arrays for neighbors
1206 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionX;
1207 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionY;
1208 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionZ;
1209
1210 std::vector<size_t, autopas::AlignedAllocator<size_t>> siteTypesNeighbors;
1211 std::vector<char, autopas::AlignedAllocator<char>> isNeighborSiteOwnedArr;
1212
1213 // we require arrays for forces for sites to maintain SIMD in site-site calculations
1214 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceX;
1215 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceY;
1216 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceZ;
1217
1218 // pre-reserve arrays
1219 exactNeighborSitePositionX.reserve(siteCountNeighbors);
1220 exactNeighborSitePositionY.reserve(siteCountNeighbors);
1221 exactNeighborSitePositionZ.reserve(siteCountNeighbors);
1222
1223 if constexpr (useMixing) {
1224 siteTypesNeighbors.reserve(siteCountNeighbors);
1225 }
1226
1227 siteForceX.reserve(siteCountNeighbors);
1228 siteForceY.reserve(siteCountNeighbors);
1229 siteForceZ.reserve(siteCountNeighbors);
1230
1231 if constexpr (calculateGlobals) {
1232 isNeighborSiteOwnedArr.reserve(siteCountNeighbors);
1233 }
1234
1235 if constexpr (useMixing) {
1236 sigmaSquareds.reserve(siteCountNeighbors);
1237 epsilon24s.reserve(siteCountNeighbors);
1238 if constexpr (applyShift) {
1239 shift6s.reserve(siteCountNeighbors);
1240 }
1241 }
1242
1243 const auto rotatedSitePositionsPrime =
1245 {q0ptr[indexPrime], q1ptr[indexPrime], q2ptr[indexPrime], q3ptr[indexPrime]},
1246 _PPLibrary->getSitePositions(typeptr[indexPrime]))
1247 : autopas::utils::quaternion::rotateVectorOfPositions(
1248 {q0ptr[indexPrime], q1ptr[indexPrime], q2ptr[indexPrime], q3ptr[indexPrime]},
1249 const_unrotatedSitePositions);
1250
1251 const auto siteTypesPrime = _PPLibrary->getSiteTypes(typeptr[indexPrime]); // this is not used if non-mixing
1252
1253 // generate site-wise arrays for neighbors of primary mol
1254 if constexpr (useMixing) {
1255 size_t siteIndex = 0;
1256 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1257 const auto neighborMolIndex = neighborList[neighborMol];
1258 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1259 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1260 _PPLibrary->getSitePositions(typeptr[neighborMolIndex]));
1261 const auto siteTypesOfMol = _PPLibrary->getSiteTypes(typeptr[neighborMolIndex]);
1262
1263 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1264 exactNeighborSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[neighborMolIndex];
1265 exactNeighborSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[neighborMolIndex];
1266 exactNeighborSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[neighborMolIndex];
1267 siteTypesNeighbors[siteIndex] = siteTypesOfMol[site];
1268 siteForceX[siteIndex] = 0.;
1269 siteForceY[siteIndex] = 0.;
1270 siteForceZ[siteIndex] = 0.;
1271 if (calculateGlobals) {
1272 isNeighborSiteOwnedArr[siteIndex] = ownedStatePtr[neighborMolIndex] == autopas::OwnershipState::owned;
1273 }
1274 ++siteIndex;
1275 }
1276 }
1277 } else {
1278 size_t siteIndex = 0;
1279 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1280 const auto neighborMolIndex = neighborList[neighborMol];
1281 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1282 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1283 const_unrotatedSitePositions);
1284 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1285 exactNeighborSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[neighborMolIndex];
1286 exactNeighborSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[neighborMolIndex];
1287 exactNeighborSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[neighborMolIndex];
1288 siteForceX[siteIndex] = 0.;
1289 siteForceY[siteIndex] = 0.;
1290 siteForceZ[siteIndex] = 0.;
1291 if (calculateGlobals) {
1292 isNeighborSiteOwnedArr[siteIndex] = ownedStatePtr[neighborMolIndex] == autopas::OwnershipState::owned;
1293 }
1294 ++siteIndex;
1295 }
1296 }
1297 }
1298
1299 // -- main force calculation --
1300
1301 // - calculate mol mask -
1302 std::vector<char, autopas::AlignedAllocator<char>> molMask;
1303 molMask.reserve(neighborListSize);
1304
1305#pragma omp simd
1306 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1307 const auto neighborMolIndex = neighborList[neighborMol]; // index of neighbor mol in soa
1308
1309 const auto ownedState = ownedStatePtr[neighborMolIndex];
1310
1311 const auto displacementCoMX = xptr[indexPrime] - xptr[neighborMolIndex];
1312 const auto displacementCoMY = yptr[indexPrime] - yptr[neighborMolIndex];
1313 const auto displacementCoMZ = zptr[indexPrime] - zptr[neighborMolIndex];
1314
1315 const auto distanceSquaredCoMX = displacementCoMX * displacementCoMX;
1316 const auto distanceSquaredCoMY = displacementCoMY * displacementCoMY;
1317 const auto distanceSquaredCoMZ = displacementCoMZ * displacementCoMZ;
1318
1319 const auto distanceSquaredCoM = distanceSquaredCoMX + distanceSquaredCoMY + distanceSquaredCoMZ;
1320
1321 // mask molecules beyond cutoff or if molecule is a dummy
1322 molMask[neighborMol] = distanceSquaredCoM <= cutoffSquared and ownedState != autopas::OwnershipState::dummy;
1323 }
1324
1325 // generate mask for each site from molecular mask
1326 std::vector<char, autopas::AlignedAllocator<char>> siteMask;
1327 siteMask.reserve(siteCountNeighbors);
1328
1329 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1330 const auto neighborMolIndex = neighborList[neighborMol]; // index of neighbor mol in soa
1331 for (size_t siteB = 0; siteB < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++siteB) {
1332 siteMask.emplace_back(molMask[neighborMol]);
1333 }
1334 }
1335
1336 // sums used for prime mol
1337 SoAFloatPrecision forceSumX = 0.;
1338 SoAFloatPrecision forceSumY = 0.;
1339 SoAFloatPrecision forceSumZ = 0.;
1340 SoAFloatPrecision torqueSumX = 0.;
1341 SoAFloatPrecision torqueSumY = 0.;
1342 SoAFloatPrecision torqueSumZ = 0.;
1343
1344 // - actual LJ calculation -
1345
1346 for (size_t primeSite = 0; primeSite < siteCountMolPrime; ++primeSite) {
1347 const auto rotatedPrimeSitePositionX = rotatedSitePositionsPrime[primeSite][0];
1348 const auto rotatedPrimeSitePositionY = rotatedSitePositionsPrime[primeSite][1];
1349 const auto rotatedPrimeSitePositionZ = rotatedSitePositionsPrime[primeSite][2];
1350
1351 const auto exactPrimeSitePositionX = rotatedPrimeSitePositionX + xptr[indexPrime];
1352 const auto exactPrimeSitePositionY = rotatedPrimeSitePositionY + yptr[indexPrime];
1353 const auto exactPrimeSitePositionZ = rotatedPrimeSitePositionZ + zptr[indexPrime];
1354
1355 // generate parameter data for chosen site
1356 if constexpr (useMixing) {
1357 const auto primeSiteType = siteTypesPrime[primeSite];
1358
1359 size_t siteIndex = 0;
1360 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1361 const auto neighborMolIndex = neighborList[neighborMol];
1362 const auto siteTypesOfNeighborMol = _PPLibrary->getSiteTypes(typeptr[neighborMolIndex]);
1363
1364 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1365 const auto mixingData = _PPLibrary->getLJMixingData(primeSiteType, siteTypesOfNeighborMol[site]);
1366 sigmaSquareds[siteIndex] = mixingData.sigmaSquared;
1367 epsilon24s[siteIndex] = mixingData.epsilon24;
1368 if constexpr (applyShift) {
1369 shift6s[siteIndex] = mixingData.shift6;
1370 }
1371 ++siteIndex;
1372 }
1373 }
1374 }
1375
1376#pragma omp simd reduction(+ : forceSumX, forceSumY, forceSumZ, torqueSumX, torqueSumY, torqueSumZ)
1377 for (size_t neighborSite = 0; neighborSite < siteCountNeighbors; ++neighborSite) {
1378 const SoAFloatPrecision sigmaSquared = useMixing ? sigmaSquareds[neighborSite] : const_sigmaSquared;
1379 const SoAFloatPrecision epsilon24 = useMixing ? epsilon24s[neighborSite] : const_epsilon24;
1380 const SoAFloatPrecision shift6 = applyShift ? (useMixing ? shift6s[neighborSite] : const_shift6) : 0;
1381
1382 const bool isNeighborSiteOwned = !calculateGlobals || isNeighborSiteOwnedArr[neighborSite];
1383
1384 const auto displacementX = exactPrimeSitePositionX - exactNeighborSitePositionX[neighborSite];
1385 const auto displacementY = exactPrimeSitePositionY - exactNeighborSitePositionY[neighborSite];
1386 const auto displacementZ = exactPrimeSitePositionZ - exactNeighborSitePositionZ[neighborSite];
1387
1388 const auto distanceSquaredX = displacementX * displacementX;
1389 const auto distanceSquaredY = displacementY * displacementY;
1390 const auto distanceSquaredZ = displacementZ * displacementZ;
1391
1392 const auto distanceSquared = distanceSquaredX + distanceSquaredY + distanceSquaredZ;
1393
1394 const auto invDistSquared = 1. / distanceSquared;
1395 const auto lj2 = sigmaSquared * invDistSquared;
1396 const auto lj6 = lj2 * lj2 * lj2;
1397 const auto lj12 = lj6 * lj6;
1398 const auto lj12m6 = lj12 - lj6;
1399 const auto scalarMultiple = siteMask[neighborSite] ? epsilon24 * (lj12 + lj12m6) * invDistSquared : 0.;
1400
1401 // calculate forces
1402 const auto forceX = scalarMultiple * displacementX;
1403 const auto forceY = scalarMultiple * displacementY;
1404 const auto forceZ = scalarMultiple * displacementZ;
1405
1406 forceSumX += forceX;
1407 forceSumY += forceY;
1408 forceSumZ += forceZ;
1409
1410 torqueSumX += rotatedPrimeSitePositionY * forceZ - rotatedPrimeSitePositionZ * forceY;
1411 torqueSumY += rotatedPrimeSitePositionZ * forceX - rotatedPrimeSitePositionX * forceZ;
1412 torqueSumZ += rotatedPrimeSitePositionX * forceY - rotatedPrimeSitePositionY * forceX;
1413
1414 // N3L
1415 if (newton3) {
1416 siteForceX[neighborSite] -= forceX;
1417 siteForceY[neighborSite] -= forceY;
1418 siteForceZ[neighborSite] -= forceZ;
1419 }
1420
1421 // calculate globals
1422 if constexpr (calculateGlobals) {
1423 const auto potentialEnergy6 = siteMask[neighborSite] ? (epsilon24 * lj12m6 + shift6) : 0.;
1424 const auto virialX = displacementX * forceX;
1425 const auto virialY = displacementY * forceY;
1426 const auto virialZ = displacementZ * forceZ;
1427
1428 // We add 6 times the potential energy for each owned particle. The total sum is corrected in endTraversal().
1429 const auto ownershipFactor =
1430 newton3 ? (ownedStatePrime == autopas::OwnershipState::owned ? 1. : 0.) + (isNeighborSiteOwned ? 1. : 0.)
1431 : (ownedStatePrime == autopas::OwnershipState::owned ? 1. : 0.);
1432 potentialEnergySum += potentialEnergy6 * ownershipFactor;
1433 virialSumX += virialX * ownershipFactor;
1434 virialSumY += virialY * ownershipFactor;
1435 virialSumZ += virialZ * ownershipFactor;
1436 }
1437 }
1438 }
1439 // Add forces to prime mol
1440 fxptr[indexPrime] += forceSumX;
1441 fyptr[indexPrime] += forceSumY;
1442 fzptr[indexPrime] += forceSumZ;
1443 txptr[indexPrime] += torqueSumX;
1444 typtr[indexPrime] += torqueSumY;
1445 tzptr[indexPrime] += torqueSumZ;
1446
1447 // Reduce forces on individual neighbor sites to molecular forces & torques if newton3=true
1448 if constexpr (newton3) {
1449 if constexpr (useMixing) {
1450 size_t siteIndex = 0;
1451 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1452 const auto neighborMolIndex = neighborList[neighborMol];
1453 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1454 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1455 _PPLibrary->getSitePositions(typeptr[neighborMolIndex]));
1456 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1457 fxptr[neighborMolIndex] += siteForceX[siteIndex];
1458 fyptr[neighborMolIndex] += siteForceY[siteIndex];
1459 fzptr[neighborMolIndex] += siteForceZ[siteIndex];
1460 txptr[neighborMolIndex] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
1461 rotatedSitePositions[site][2] * siteForceY[siteIndex];
1462 typtr[neighborMolIndex] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
1463 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
1464 tzptr[neighborMolIndex] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
1465 rotatedSitePositions[site][1] * siteForceX[siteIndex];
1466 ++siteIndex;
1467 }
1468 }
1469 } else {
1470 size_t siteIndex = 0;
1471 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1472 const auto neighborMolIndex = neighborList[neighborMol];
1473 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1474 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1475 const_unrotatedSitePositions);
1476 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1477 fxptr[neighborMolIndex] += siteForceX[siteIndex];
1478 fyptr[neighborMolIndex] += siteForceY[siteIndex];
1479 fzptr[neighborMolIndex] += siteForceZ[siteIndex];
1480 txptr[neighborMolIndex] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
1481 rotatedSitePositions[site][2] * siteForceY[siteIndex];
1482 typtr[neighborMolIndex] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
1483 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
1484 txptr[neighborMolIndex] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
1485 rotatedSitePositions[site][1] * siteForceX[siteIndex];
1486 ++siteIndex;
1487 }
1488 }
1489 }
1490 }
1491
1492 if constexpr (calculateGlobals) {
1493 const auto threadNum = autopas::autopas_get_thread_num();
1494
1495 _aosThreadData[threadNum].potentialEnergySum += potentialEnergySum;
1496 _aosThreadData[threadNum].virialSum[0] += virialSumX;
1497 _aosThreadData[threadNum].virialSum[1] += virialSumY;
1498 _aosThreadData[threadNum].virialSum[2] += virialSumZ;
1499 }
1500 }
1501
1505 class AoSThreadData {
1506 public:
1507 AoSThreadData() : virialSum{0., 0., 0.}, potentialEnergySum{0.}, __remainingTo64{} {}
1508 void setZero() {
1509 virialSum = {0., 0., 0.};
1510 potentialEnergySum = 0.;
1511 }
1512
1513 // variables
1514 std::array<double, 3> virialSum;
1515 double potentialEnergySum;
1516
1517 private:
1518 // dummy parameter to get the right size (64 bytes)
1519 double __remainingTo64[(64 - 4 * sizeof(double)) / sizeof(double)];
1520 };
1521
1522 static_assert(sizeof(AoSThreadData) % 64 == 0, "AoSThreadData has wrong size (should be multiple of 64)");
1523
1527 std::vector<AoSThreadData> _aosThreadData;
1528};
1529} // namespace mdLib
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
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
intType getNumSites(intType i) const
Get number of sites of a multi-site molecule.
Definition: ParticlePropertiesLibrary.h:554
std::vector< std::array< floatType, 3 > > getSitePositions(intType i) const
Get relative site positions to a multi-site molecule's center-of-mass.
Definition: ParticlePropertiesLibrary.h:517
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
auto getLJMixingData(intType i, intType j) const
Get complete mixing data for one pair of LJ site types.
Definition: ParticlePropertiesLibrary.h:234
std::vector< intType > getSiteTypes(intType i) const
Get site types of a multi-site molecule.
Definition: ParticlePropertiesLibrary.h:528
AlignedAllocator class.
Definition: AlignedAllocator.h:29
PairwiseFunctor class.
Definition: PairwiseFunctor.h:31
PairwiseFunctor(double cutoff)
Constructor.
Definition: PairwiseFunctor.h:42
View on a fixed part of a SoA between a start index and an end index.
Definition: SoAView.h:23
size_t size() const
Returns the number of particles in the view.
Definition: SoAView.h:83
Default exception class for autopas exceptions.
Definition: ExceptionHandler.h:116
A functor to handle Lennard-Jones interactions between two Multisite Molecules.
Definition: LJMultisiteFunctor.h:49
void SoAFunctorSingle(autopas::SoAView< SoAArraysType > soa, bool newton3) final
PairwiseFunctor for structure of arrays (SoA)
Definition: LJMultisiteFunctor.h:281
void AoSFunctor(Particle_T &particleA, Particle_T &particleB, bool newton3) final
Functor for arrays of structures (AoS).
Definition: LJMultisiteFunctor.h:181
LJMultisiteFunctor()=delete
Delete Default constructor.
bool isRelevantForTuning() final
Specifies whether the functor should be considered for the auto-tuning process.
Definition: LJMultisiteFunctor.h:164
LJMultisiteFunctor(double cutoff, ParticlePropertiesLibrary< double, size_t > &particlePropertiesLibrary)
Constructor for Functor with particle mixing enabled.
Definition: LJMultisiteFunctor.h:154
LJMultisiteFunctor(double cutoff)
Constructor for Functor with particle mixing disabled.
Definition: LJMultisiteFunctor.h:140
void initTraversal() final
Reset the global values.
Definition: LJMultisiteFunctor.h:712
static constexpr bool getMixing()
Definition: LJMultisiteFunctor.h:683
static constexpr auto getComputedAttr()
Get attributes computed by this functor.
Definition: LJMultisiteFunctor.h:674
void setParticleProperties(SoAFloatPrecision epsilon24, SoAFloatPrecision sigmaSquared, std::vector< std::array< SoAFloatPrecision, 3 > > sitePositionsLJ)
Sets the molecule properties constants for this functor.
Definition: LJMultisiteFunctor.h:629
bool allowsNewton3() final
Specifies whether the functor is capable of Newton3-like functors.
Definition: LJMultisiteFunctor.h:166
bool allowsNonNewton3() final
Specifies whether the functor is capable of non-Newton3-like functors.
Definition: LJMultisiteFunctor.h:170
double getPotentialEnergy()
Get the potential energy.
Definition: LJMultisiteFunctor.h:757
std::string getName() final
Returns name of functor.
Definition: LJMultisiteFunctor.h:162
unsigned long getNumFlopsPerKernelCall(size_t molAType, size_t molBType, bool newton3)
Get the number of flops used per kernel call - i.e.
Definition: LJMultisiteFunctor.h:695
void SoAFunctorPair(autopas::SoAView< SoAArraysType > soa1, autopas::SoAView< SoAArraysType > soa2, const bool newton3) final
PairwiseFunctor for structure of arrays (SoA)
Definition: LJMultisiteFunctor.h:596
static constexpr auto getNeededAttr(std::false_type)
Get attributes needed for computation without N3 optimization.
Definition: LJMultisiteFunctor.h:659
void SoAFunctorVerlet(autopas::SoAView< SoAArraysType > soa, const size_t indexFirst, const std::vector< size_t, autopas::AlignedAllocator< size_t > > &neighborList, bool newton3) final
PairwiseFunctor for structure of arrays (SoA) for neighbor lists.
Definition: LJMultisiteFunctor.h:610
double getVirial()
Get the virial.
Definition: LJMultisiteFunctor.h:775
void endTraversal(bool newton3) final
Postprocesses global values, e.g.
Definition: LJMultisiteFunctor.h:725
static constexpr auto getNeededAttr()
Get attributes needed for computation.
Definition: LJMultisiteFunctor.h:644
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, SIZE > mulScalar(const std::array< T, SIZE > &a, T s)
Multiplies a scalar s to each element of array a and returns the result.
Definition: ArrayMath.h:181
constexpr std::array< T, SIZE > sub(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Subtracts array b from array a and returns the result.
Definition: ArrayMath.h:45
constexpr std::array< T, SIZE > add(const std::array< T, SIZE > &a, const std::array< T, SIZE > &b)
Adds two arrays, returns the result.
Definition: ArrayMath.h:28
constexpr std::array< T, 3 > cross(const std::array< T, 3 > &a, const std::array< T, 3 > &b)
Generates the cross product of two arrays of 3 floats.
Definition: ArrayMath.h:249
std::vector< std::array< double, 3 > > rotateVectorOfPositions(const std::array< double, 4 > &q, const std::vector< std::array< double, 3 > > &positionVector)
Rotates a std::vector of 3D positions.
Definition: Quaternion.cpp:13
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:34
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
OwnershipState
Enum that specifies the state of ownership.
Definition: OwnershipState.h:20
@ 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