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 std::span<const size_t> neighborList, bool newton3) final {
612 if (soa.size() == 0 or neighborList.empty()) return;
613 if (newton3) {
614 SoAFunctorVerletImpl<true>(soa, indexFirst, neighborList);
615 } else {
616 SoAFunctorVerletImpl<false>(soa, indexFirst, neighborList);
617 }
618 }
619
628 void setParticleProperties(SoAFloatPrecision epsilon24, SoAFloatPrecision sigmaSquared,
629 std::vector<std::array<SoAFloatPrecision, 3>> sitePositionsLJ) {
630 _epsilon24 = epsilon24;
631 _sigmaSquared = sigmaSquared;
632 if (applyShift) {
633 _shift6 = ParticlePropertiesLibrary<double, size_t>::calcShift6(_epsilon24, _sigmaSquared, _cutoffSquared);
634 } else {
635 _shift6 = 0;
636 }
637 _sitePositionsLJ = sitePositionsLJ;
638 }
639
643 constexpr static auto getNeededAttr() {
644 return std::array<typename Particle_T::AttributeNames, 16>{
645 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
646 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
647 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY,
648 Particle_T::AttributeNames::forceZ, Particle_T::AttributeNames::quaternion0,
649 Particle_T::AttributeNames::quaternion1, Particle_T::AttributeNames::quaternion2,
650 Particle_T::AttributeNames::quaternion3, Particle_T::AttributeNames::torqueX,
651 Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ,
652 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
653 }
654
658 constexpr static auto getNeededAttr(std::false_type) {
659 return std::array<typename Particle_T::AttributeNames, 16>{
660 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
661 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
662 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY,
663 Particle_T::AttributeNames::forceZ, Particle_T::AttributeNames::quaternion0,
664 Particle_T::AttributeNames::quaternion1, Particle_T::AttributeNames::quaternion2,
665 Particle_T::AttributeNames::quaternion3, Particle_T::AttributeNames::torqueX,
666 Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ,
667 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
668 }
669
673 constexpr static auto getComputedAttr() {
674 return std::array<typename Particle_T::AttributeNames, 6>{
675 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY, Particle_T::AttributeNames::forceZ,
676 Particle_T::AttributeNames::torqueX, Particle_T::AttributeNames::torqueY, Particle_T::AttributeNames::torqueZ};
677 }
678
682 constexpr static bool getMixing() { return useMixing; }
683
694 unsigned long getNumFlopsPerKernelCall(size_t molAType, size_t molBType, bool newton3) {
695 // Site-to-site displacement: 6 (3 in the SoA case, but this requires O(N) precomputing site positions)
696 // Site-to-site distance squared: 4
697 // Compute scale: 9
698 // Apply scale to force: With newton3: 6, Without: 3
699 // Apply scale to torque: With newton3 18, Without: 9 (0 in SoA case, with O(N) post computing)
700 // Site-to-site total: With newton3: 33, Without: 26
701 // (SoA total: With N3L: 22, Without N3L: 19)
702 // Above multiplied by number sites of i * number sites of j
703 const unsigned long siteToSiteFlops = newton3 ? 33ul : 26ul;
704 return _PPLibrary->getNumSites(molAType) * _PPLibrary->getNumSites(molBType) * siteToSiteFlops;
705 }
706
711 void initTraversal() final {
712 _potentialEnergySum = 0;
713 _virialSum = {0., 0., 0.};
714 _postProcessed = false;
715 for (size_t i = 0; i < _aosThreadData.size(); i++) {
716 _aosThreadData[i].setZero();
717 }
718 }
719
724 void endTraversal(bool newton3) final {
725 using namespace autopas::utils::ArrayMath::literals;
726
727 if (_postProcessed) {
729 "Already postprocessed, endTraversal(bool newton3) was called twice without calling initTraversal().");
730 }
731 if (calculateGlobals) {
732 for (size_t i = 0; i < _aosThreadData.size(); ++i) {
733 _potentialEnergySum += _aosThreadData[i].potentialEnergySum;
734 _virialSum += _aosThreadData[i].virialSum;
735 }
736
737 // For each interaction, we added the full contribution for both particles. Divide by 2 here, so that each
738 // contribution is only counted once per pair.
739 _potentialEnergySum *= 0.5;
740 _virialSum *= 0.5;
741
742 // We have always calculated 6*potentialEnergy, so we divide by 6 here!
743 _potentialEnergySum /= 6.;
744 _postProcessed = true;
745
746 AutoPasLog(DEBUG, "Final potential energy {}", _potentialEnergySum);
747 AutoPasLog(DEBUG, "Final virial {}", _virialSum[0] + _virialSum[1] + _virialSum[2]);
748 }
749 }
750
757 if (not calculateGlobals) {
759 "Trying to get potential energy even though calculateGlobals is false. If you want this functor to calculate "
760 "global "
761 "values, please specify calculateGlobals to be true.");
762 }
763 if (not _postProcessed) {
765 "Cannot get potential energy, because endTraversal was not called.");
766 }
767 return _potentialEnergySum;
768 }
769
774 double getVirial() {
775 if (not calculateGlobals) {
777 "Trying to get virial even though calculateGlobals is false. If you want this functor to calculate global "
778 "values, please specify calculateGlobals to be true.");
779 }
780 if (not _postProcessed) {
782 "Cannot get virial, because endTraversal was not called.");
783 }
784 return _virialSum[0] + _virialSum[1] + _virialSum[2];
785 }
786
787 private:
794 template <bool newton3>
795 void SoAFunctorPairImpl(autopas::SoAView<SoAArraysType> soaA, autopas::SoAView<SoAArraysType> soaB) {
796 if (soaA.size() == 0 || soaB.size() == 0) return;
797
798 const auto *const __restrict xAptr = soaA.template begin<Particle_T::AttributeNames::posX>();
799 const auto *const __restrict yAptr = soaA.template begin<Particle_T::AttributeNames::posY>();
800 const auto *const __restrict zAptr = soaA.template begin<Particle_T::AttributeNames::posZ>();
801 const auto *const __restrict xBptr = soaB.template begin<Particle_T::AttributeNames::posX>();
802 const auto *const __restrict yBptr = soaB.template begin<Particle_T::AttributeNames::posY>();
803 const auto *const __restrict zBptr = soaB.template begin<Particle_T::AttributeNames::posZ>();
804
805 const auto *const __restrict ownedStatePtrA = soaA.template begin<Particle_T::AttributeNames::ownershipState>();
806 const auto *const __restrict ownedStatePtrB = soaB.template begin<Particle_T::AttributeNames::ownershipState>();
807
808 const auto *const __restrict q0Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion0>();
809 const auto *const __restrict q1Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion1>();
810 const auto *const __restrict q2Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion2>();
811 const auto *const __restrict q3Aptr = soaA.template begin<Particle_T::AttributeNames::quaternion3>();
812 const auto *const __restrict q0Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion0>();
813 const auto *const __restrict q1Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion1>();
814 const auto *const __restrict q2Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion2>();
815 const auto *const __restrict q3Bptr = soaB.template begin<Particle_T::AttributeNames::quaternion3>();
816
817 SoAFloatPrecision *const __restrict fxAptr = soaA.template begin<Particle_T::AttributeNames::forceX>();
818 SoAFloatPrecision *const __restrict fyAptr = soaA.template begin<Particle_T::AttributeNames::forceY>();
819 SoAFloatPrecision *const __restrict fzAptr = soaA.template begin<Particle_T::AttributeNames::forceZ>();
820 SoAFloatPrecision *const __restrict fxBptr = soaB.template begin<Particle_T::AttributeNames::forceX>();
821 SoAFloatPrecision *const __restrict fyBptr = soaB.template begin<Particle_T::AttributeNames::forceY>();
822 SoAFloatPrecision *const __restrict fzBptr = soaB.template begin<Particle_T::AttributeNames::forceZ>();
823
824 SoAFloatPrecision *const __restrict txAptr = soaA.template begin<Particle_T::AttributeNames::torqueX>();
825 SoAFloatPrecision *const __restrict tyAptr = soaA.template begin<Particle_T::AttributeNames::torqueY>();
826 SoAFloatPrecision *const __restrict tzAptr = soaA.template begin<Particle_T::AttributeNames::torqueZ>();
827 SoAFloatPrecision *const __restrict txBptr = soaB.template begin<Particle_T::AttributeNames::torqueX>();
828 SoAFloatPrecision *const __restrict tyBptr = soaB.template begin<Particle_T::AttributeNames::torqueY>();
829 SoAFloatPrecision *const __restrict tzBptr = soaB.template begin<Particle_T::AttributeNames::torqueZ>();
830
831 [[maybe_unused]] auto *const __restrict typeptrA = soaA.template begin<Particle_T::AttributeNames::typeId>();
832 [[maybe_unused]] auto *const __restrict typeptrB = soaB.template begin<Particle_T::AttributeNames::typeId>();
833
834 SoAFloatPrecision potentialEnergySum = 0.;
835 SoAFloatPrecision virialSumX = 0.;
836 SoAFloatPrecision virialSumY = 0.;
837 SoAFloatPrecision virialSumZ = 0.;
838
839 // local redeclarations to help compilers
840 const SoAFloatPrecision cutoffSquared = _cutoffSquared;
841
842 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> sigmaSquareds;
843 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> epsilon24s;
844 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> shift6s;
845
846 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBx;
847 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBy;
848 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactSitePositionBz;
849
850 // we require arrays for forces for sites to maintain SIMD in site-site calculations
851 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBx;
852 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBy;
853 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceBz;
854
855 std::vector<size_t, autopas::AlignedAllocator<size_t>> siteTypesB;
856 std::vector<char, autopas::AlignedAllocator<char>> isSiteOwnedBArr;
857
858 const SoAFloatPrecision const_sigmaSquared = _sigmaSquared;
859 const SoAFloatPrecision const_epsilon24 = _epsilon24;
860 const SoAFloatPrecision const_shift6 = _shift6;
861
862 const auto const_unrotatedSitePositions = _sitePositionsLJ;
863
864 // count number of sites in both SoAs
865 size_t siteCountB = 0;
866 if constexpr (useMixing) {
867 for (size_t mol = 0; mol < soaB.size(); ++mol) {
868 siteCountB += _PPLibrary->getNumSites(typeptrB[mol]);
869 }
870 } else {
871 siteCountB = const_unrotatedSitePositions.size() * soaB.size();
872 }
873
874 // pre-reserve std::vectors
875 exactSitePositionBx.reserve(siteCountB);
876 exactSitePositionBy.reserve(siteCountB);
877 exactSitePositionBz.reserve(siteCountB);
878
879 if constexpr (useMixing) {
880 siteTypesB.reserve(siteCountB);
881 }
882
883 siteForceBx.reserve(siteCountB);
884 siteForceBy.reserve(siteCountB);
885 siteForceBz.reserve(siteCountB);
886
887 if constexpr (calculateGlobals) {
888 // this is only needed for vectorization when calculating globals
889 isSiteOwnedBArr.reserve(siteCountB);
890 }
891
892 if constexpr (useMixing) {
893 siteTypesB.reserve(siteCountB);
894 sigmaSquareds.reserve(siteCountB);
895 epsilon24s.reserve(siteCountB);
896 if constexpr (applyShift) {
897 shift6s.reserve(siteCountB);
898 }
899 }
900
901 // Fill site-wise std::vectors for SIMD
902 if constexpr (useMixing) {
903 size_t siteIndex = 0;
904 for (size_t mol = 0; mol < soaB.size(); ++mol) {
905 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
906 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, _PPLibrary->getSitePositions(typeptrB[mol]));
907 const auto siteTypesOfMol = _PPLibrary->getSiteTypes(typeptrB[mol]);
908
909 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptrB[mol]); ++site) {
910 exactSitePositionBx[siteIndex] = rotatedSitePositions[site][0] + xBptr[mol];
911 exactSitePositionBy[siteIndex] = rotatedSitePositions[site][1] + yBptr[mol];
912 exactSitePositionBz[siteIndex] = rotatedSitePositions[site][2] + zBptr[mol];
913 siteTypesB[siteIndex] = siteTypesOfMol[site];
914 siteForceBx[siteIndex] = 0.;
915 siteForceBy[siteIndex] = 0.;
916 siteForceBz[siteIndex] = 0.;
917 if (calculateGlobals) {
918 isSiteOwnedBArr[siteIndex] = ownedStatePtrB[mol] == autopas::OwnershipState::owned;
919 }
920 ++siteIndex;
921 }
922 }
923 } else {
924 size_t siteIndex = 0;
925 for (size_t mol = 0; mol < soaB.size(); mol++) {
926 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
927 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, const_unrotatedSitePositions);
928 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
929 exactSitePositionBx[siteIndex] = rotatedSitePositions[site][0] + xBptr[mol];
930 exactSitePositionBy[siteIndex] = rotatedSitePositions[site][1] + yBptr[mol];
931 exactSitePositionBz[siteIndex] = rotatedSitePositions[site][2] + zBptr[mol];
932 siteForceBx[siteIndex] = 0.;
933 siteForceBy[siteIndex] = 0.;
934 siteForceBz[siteIndex] = 0.;
935 if (calculateGlobals) {
936 isSiteOwnedBArr[siteIndex] = ownedStatePtrB[mol] == autopas::OwnershipState::owned;
937 }
938 ++siteIndex;
939 }
940 }
941 }
942
943 // main force calculation loop
944 for (size_t molA = 0; molA < soaA.size(); ++molA) {
945 const auto ownedStateA = ownedStatePtrA[molA];
946 if (ownedStateA == autopas::OwnershipState::dummy) {
947 continue;
948 }
949
950 const auto noSitesInMolA =
951 useMixing ? _PPLibrary->getNumSites(typeptrA[molA]) : const_unrotatedSitePositions.size();
952 const auto unrotatedSitePositionsA =
953 useMixing ? _PPLibrary->getSitePositions(typeptrA[molA]) : const_unrotatedSitePositions;
954
955 const auto rotatedSitePositionsA = autopas::utils::quaternion::rotateVectorOfPositions(
956 {q0Aptr[molA], q1Aptr[molA], q2Aptr[molA], q3Aptr[molA]}, unrotatedSitePositionsA);
957
958 // create mask over every mol in cell B (char to keep arrays aligned)
959 std::vector<char, autopas::AlignedAllocator<char>> molMask;
960 molMask.reserve(soaB.size());
961
962#pragma omp simd
963 for (size_t molB = 0; molB < soaB.size(); ++molB) {
964 const auto ownedStateB = ownedStatePtrB[molB];
965
966 const auto displacementCoMX = xAptr[molA] - xBptr[molB];
967 const auto displacementCoMY = yAptr[molA] - yBptr[molB];
968 const auto displacementCoMZ = zAptr[molA] - zBptr[molB];
969
970 const auto distanceSquaredCoMX = displacementCoMX * displacementCoMX;
971 const auto distanceSquaredCoMY = displacementCoMY * displacementCoMY;
972 const auto distanceSquaredCoMZ = displacementCoMZ * displacementCoMZ;
973
974 const auto distanceSquaredCoM = distanceSquaredCoMX + distanceSquaredCoMY + distanceSquaredCoMZ;
975
976 // mask sites of molecules beyond cutoff or if molecule is a dummy
977 molMask[molB] = distanceSquaredCoM <= cutoffSquared and ownedStateB != autopas::OwnershipState::dummy;
978 }
979
980 // generate mask for each site in cell B from molecular mask
981 std::vector<char, autopas::AlignedAllocator<char>> siteMask;
982 siteMask.reserve(siteCountB);
983
984 for (size_t molB = 0; molB < soaB.size(); ++molB) {
985 for (size_t siteB = 0; siteB < _PPLibrary->getNumSites(typeptrB[molB]); ++siteB) {
986 siteMask.emplace_back(molMask[molB]);
987 }
988 }
989
990 // sums used for molA
991 SoAFloatPrecision forceSumX = 0.;
992 SoAFloatPrecision forceSumY = 0.;
993 SoAFloatPrecision forceSumZ = 0.;
994 SoAFloatPrecision torqueSumX = 0.;
995 SoAFloatPrecision torqueSumY = 0.;
996 SoAFloatPrecision torqueSumZ = 0.;
997
998 for (size_t siteA = 0; siteA < noSitesInMolA; ++siteA) {
999 if (useMixing) {
1000 // preload sigmas, epsilons, and shifts
1001 for (size_t siteB = 0; siteB < siteCountB; ++siteB) {
1002 const auto mixingData =
1003 _PPLibrary->getLJMixingData(_PPLibrary->getSiteTypes(typeptrA[molA])[siteA], siteTypesB[siteB]);
1004 sigmaSquareds[siteB] = mixingData.sigmaSquared;
1005 epsilon24s[siteB] = mixingData.epsilon24;
1006 if (applyShift) {
1007 shift6s[siteB] = mixingData.shift6;
1008 }
1009 }
1010 }
1011
1012 const auto rotatedSitePositionAx = rotatedSitePositionsA[siteA][0];
1013 const auto rotatedSitePositionAy = rotatedSitePositionsA[siteA][1];
1014 const auto rotatedSitePositionAz = rotatedSitePositionsA[siteA][2];
1015
1016 const auto exactSitePositionAx = rotatedSitePositionAx + xAptr[molA];
1017 const auto exactSitePositionAy = rotatedSitePositionAy + yAptr[molA];
1018 const auto exactSitePositionAz = rotatedSitePositionAz + zAptr[molA];
1019
1020#pragma omp simd reduction (+ : forceSumX, forceSumY, forceSumZ, torqueSumX, torqueSumY, torqueSumZ, potentialEnergySum, virialSumX, virialSumY, virialSumZ)
1021 for (size_t siteB = 0; siteB < siteCountB; ++siteB) {
1022 const SoAFloatPrecision sigmaSquared = useMixing ? sigmaSquareds[siteB] : const_sigmaSquared;
1023 const SoAFloatPrecision epsilon24 = useMixing ? epsilon24s[siteB] : const_epsilon24;
1024 const SoAFloatPrecision shift6 = applyShift ? (useMixing ? shift6s[siteB] : const_shift6) : 0;
1025
1026 const auto isSiteOwnedB = !calculateGlobals || isSiteOwnedBArr[siteB];
1027
1028 const auto displacementX = exactSitePositionAx - exactSitePositionBx[siteB];
1029 const auto displacementY = exactSitePositionAy - exactSitePositionBy[siteB];
1030 const auto displacementZ = exactSitePositionAz - exactSitePositionBz[siteB];
1031
1032 const auto distanceSquaredX = displacementX * displacementX;
1033 const auto distanceSquaredY = displacementY * displacementY;
1034 const auto distanceSquaredZ = displacementZ * displacementZ;
1035
1036 const auto distanceSquared = distanceSquaredX + distanceSquaredY + distanceSquaredZ;
1037
1038 const auto invDistSquared = 1. / distanceSquared;
1039 const auto lj2 = sigmaSquared * invDistSquared;
1040 const auto lj6 = lj2 * lj2 * lj2;
1041 const auto lj12 = lj6 * lj6;
1042 const auto lj12m6 = lj12 - lj6;
1043 const auto scalarMultiple = siteMask[siteB] ? epsilon24 * (lj12 + lj12m6) * invDistSquared : 0.;
1044
1045 // calculate forces
1046 const auto forceX = scalarMultiple * displacementX;
1047 const auto forceY = scalarMultiple * displacementY;
1048 const auto forceZ = scalarMultiple * displacementZ;
1049
1050 forceSumX += forceX;
1051 forceSumY += forceY;
1052 forceSumZ += forceZ;
1053
1054 torqueSumX += rotatedSitePositionAy * forceZ - rotatedSitePositionAz * forceY;
1055 torqueSumY += rotatedSitePositionAz * forceX - rotatedSitePositionAx * forceZ;
1056 torqueSumZ += rotatedSitePositionAx * forceY - rotatedSitePositionAy * forceX;
1057
1058 // N3L ( total molecular forces + torques to be determined later )
1059 if constexpr (newton3) {
1060 siteForceBx[siteB] -= forceX;
1061 siteForceBy[siteB] -= forceY;
1062 siteForceBz[siteB] -= forceZ;
1063 }
1064
1065 // globals
1066 if constexpr (calculateGlobals) {
1067 const auto potentialEnergy6 = siteMask[siteB] ? (epsilon24 * lj12m6 + shift6) : 0.;
1068 const auto virialX = displacementX * forceX;
1069 const auto virialY = displacementY * forceY;
1070 const auto virialZ = displacementZ * forceZ;
1071
1072 // We add 6 times the potential energy for each owned particle. The total sum is corrected in
1073 // endTraversal().
1074 const auto ownershipFactor =
1075 newton3 ? (ownedStateA == autopas::OwnershipState::owned ? 1. : 0.) + (isSiteOwnedB ? 1. : 0.)
1076 : (ownedStateA == autopas::OwnershipState::owned ? 1. : 0.);
1077 potentialEnergySum += potentialEnergy6 * ownershipFactor;
1078 virialSumX += virialX * ownershipFactor;
1079 virialSumY += virialY * ownershipFactor;
1080 virialSumZ += virialZ * ownershipFactor;
1081 }
1082 }
1083 }
1084 fxAptr[molA] += forceSumX;
1085 fyAptr[molA] += forceSumY;
1086 fzAptr[molA] += forceSumZ;
1087 txAptr[molA] += torqueSumX;
1088 tyAptr[molA] += torqueSumY;
1089 tzAptr[molA] += torqueSumZ;
1090 }
1091
1092 // reduce the forces on individual sites in SoA B to total forces & torques on whole molecules
1093 if constexpr (useMixing) {
1094 size_t siteIndex = 0;
1095 for (size_t mol = 0; mol < soaB.size(); ++mol) {
1096 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1097 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, _PPLibrary->getSitePositions(typeptrB[mol]));
1098 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptrB[mol]); ++site) {
1099 fxBptr[mol] += siteForceBx[siteIndex];
1100 fyBptr[mol] += siteForceBy[siteIndex];
1101 fzBptr[mol] += siteForceBz[siteIndex];
1102 txBptr[mol] += rotatedSitePositions[site][1] * siteForceBz[siteIndex] -
1103 rotatedSitePositions[site][2] * siteForceBy[siteIndex];
1104 tyBptr[mol] += rotatedSitePositions[site][2] * siteForceBx[siteIndex] -
1105 rotatedSitePositions[site][0] * siteForceBz[siteIndex];
1106 tzBptr[mol] += rotatedSitePositions[site][0] * siteForceBy[siteIndex] -
1107 rotatedSitePositions[site][1] * siteForceBx[siteIndex];
1108 ++siteIndex;
1109 }
1110 }
1111 } else {
1112 size_t siteIndex = 0;
1113 for (size_t mol = 0; mol < soaB.size(); ++mol) {
1114 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1115 {q0Bptr[mol], q1Bptr[mol], q2Bptr[mol], q3Bptr[mol]}, const_unrotatedSitePositions);
1116 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1117 fxBptr[mol] += siteForceBx[siteIndex];
1118 fyBptr[mol] += siteForceBy[siteIndex];
1119 fzBptr[mol] += siteForceBz[siteIndex];
1120 txBptr[mol] += rotatedSitePositions[site][1] * siteForceBz[siteIndex] -
1121 rotatedSitePositions[site][2] * siteForceBy[siteIndex];
1122 tyBptr[mol] += rotatedSitePositions[site][2] * siteForceBx[siteIndex] -
1123 rotatedSitePositions[site][0] * siteForceBz[siteIndex];
1124 tzBptr[mol] += rotatedSitePositions[site][0] * siteForceBy[siteIndex] -
1125 rotatedSitePositions[site][1] * siteForceBx[siteIndex];
1126 ++siteIndex;
1127 }
1128 }
1129 }
1130 if constexpr (calculateGlobals) {
1131 const auto threadNum = autopas::autopas_get_thread_num();
1132 // SoAFunctorPairImpl obtains the potential energy * 12. For non-newton3, this sum is divided by 12 in
1133 // post-processing. For newton3, this sum is only divided by 6 in post-processing, so must be divided by 2 here.
1134 _aosThreadData[threadNum].potentialEnergySum += potentialEnergySum;
1135 _aosThreadData[threadNum].virialSum[0] += virialSumX;
1136 _aosThreadData[threadNum].virialSum[1] += virialSumY;
1137 _aosThreadData[threadNum].virialSum[2] += virialSumZ;
1138 }
1139 }
1140
1141 template <bool newton3>
1142 void SoAFunctorVerletImpl(autopas::SoAView<SoAArraysType> soa, const size_t indexPrime,
1143 std::span<const size_t> neighborList) {
1144 const auto *const __restrict ownedStatePtr = soa.template begin<Particle_T::AttributeNames::ownershipState>();
1145
1146 // Skip if primary particle is dummy
1147 const auto ownedStatePrime = ownedStatePtr[indexPrime];
1148 if (ownedStatePrime == autopas::OwnershipState::dummy) {
1149 return;
1150 }
1151
1152 const auto *const __restrict xptr = soa.template begin<Particle_T::AttributeNames::posX>();
1153 const auto *const __restrict yptr = soa.template begin<Particle_T::AttributeNames::posY>();
1154 const auto *const __restrict zptr = soa.template begin<Particle_T::AttributeNames::posZ>();
1155
1156 const auto *const __restrict q0ptr = soa.template begin<Particle_T::AttributeNames::quaternion0>();
1157 const auto *const __restrict q1ptr = soa.template begin<Particle_T::AttributeNames::quaternion1>();
1158 const auto *const __restrict q2ptr = soa.template begin<Particle_T::AttributeNames::quaternion2>();
1159 const auto *const __restrict q3ptr = soa.template begin<Particle_T::AttributeNames::quaternion3>();
1160
1161 SoAFloatPrecision *const __restrict fxptr = soa.template begin<Particle_T::AttributeNames::forceX>();
1162 SoAFloatPrecision *const __restrict fyptr = soa.template begin<Particle_T::AttributeNames::forceY>();
1163 SoAFloatPrecision *const __restrict fzptr = soa.template begin<Particle_T::AttributeNames::forceZ>();
1164
1165 SoAFloatPrecision *const __restrict txptr = soa.template begin<Particle_T::AttributeNames::torqueX>();
1166 SoAFloatPrecision *const __restrict typtr = soa.template begin<Particle_T::AttributeNames::torqueY>();
1167 SoAFloatPrecision *const __restrict tzptr = soa.template begin<Particle_T::AttributeNames::torqueZ>();
1168
1169 [[maybe_unused]] auto *const __restrict typeptr = soa.template begin<Particle_T::AttributeNames::typeId>();
1170
1171 SoAFloatPrecision potentialEnergySum = 0.;
1172 SoAFloatPrecision virialSumX = 0.;
1173 SoAFloatPrecision virialSumY = 0.;
1174 SoAFloatPrecision virialSumZ = 0.;
1175
1176 // the local redeclaration of the following values helps the SoAFloatPrecision-generation of various compilers.
1177 const SoAFloatPrecision cutoffSquared = _cutoffSquared;
1178 const auto const_unrotatedSitePositions = _sitePositionsLJ;
1179
1180 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> sigmaSquareds;
1181 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> epsilon24s;
1182 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> shift6s;
1183
1184 const auto const_sigmaSquared = _sigmaSquared;
1185 const auto const_epsilon24 = _epsilon24;
1186 const auto const_shift6 = _shift6;
1187
1188 const size_t neighborListSize = neighborList.size();
1189
1190 // Count sites
1191 const size_t siteCountMolPrime =
1192 useMixing ? _PPLibrary->getNumSites(typeptr[indexPrime]) : const_unrotatedSitePositions.size();
1193
1194 size_t siteCountNeighbors = 0; // site count of neighbours of primary molecule
1195 if constexpr (useMixing) {
1196 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1197 siteCountNeighbors += _PPLibrary->getNumSites(typeptr[neighborList[neighborMol]]);
1198 }
1199 } else {
1200 siteCountNeighbors = const_unrotatedSitePositions.size() * neighborListSize;
1201 }
1202
1203 // initialize site-wise arrays for neighbors
1204 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionX;
1205 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionY;
1206 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> exactNeighborSitePositionZ;
1207
1208 std::vector<size_t, autopas::AlignedAllocator<size_t>> siteTypesNeighbors;
1209 std::vector<char, autopas::AlignedAllocator<char>> isNeighborSiteOwnedArr;
1210
1211 // we require arrays for forces for sites to maintain SIMD in site-site calculations
1212 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceX;
1213 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceY;
1214 std::vector<SoAFloatPrecision, autopas::AlignedAllocator<SoAFloatPrecision>> siteForceZ;
1215
1216 // pre-reserve arrays
1217 exactNeighborSitePositionX.reserve(siteCountNeighbors);
1218 exactNeighborSitePositionY.reserve(siteCountNeighbors);
1219 exactNeighborSitePositionZ.reserve(siteCountNeighbors);
1220
1221 if constexpr (useMixing) {
1222 siteTypesNeighbors.reserve(siteCountNeighbors);
1223 }
1224
1225 siteForceX.reserve(siteCountNeighbors);
1226 siteForceY.reserve(siteCountNeighbors);
1227 siteForceZ.reserve(siteCountNeighbors);
1228
1229 if constexpr (calculateGlobals) {
1230 isNeighborSiteOwnedArr.reserve(siteCountNeighbors);
1231 }
1232
1233 if constexpr (useMixing) {
1234 sigmaSquareds.reserve(siteCountNeighbors);
1235 epsilon24s.reserve(siteCountNeighbors);
1236 if constexpr (applyShift) {
1237 shift6s.reserve(siteCountNeighbors);
1238 }
1239 }
1240
1241 const auto rotatedSitePositionsPrime =
1243 {q0ptr[indexPrime], q1ptr[indexPrime], q2ptr[indexPrime], q3ptr[indexPrime]},
1244 _PPLibrary->getSitePositions(typeptr[indexPrime]))
1245 : autopas::utils::quaternion::rotateVectorOfPositions(
1246 {q0ptr[indexPrime], q1ptr[indexPrime], q2ptr[indexPrime], q3ptr[indexPrime]},
1247 const_unrotatedSitePositions);
1248
1249 const auto siteTypesPrime = _PPLibrary->getSiteTypes(typeptr[indexPrime]); // this is not used if non-mixing
1250
1251 // generate site-wise arrays for neighbors of primary mol
1252 if constexpr (useMixing) {
1253 size_t siteIndex = 0;
1254 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1255 const auto neighborMolIndex = neighborList[neighborMol];
1256 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1257 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1258 _PPLibrary->getSitePositions(typeptr[neighborMolIndex]));
1259 const auto siteTypesOfMol = _PPLibrary->getSiteTypes(typeptr[neighborMolIndex]);
1260
1261 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1262 exactNeighborSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[neighborMolIndex];
1263 exactNeighborSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[neighborMolIndex];
1264 exactNeighborSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[neighborMolIndex];
1265 siteTypesNeighbors[siteIndex] = siteTypesOfMol[site];
1266 siteForceX[siteIndex] = 0.;
1267 siteForceY[siteIndex] = 0.;
1268 siteForceZ[siteIndex] = 0.;
1269 if (calculateGlobals) {
1270 isNeighborSiteOwnedArr[siteIndex] = ownedStatePtr[neighborMolIndex] == autopas::OwnershipState::owned;
1271 }
1272 ++siteIndex;
1273 }
1274 }
1275 } else {
1276 size_t siteIndex = 0;
1277 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1278 const auto neighborMolIndex = neighborList[neighborMol];
1279 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1280 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1281 const_unrotatedSitePositions);
1282 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1283 exactNeighborSitePositionX[siteIndex] = rotatedSitePositions[site][0] + xptr[neighborMolIndex];
1284 exactNeighborSitePositionY[siteIndex] = rotatedSitePositions[site][1] + yptr[neighborMolIndex];
1285 exactNeighborSitePositionZ[siteIndex] = rotatedSitePositions[site][2] + zptr[neighborMolIndex];
1286 siteForceX[siteIndex] = 0.;
1287 siteForceY[siteIndex] = 0.;
1288 siteForceZ[siteIndex] = 0.;
1289 if (calculateGlobals) {
1290 isNeighborSiteOwnedArr[siteIndex] = ownedStatePtr[neighborMolIndex] == autopas::OwnershipState::owned;
1291 }
1292 ++siteIndex;
1293 }
1294 }
1295 }
1296
1297 // -- main force calculation --
1298
1299 // - calculate mol mask -
1300 std::vector<char, autopas::AlignedAllocator<char>> molMask;
1301 molMask.reserve(neighborListSize);
1302
1303#pragma omp simd
1304 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1305 const auto neighborMolIndex = neighborList[neighborMol]; // index of neighbor mol in soa
1306
1307 const auto ownedState = ownedStatePtr[neighborMolIndex];
1308
1309 const auto displacementCoMX = xptr[indexPrime] - xptr[neighborMolIndex];
1310 const auto displacementCoMY = yptr[indexPrime] - yptr[neighborMolIndex];
1311 const auto displacementCoMZ = zptr[indexPrime] - zptr[neighborMolIndex];
1312
1313 const auto distanceSquaredCoMX = displacementCoMX * displacementCoMX;
1314 const auto distanceSquaredCoMY = displacementCoMY * displacementCoMY;
1315 const auto distanceSquaredCoMZ = displacementCoMZ * displacementCoMZ;
1316
1317 const auto distanceSquaredCoM = distanceSquaredCoMX + distanceSquaredCoMY + distanceSquaredCoMZ;
1318
1319 // mask molecules beyond cutoff or if molecule is a dummy
1320 molMask[neighborMol] = distanceSquaredCoM <= cutoffSquared and ownedState != autopas::OwnershipState::dummy;
1321 }
1322
1323 // generate mask for each site from molecular mask
1324 std::vector<char, autopas::AlignedAllocator<char>> siteMask;
1325 siteMask.reserve(siteCountNeighbors);
1326
1327 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1328 const auto neighborMolIndex = neighborList[neighborMol]; // index of neighbor mol in soa
1329 for (size_t siteB = 0; siteB < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++siteB) {
1330 siteMask.emplace_back(molMask[neighborMol]);
1331 }
1332 }
1333
1334 // sums used for prime mol
1335 SoAFloatPrecision forceSumX = 0.;
1336 SoAFloatPrecision forceSumY = 0.;
1337 SoAFloatPrecision forceSumZ = 0.;
1338 SoAFloatPrecision torqueSumX = 0.;
1339 SoAFloatPrecision torqueSumY = 0.;
1340 SoAFloatPrecision torqueSumZ = 0.;
1341
1342 // - actual LJ calculation -
1343
1344 for (size_t primeSite = 0; primeSite < siteCountMolPrime; ++primeSite) {
1345 const auto rotatedPrimeSitePositionX = rotatedSitePositionsPrime[primeSite][0];
1346 const auto rotatedPrimeSitePositionY = rotatedSitePositionsPrime[primeSite][1];
1347 const auto rotatedPrimeSitePositionZ = rotatedSitePositionsPrime[primeSite][2];
1348
1349 const auto exactPrimeSitePositionX = rotatedPrimeSitePositionX + xptr[indexPrime];
1350 const auto exactPrimeSitePositionY = rotatedPrimeSitePositionY + yptr[indexPrime];
1351 const auto exactPrimeSitePositionZ = rotatedPrimeSitePositionZ + zptr[indexPrime];
1352
1353 // generate parameter data for chosen site
1354 if constexpr (useMixing) {
1355 const auto primeSiteType = siteTypesPrime[primeSite];
1356
1357 size_t siteIndex = 0;
1358 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1359 const auto neighborMolIndex = neighborList[neighborMol];
1360 const auto siteTypesOfNeighborMol = _PPLibrary->getSiteTypes(typeptr[neighborMolIndex]);
1361
1362 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1363 const auto mixingData = _PPLibrary->getLJMixingData(primeSiteType, siteTypesOfNeighborMol[site]);
1364 sigmaSquareds[siteIndex] = mixingData.sigmaSquared;
1365 epsilon24s[siteIndex] = mixingData.epsilon24;
1366 if constexpr (applyShift) {
1367 shift6s[siteIndex] = mixingData.shift6;
1368 }
1369 ++siteIndex;
1370 }
1371 }
1372 }
1373
1374#pragma omp simd reduction(+ : forceSumX, forceSumY, forceSumZ, torqueSumX, torqueSumY, torqueSumZ)
1375 for (size_t neighborSite = 0; neighborSite < siteCountNeighbors; ++neighborSite) {
1376 const SoAFloatPrecision sigmaSquared = useMixing ? sigmaSquareds[neighborSite] : const_sigmaSquared;
1377 const SoAFloatPrecision epsilon24 = useMixing ? epsilon24s[neighborSite] : const_epsilon24;
1378 const SoAFloatPrecision shift6 = applyShift ? (useMixing ? shift6s[neighborSite] : const_shift6) : 0;
1379
1380 const bool isNeighborSiteOwned = !calculateGlobals || isNeighborSiteOwnedArr[neighborSite];
1381
1382 const auto displacementX = exactPrimeSitePositionX - exactNeighborSitePositionX[neighborSite];
1383 const auto displacementY = exactPrimeSitePositionY - exactNeighborSitePositionY[neighborSite];
1384 const auto displacementZ = exactPrimeSitePositionZ - exactNeighborSitePositionZ[neighborSite];
1385
1386 const auto distanceSquaredX = displacementX * displacementX;
1387 const auto distanceSquaredY = displacementY * displacementY;
1388 const auto distanceSquaredZ = displacementZ * displacementZ;
1389
1390 const auto distanceSquared = distanceSquaredX + distanceSquaredY + distanceSquaredZ;
1391
1392 const auto invDistSquared = 1. / distanceSquared;
1393 const auto lj2 = sigmaSquared * invDistSquared;
1394 const auto lj6 = lj2 * lj2 * lj2;
1395 const auto lj12 = lj6 * lj6;
1396 const auto lj12m6 = lj12 - lj6;
1397 const auto scalarMultiple = siteMask[neighborSite] ? epsilon24 * (lj12 + lj12m6) * invDistSquared : 0.;
1398
1399 // calculate forces
1400 const auto forceX = scalarMultiple * displacementX;
1401 const auto forceY = scalarMultiple * displacementY;
1402 const auto forceZ = scalarMultiple * displacementZ;
1403
1404 forceSumX += forceX;
1405 forceSumY += forceY;
1406 forceSumZ += forceZ;
1407
1408 torqueSumX += rotatedPrimeSitePositionY * forceZ - rotatedPrimeSitePositionZ * forceY;
1409 torqueSumY += rotatedPrimeSitePositionZ * forceX - rotatedPrimeSitePositionX * forceZ;
1410 torqueSumZ += rotatedPrimeSitePositionX * forceY - rotatedPrimeSitePositionY * forceX;
1411
1412 // N3L
1413 if (newton3) {
1414 siteForceX[neighborSite] -= forceX;
1415 siteForceY[neighborSite] -= forceY;
1416 siteForceZ[neighborSite] -= forceZ;
1417 }
1418
1419 // calculate globals
1420 if constexpr (calculateGlobals) {
1421 const auto potentialEnergy6 = siteMask[neighborSite] ? (epsilon24 * lj12m6 + shift6) : 0.;
1422 const auto virialX = displacementX * forceX;
1423 const auto virialY = displacementY * forceY;
1424 const auto virialZ = displacementZ * forceZ;
1425
1426 // We add 6 times the potential energy for each owned particle. The total sum is corrected in endTraversal().
1427 const auto ownershipFactor =
1428 newton3 ? (ownedStatePrime == autopas::OwnershipState::owned ? 1. : 0.) + (isNeighborSiteOwned ? 1. : 0.)
1429 : (ownedStatePrime == autopas::OwnershipState::owned ? 1. : 0.);
1430 potentialEnergySum += potentialEnergy6 * ownershipFactor;
1431 virialSumX += virialX * ownershipFactor;
1432 virialSumY += virialY * ownershipFactor;
1433 virialSumZ += virialZ * ownershipFactor;
1434 }
1435 }
1436 }
1437 // Add forces to prime mol
1438 fxptr[indexPrime] += forceSumX;
1439 fyptr[indexPrime] += forceSumY;
1440 fzptr[indexPrime] += forceSumZ;
1441 txptr[indexPrime] += torqueSumX;
1442 typtr[indexPrime] += torqueSumY;
1443 tzptr[indexPrime] += torqueSumZ;
1444
1445 // Reduce forces on individual neighbor sites to molecular forces & torques if newton3=true
1446 if constexpr (newton3) {
1447 if constexpr (useMixing) {
1448 size_t siteIndex = 0;
1449 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1450 const auto neighborMolIndex = neighborList[neighborMol];
1451 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1452 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1453 _PPLibrary->getSitePositions(typeptr[neighborMolIndex]));
1454 for (size_t site = 0; site < _PPLibrary->getNumSites(typeptr[neighborMolIndex]); ++site) {
1455 fxptr[neighborMolIndex] += siteForceX[siteIndex];
1456 fyptr[neighborMolIndex] += siteForceY[siteIndex];
1457 fzptr[neighborMolIndex] += siteForceZ[siteIndex];
1458 txptr[neighborMolIndex] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
1459 rotatedSitePositions[site][2] * siteForceY[siteIndex];
1460 typtr[neighborMolIndex] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
1461 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
1462 tzptr[neighborMolIndex] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
1463 rotatedSitePositions[site][1] * siteForceX[siteIndex];
1464 ++siteIndex;
1465 }
1466 }
1467 } else {
1468 size_t siteIndex = 0;
1469 for (size_t neighborMol = 0; neighborMol < neighborListSize; ++neighborMol) {
1470 const auto neighborMolIndex = neighborList[neighborMol];
1471 const auto rotatedSitePositions = autopas::utils::quaternion::rotateVectorOfPositions(
1472 {q0ptr[neighborMolIndex], q1ptr[neighborMolIndex], q2ptr[neighborMolIndex], q3ptr[neighborMolIndex]},
1473 const_unrotatedSitePositions);
1474 for (size_t site = 0; site < const_unrotatedSitePositions.size(); ++site) {
1475 fxptr[neighborMolIndex] += siteForceX[siteIndex];
1476 fyptr[neighborMolIndex] += siteForceY[siteIndex];
1477 fzptr[neighborMolIndex] += siteForceZ[siteIndex];
1478 txptr[neighborMolIndex] += rotatedSitePositions[site][1] * siteForceZ[siteIndex] -
1479 rotatedSitePositions[site][2] * siteForceY[siteIndex];
1480 typtr[neighborMolIndex] += rotatedSitePositions[site][2] * siteForceX[siteIndex] -
1481 rotatedSitePositions[site][0] * siteForceZ[siteIndex];
1482 txptr[neighborMolIndex] += rotatedSitePositions[site][0] * siteForceY[siteIndex] -
1483 rotatedSitePositions[site][1] * siteForceX[siteIndex];
1484 ++siteIndex;
1485 }
1486 }
1487 }
1488 }
1489
1490 if constexpr (calculateGlobals) {
1491 const auto threadNum = autopas::autopas_get_thread_num();
1492
1493 _aosThreadData[threadNum].potentialEnergySum += potentialEnergySum;
1494 _aosThreadData[threadNum].virialSum[0] += virialSumX;
1495 _aosThreadData[threadNum].virialSum[1] += virialSumY;
1496 _aosThreadData[threadNum].virialSum[2] += virialSumZ;
1497 }
1498 }
1499
1503 class AoSThreadData {
1504 public:
1505 AoSThreadData() : virialSum{0., 0., 0.}, potentialEnergySum{0.}, __remainingTo64{} {}
1506 void setZero() {
1507 virialSum = {0., 0., 0.};
1508 potentialEnergySum = 0.;
1509 }
1510
1511 // variables
1512 std::array<double, 3> virialSum;
1513 double potentialEnergySum;
1514
1515 private:
1516 // dummy parameter to get the right size (64 bytes)
1517 double __remainingTo64[(64 - 4 * sizeof(double)) / sizeof(double)];
1518 };
1519
1520 static_assert(sizeof(AoSThreadData) % 64 == 0, "AoSThreadData has wrong size (should be multiple of 64)");
1521
1525 std::vector<AoSThreadData> _aosThreadData;
1526};
1527} // 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
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
PairwiseFunctor class.
Definition: PairwiseFunctor.h:66
PairwiseFunctor(double cutoff)
Constructor.
Definition: PairwiseFunctor.h:77
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
A functor to handle Lennard-Jones interactions between two Multisite Molecules.
Definition: LJMultisiteFunctor.h:49
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: LJMultisiteFunctor.h:610
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:711
static constexpr bool getMixing()
Definition: LJMultisiteFunctor.h:682
static constexpr auto getComputedAttr()
Get attributes computed by this functor.
Definition: LJMultisiteFunctor.h:673
void setParticleProperties(SoAFloatPrecision epsilon24, SoAFloatPrecision sigmaSquared, std::vector< std::array< SoAFloatPrecision, 3 > > sitePositionsLJ)
Sets the molecule properties constants for this functor.
Definition: LJMultisiteFunctor.h:628
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:756
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:694
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:658
double getVirial()
Get the virial.
Definition: LJMultisiteFunctor.h:774
void endTraversal(bool newton3) final
Postprocesses global values, e.g.
Definition: LJMultisiteFunctor.h:724
static constexpr auto getNeededAttr()
Get attributes needed for computation.
Definition: LJMultisiteFunctor.h:643
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:33
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