AutoPas  3.0.0
Loading...
Searching...
No Matches
AxilrodTellerFunctor.h
Go to the documentation of this file.
1
7#pragma once
8
15#include "autopas/utils/SoA.h"
18#include "autopas/utils/inBox.h"
19
20namespace mdLib {
21
96template <class Particle_T, bool useMixing = false, autopas::FunctorN3Modes useNewton3 = autopas::FunctorN3Modes::Both,
97 bool calculateGlobals = false, bool countFLOPs = false>
100 Particle_T, AxilrodTellerFunctor<Particle_T, useMixing, useNewton3, calculateGlobals, countFLOPs>> {
104 using SoAArraysType = typename Particle_T::SoAArraysType;
105
109 using SoAFloatPrecision = typename Particle_T::ParticleSoAFloatPrecision;
110
111 public:
116
117 private:
123 explicit AxilrodTellerFunctor(double cutoff, void * /*dummy*/)
124 : autopas::TriwiseFunctor<Particle_T,
125 AxilrodTellerFunctor<Particle_T, useMixing, useNewton3, calculateGlobals, countFLOPs>>(
126 cutoff),
127 _cutoffSquared{cutoff * cutoff},
128 _potentialEnergySum{0.},
129 _virialSum{0., 0., 0.},
130 _aosThreadDataGlobals(),
131 _postProcessed{false} {
132 if constexpr (calculateGlobals) {
133 _aosThreadDataGlobals.resize(autopas::autopas_get_max_threads());
134 }
135 if constexpr (countFLOPs) {
136 _aosThreadDataFLOPs.resize(autopas::autopas_get_max_threads());
137 }
138 }
139
140 public:
149 explicit AxilrodTellerFunctor(double cutoff) : AxilrodTellerFunctor(cutoff, nullptr) {
150 static_assert(not useMixing,
151 "Mixing without a ParticlePropertiesLibrary is not possible! Use a different constructor or set "
152 "mixing to false.");
153 }
154
161 explicit AxilrodTellerFunctor(double cutoff, ParticlePropertiesLibrary<double, size_t> &particlePropertiesLibrary)
162 : AxilrodTellerFunctor(cutoff, nullptr) {
163 static_assert(useMixing,
164 "Not using Mixing but using a ParticlePropertiesLibrary is not allowed! Use a different constructor "
165 "or set mixing to true.");
166 _PPLibrary = &particlePropertiesLibrary;
167 }
168
169 std::string getName() final { return "AxilrodTellerFunctorAutoVec"; }
170
171 bool isRelevantForTuning() final { return true; }
172
173 bool allowsNewton3() final {
174 return useNewton3 == autopas::FunctorN3Modes::Newton3Only or useNewton3 == autopas::FunctorN3Modes::Both;
175 }
176
177 bool allowsNonNewton3() final {
178 return useNewton3 == autopas::FunctorN3Modes::Newton3Off or useNewton3 == autopas::FunctorN3Modes::Both;
179 }
180
181 void AoSFunctor(Particle_T &i, Particle_T &j, Particle_T &k, bool newton3) final {
182 using namespace autopas::utils::ArrayMath::literals;
183
184 if (i.isDummy() or j.isDummy() or k.isDummy()) {
185 return;
186 }
187
188 const auto threadnum = autopas::autopas_get_thread_num();
189
190 if constexpr (countFLOPs) {
191 ++_aosThreadDataFLOPs[threadnum].numDistCalls;
192 }
193
194 auto nu = _nu;
195 if constexpr (useMixing) {
196 nu = _PPLibrary->getMixingNu(i.getTypeId(), j.getTypeId(), k.getTypeId());
197 }
198
199 const auto displacementIJ = j.getR() - i.getR();
200 const auto displacementJK = k.getR() - j.getR();
201 const auto displacementKI = i.getR() - k.getR();
202
203 const double distSquaredIJ = autopas::utils::ArrayMath::dot(displacementIJ, displacementIJ);
204 const double distSquaredJK = autopas::utils::ArrayMath::dot(displacementJK, displacementJK);
205 const double distSquaredKI = autopas::utils::ArrayMath::dot(displacementKI, displacementKI);
206
207 // Check cutoff for every distance
208 if (distSquaredIJ > _cutoffSquared or distSquaredJK > _cutoffSquared or distSquaredKI > _cutoffSquared) {
209 return;
210 }
211
212 // Calculate prefactor
213 const double allDistsSquared = distSquaredIJ * distSquaredJK * distSquaredKI;
214 const double allDistsTo5 = allDistsSquared * allDistsSquared * std::sqrt(allDistsSquared);
215 const double factor = 3.0 * nu / allDistsTo5;
216
217 // Dot products of both distance vectors going from one particle
218 const double IJDotKI = autopas::utils::ArrayMath::dot(displacementIJ, displacementKI);
219 const double IJDotJK = autopas::utils::ArrayMath::dot(displacementIJ, displacementJK);
220 const double JKDotKI = autopas::utils::ArrayMath::dot(displacementJK, displacementKI);
221
222 const double allDotProducts = IJDotKI * IJDotJK * JKDotKI;
223
224 const auto forceIDirectionJK = displacementJK * IJDotKI * (IJDotJK - JKDotKI);
225 const auto forceIDirectionIJ =
226 displacementIJ * (IJDotJK * JKDotKI - distSquaredJK * distSquaredKI + 5.0 * allDotProducts / distSquaredIJ);
227 const auto forceIDirectionKI =
228 displacementKI * (-IJDotJK * JKDotKI + distSquaredIJ * distSquaredJK - 5.0 * allDotProducts / distSquaredKI);
229
230 const auto forceI = (forceIDirectionJK + forceIDirectionIJ + forceIDirectionKI) * factor;
231 i.addF(forceI);
232
233 auto forceJ = forceI;
234 auto forceK = forceI;
235 if (newton3) {
236 const auto forceJDirectionKI = displacementKI * IJDotJK * (JKDotKI - IJDotKI);
237 const auto forceJDirectionIJ =
238 displacementIJ * (-IJDotKI * JKDotKI + distSquaredJK * distSquaredKI - 5.0 * allDotProducts / distSquaredIJ);
239 const auto forceJDirectionJK =
240 displacementJK * (IJDotKI * JKDotKI - distSquaredIJ * distSquaredKI + 5.0 * allDotProducts / distSquaredJK);
241
242 forceJ = (forceJDirectionKI + forceJDirectionIJ + forceJDirectionJK) * factor;
243 j.addF(forceJ);
244
245 forceK = (forceI + forceJ) * (-1.0);
246 k.addF(forceK);
247 }
248
249 if constexpr (countFLOPs) {
250 if (newton3) {
251 ++_aosThreadDataFLOPs[threadnum].numKernelCallsN3;
252 } else {
253 ++_aosThreadDataFLOPs[threadnum].numKernelCallsNoN3;
254 }
255 }
256
257 if constexpr (calculateGlobals) {
258 // Add 3 * potential energy to every owned particle of the interaction.
259 // Division to the correct value is handled in endTraversal().
260 const double potentialEnergy3 = factor * (allDistsSquared - 3.0 * allDotProducts);
261
262 // Virial is calculated as f_i * r_i
263 // see Thompson et al.: https://doi.org/10.1063/1.3245303
264 const auto virialI = forceI * i.getR();
265 if (i.isOwned()) {
266 _aosThreadDataGlobals[threadnum].potentialEnergySum += potentialEnergy3;
267 _aosThreadDataGlobals[threadnum].virialSum += virialI;
268 }
269 // for non-newton3 particles j and/or k will be considered in a separate calculation
270 if (newton3 and j.isOwned()) {
271 const auto virialJ = forceJ * j.getR();
272 _aosThreadDataGlobals[threadnum].potentialEnergySum += potentialEnergy3;
273 _aosThreadDataGlobals[threadnum].virialSum += virialJ;
274 }
275 if (newton3 and k.isOwned()) {
276 const auto virialK = forceK * k.getR();
277 _aosThreadDataGlobals[threadnum].potentialEnergySum += potentialEnergy3;
278 _aosThreadDataGlobals[threadnum].virialSum += virialK;
279 }
280 if constexpr (countFLOPs) {
281 if (newton3) {
282 ++_aosThreadDataFLOPs[threadnum].numGlobalCalcsN3;
283 } else {
284 ++_aosThreadDataFLOPs[threadnum].numGlobalCalcsNoN3;
285 }
286 }
287 }
288 }
289
297 void setParticleProperties(SoAFloatPrecision nu) { _nu = nu; }
298
302 constexpr static auto getNeededAttr() {
303 return std::array<typename Particle_T::AttributeNames, 9>{Particle_T::AttributeNames::id,
304 Particle_T::AttributeNames::posX,
305 Particle_T::AttributeNames::posY,
306 Particle_T::AttributeNames::posZ,
307 Particle_T::AttributeNames::forceX,
308 Particle_T::AttributeNames::forceY,
309 Particle_T::AttributeNames::forceZ,
310 Particle_T::AttributeNames::typeId,
311 Particle_T::AttributeNames::ownershipState};
312 }
313
317 constexpr static auto getNeededAttr(std::false_type) {
318 return std::array<typename Particle_T::AttributeNames, 6>{
319 Particle_T::AttributeNames::id, Particle_T::AttributeNames::posX,
320 Particle_T::AttributeNames::posY, Particle_T::AttributeNames::posZ,
321 Particle_T::AttributeNames::typeId, Particle_T::AttributeNames::ownershipState};
322 }
323
327 constexpr static auto getComputedAttr() {
328 return std::array<typename Particle_T::AttributeNames, 3>{
329 Particle_T::AttributeNames::forceX, Particle_T::AttributeNames::forceY, Particle_T::AttributeNames::forceZ};
330 }
331
336 constexpr static bool getMixing() { return useMixing; }
337
342 void initTraversal() final {
343 _potentialEnergySum = 0.;
344 _virialSum = {0., 0., 0.};
345 _postProcessed = false;
346 for (size_t i = 0; i < _aosThreadDataGlobals.size(); ++i) {
347 _aosThreadDataGlobals[i].setZero();
348 }
349 }
350
355 void endTraversal(bool newton3) final {
356 using namespace autopas::utils::ArrayMath::literals;
357
358 if (_postProcessed) {
360 "Already postprocessed, endTraversal(bool newton3) was called twice without calling initTraversal().");
361 }
362 if (calculateGlobals) {
363 // Accumulate potential energy and virial values.
364 for (const auto &data : _aosThreadDataGlobals) {
365 _potentialEnergySum += data.potentialEnergySum;
366 _virialSum += data.virialSum;
367 }
368
369 // For each interaction, we added the full contribution for all three particles. Divide by 3 here, so that each
370 // contribution is only counted once per triplet.
371 _potentialEnergySum /= 3.;
372
373 // Additionally, we have always calculated 3*potentialEnergy, so we divide by 3 again.
374 _potentialEnergySum /= 3.;
375
376 _postProcessed = true;
377
378 AutoPasLog(TRACE, "Final potential energy {}", _potentialEnergySum);
379 AutoPasLog(TRACE, "Final virial {}", _virialSum[0] + _virialSum[1] + _virialSum[2]);
380 }
381 }
382
388 if (not calculateGlobals) {
390 "Trying to get potential energy even though calculateGlobals is false. If you want this functor to calculate "
391 "global "
392 "values, please specify calculateGlobals to be true.");
393 }
394 if (not _postProcessed) {
396 "Cannot get potential energy, because endTraversal was not called.");
397 }
398 return _potentialEnergySum;
399 }
400
405 double getVirial() {
406 if (not calculateGlobals) {
408 "Trying to get virial even though calculateGlobals is false. If you want this functor to calculate global "
409 "values, please specify calculateGlobals to be true.");
410 }
411 if (not _postProcessed) {
413 "Cannot get virial, because endTraversal was not called.");
414 }
415 return _virialSum[0] + _virialSum[1] + _virialSum[2];
416 }
417
453 [[nodiscard]] size_t getNumFLOPs() const override {
454 if constexpr (countFLOPs) {
455 const size_t numDistCallsAcc =
456 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
457 [](size_t sum, const auto &data) { return sum + data.numDistCalls; });
458 const size_t numKernelCallsN3Acc =
459 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
460 [](size_t sum, const auto &data) { return sum + data.numKernelCallsN3; });
461 const size_t numKernelCallsNoN3Acc =
462 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
463 [](size_t sum, const auto &data) { return sum + data.numKernelCallsNoN3; });
464 const size_t numGlobalCalcsN3Acc =
465 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
466 [](size_t sum, const auto &data) { return sum + data.numGlobalCalcsN3; });
467 const size_t numGlobalCalcsNoN3Acc =
468 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
469 [](size_t sum, const auto &data) { return sum + data.numGlobalCalcsNoN3; });
470
471 constexpr size_t numFLOPsPerDistanceCall = 24;
472 constexpr size_t numFLOPsPerN3KernelCall = 100;
473 constexpr size_t numFLOPsPerNoN3KernelCall = 59;
474 constexpr size_t numFLOPsPerN3GlobalCalc = 24;
475 constexpr size_t numFLOPsPerNoN3GlobalCalc = 10;
476
477 return numDistCallsAcc * numFLOPsPerDistanceCall + numKernelCallsN3Acc * numFLOPsPerN3KernelCall +
478 numKernelCallsNoN3Acc * numFLOPsPerNoN3KernelCall + numGlobalCalcsN3Acc * numFLOPsPerN3GlobalCalc +
479 numGlobalCalcsNoN3Acc * numFLOPsPerNoN3GlobalCalc;
480 } else {
481 // This is needed because this function still gets called with FLOP logging disabled, just nothing is done with it
482 return std::numeric_limits<size_t>::max();
483 }
484 }
485
486 [[nodiscard]] double getHitRate() const override {
487 if constexpr (countFLOPs) {
488 const size_t numDistCallsAcc =
489 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
490 [](size_t sum, const auto &data) { return sum + data.numDistCalls; });
491 const size_t numKernelCallsN3Acc =
492 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
493 [](size_t sum, const auto &data) { return sum + data.numKernelCallsN3; });
494 const size_t numKernelCallsNoN3Acc =
495 std::accumulate(_aosThreadDataFLOPs.begin(), _aosThreadDataFLOPs.end(), 0ul,
496 [](size_t sum, const auto &data) { return sum + data.numKernelCallsNoN3; });
497
498 return (static_cast<double>(numKernelCallsNoN3Acc) + static_cast<double>(numKernelCallsN3Acc)) /
499 (static_cast<double>(numDistCallsAcc));
500 } else {
501 // This is needed because this function still gets called with FLOP logging disabled, just nothing is done with it
502 return std::numeric_limits<double>::quiet_NaN();
503 }
504 }
505
506 private:
507 template <bool newton3>
508 void SoAFunctorVerletImpl(autopas::SoAView<SoAArraysType> soa, const size_t indexFirst,
509 const std::vector<size_t, autopas::AlignedAllocator<size_t>> &neighborList) {
510 autopas::utils::ExceptionHandler::exception("AxilrodTellerFunctor::SoAFunctorVerletImpl() is not implemented.");
511 }
512
516 class AoSThreadDataGlobals {
517 public:
518 AoSThreadDataGlobals() : virialSum{0., 0., 0.}, potentialEnergySum{0.}, __remainingTo64{} {}
519 void setZero() {
520 virialSum = {0., 0., 0.};
521 potentialEnergySum = 0.;
522 }
523
524 // variables
525 std::array<double, 3> virialSum;
526 double potentialEnergySum;
527
528 private:
529 // dummy parameter to get the right size (64 bytes)
530 double __remainingTo64[(64 - 4 * sizeof(double)) / sizeof(double)];
531 };
532
540 class AoSThreadDataFLOPs {
541 public:
542 AoSThreadDataFLOPs() : __remainingTo64{} {}
543
547 void setZero() {
548 numKernelCallsNoN3 = 0;
549 numKernelCallsN3 = 0;
550 numDistCalls = 0;
551 numGlobalCalcsN3 = 0;
552 numGlobalCalcsNoN3 = 0;
553 }
554
559 size_t numKernelCallsNoN3 = 0;
560
565 size_t numKernelCallsN3 = 0;
566
571 size_t numDistCalls = 0;
572
576 size_t numGlobalCalcsN3 = 0;
577
581 size_t numGlobalCalcsNoN3 = 0;
582
583 private:
587 double __remainingTo64[(64 - 5 * sizeof(size_t)) / sizeof(size_t)];
588 };
589
590 // make sure of the size of AoSThreadDataGlobals
591 static_assert(sizeof(AoSThreadDataGlobals) % 64 == 0, "AoSThreadDataGlobals has wrong size");
592 static_assert(sizeof(AoSThreadDataFLOPs) % 64 == 0, "AoSThreadDataFLOPs has wrong size");
593
594 const double _cutoffSquared;
595
596 // Parameter of the Axilrod-Teller potential
597 // not const because they might be reset through PPL
598 double _nu = 0.0;
599
601
602 // sum of the potential energy, only calculated if calculateGlobals is true
603 double _potentialEnergySum;
604
605 // sum of the virial, only calculated if calculateGlobals is true
606 std::array<double, 3> _virialSum;
607
608 // thread buffer for aos
609 std::vector<AoSThreadDataGlobals> _aosThreadDataGlobals;
610 std::vector<AoSThreadDataFLOPs> _aosThreadDataFLOPs{};
611
612 // defines whether or whether not the global values are already preprocessed
613 bool _postProcessed;
614};
615} // 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 getMixingNu(intType i, intType j, intType k) const
Returns the precomputed mixed epsilon * 24.
Definition: ParticlePropertiesLibrary.h:283
AlignedAllocator class.
Definition: AlignedAllocator.h:29
View on a fixed part of a SoA between a start index and an end index.
Definition: SoAView.h:23
TriwiseFunctor class.
Definition: TriwiseFunctor.h:28
TriwiseFunctor(double cutoff)
Constructor.
Definition: TriwiseFunctor.h:39
Default exception class for autopas exceptions.
Definition: ExceptionHandler.h:115
static void exception(const Exception e)
Handle an exception derived by std::exception.
Definition: ExceptionHandler.h:63
A functor to handle Axilrod-Teller(-Muto) interactions between three particles (molecules).
Definition: AxilrodTellerFunctor.h:100
double getVirial()
Get the virial.
Definition: AxilrodTellerFunctor.h:405
AxilrodTellerFunctor()=delete
Deleted default constructor.
double getHitRate() const override
Get the hit rate.
Definition: AxilrodTellerFunctor.h:486
AxilrodTellerFunctor(double cutoff)
Constructor for Functor with mixing disabled.
Definition: AxilrodTellerFunctor.h:149
AxilrodTellerFunctor(double cutoff, ParticlePropertiesLibrary< double, size_t > &particlePropertiesLibrary)
Constructor for Functor with mixing active.
Definition: AxilrodTellerFunctor.h:161
void setParticleProperties(SoAFloatPrecision nu)
Sets the particle properties constants for this functor.
Definition: AxilrodTellerFunctor.h:297
static constexpr bool getMixing()
Definition: AxilrodTellerFunctor.h:336
static constexpr auto getNeededAttr()
Get attributes needed for computation.
Definition: AxilrodTellerFunctor.h:302
std::string getName() final
Returns name of functor.
Definition: AxilrodTellerFunctor.h:169
static constexpr auto getNeededAttr(std::false_type)
Get attributes needed for computation without N3 optimization.
Definition: AxilrodTellerFunctor.h:317
void initTraversal() final
Reset the global values.
Definition: AxilrodTellerFunctor.h:342
bool isRelevantForTuning() final
Specifies whether the functor should be considered for the auto-tuning process.
Definition: AxilrodTellerFunctor.h:171
bool allowsNonNewton3() final
Specifies whether the functor is capable of non-Newton3-like functors.
Definition: AxilrodTellerFunctor.h:177
bool allowsNewton3() final
Specifies whether the functor is capable of Newton3-like functors.
Definition: AxilrodTellerFunctor.h:173
void AoSFunctor(Particle_T &i, Particle_T &j, Particle_T &k, bool newton3) final
TriwiseFunctor for arrays of structures (AoS).
Definition: AxilrodTellerFunctor.h:181
static constexpr auto getComputedAttr()
Get attributes computed by this functor.
Definition: AxilrodTellerFunctor.h:327
double getPotentialEnergy()
Get the potential Energy.
Definition: AxilrodTellerFunctor.h:387
size_t getNumFLOPs() const override
Gets the number of useful FLOPs.
Definition: AxilrodTellerFunctor.h:453
void endTraversal(bool newton3) final
Accumulates global values, e.g.
Definition: AxilrodTellerFunctor.h:355
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
This is the main namespace of AutoPas.
Definition: AutoPasDecl.h:32
int autopas_get_max_threads()
Dummy for omp_get_max_threads() when no OpenMP is available.
Definition: WrapOpenMP.h:144
FunctorN3Modes
Newton 3 modes for the Functor.
Definition: Functor.h:22
int autopas_get_thread_num()
Dummy for omp_set_lock() when no OpenMP is available.
Definition: WrapOpenMP.h:132