AutoPas  3.0.0
Loading...
Searching...
No Matches
LiveInfo.h
Go to the documentation of this file.
1
7#pragma once
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12#include <limits>
13#include <variant>
14
24
25namespace autopas {
26
31class LiveInfo {
32 // The class currently needs to be defined in a header only since iteration over a particle container requires to
33 // know the Particle type. Actually, no particle specific information can be used here, so only a pointer to
34 // ParticleBase would suffice, but iteration doesn't work that way at the moment.
35
36 public:
40 using InfoType = std::variant<bool, double, size_t, ContainerOption, TraversalOption, LoadEstimatorOption,
41 DataLayoutOption, Newton3Option>;
42
80 template <class Particle_T, class PairwiseFunctor>
82 unsigned int rebuildFrequency) {
83 using namespace autopas::utils::ArrayMath::literals;
86
87 // Some aliases for quicker access
88 const auto &boxMin = container.getBoxMin();
89 const auto &boxMax = container.getBoxMax();
90 const auto cutoff = container.getCutoff();
91 const auto cutoffInv = 1.0 / cutoff;
92 const auto numParticles = container.getNumberOfParticles();
93
94 infos["numParticles"] = numParticles;
95 infos["cutoff"] = cutoff;
96 infos["skin"] = container.getVerletSkin();
97 infos["rebuildFrequency"] = static_cast<size_t>(rebuildFrequency);
98 const auto domainSize = boxMax - boxMin;
99
100 infos["domainSizeX"] = domainSize[0];
101 infos["domainSizeY"] = domainSize[1];
102 infos["domainSizeZ"] = domainSize[2];
103
104 infos["particleSize"] = sizeof(Particle_T);
105
106 // Calculate number of cells for a linked cells container, assuming cell size factor == 1
107 const auto cellsPerDim = ceilToInt(domainSize * cutoffInv);
108 const auto numCells = static_cast<size_t>(cellsPerDim[0] * cellsPerDim[1] * cellsPerDim[2]);
109 infos["numCells"] = numCells;
110
111 // Count how many particles are in each cell via bin counting
112 std::vector<size_t> particleBins;
113 // +1 because we count all halo particles in the last bin
114 particleBins.resize(numCells + 1);
115 // Blurred cells divide the domain into 3x3x3 equivalent boxes.
116 std::vector<size_t> particleBinsBlurred;
117 particleBinsBlurred.resize(27);
118 const auto blurredCellDimsReciproc = std::array<double, 3>{3.0, 3.0, 3.0} / domainSize;
119 for (const Particle_T &particle : container) {
120 if (utils::inBox(particle.getR(), boxMin, boxMax)) {
121 // find the actual cell
122 const auto offsetIntoBox = particle.getR() - boxMin;
123 const auto cell = floorToInt(offsetIntoBox * cutoffInv);
124 const auto binIndex = (cell[2] * cellsPerDim[1] + cell[1]) * cellsPerDim[0] + cell[0];
125 particleBins[binIndex]++;
126
127 // find the blurred cell
128 const auto cellBlurred = floorToInt(offsetIntoBox * blurredCellDimsReciproc);
129 const auto binIndexBlurred = (cellBlurred[2] * 3 + cellBlurred[1]) * 3 + cellBlurred[0];
130 particleBinsBlurred[binIndexBlurred]++;
131 } else {
132 // found a halo particle
133 particleBins.back()++;
134 }
135 }
136
137 infos["numHaloParticles"] = particleBins.back();
138
139 // calculate statistics about particle distributions per cell
140 const auto avgParticlesPerCell = numParticles == 0
141 ? 0.
142 : static_cast<double>(container.getNumberOfParticles() - particleBins.back()) /
143 static_cast<double>(numCells);
144 const auto avgParticlesPerBlurredCell =
145 numParticles == 0 ? 0.
146 : static_cast<double>(container.getNumberOfParticles() - particleBins.back()) /
147 static_cast<double>(particleBinsBlurred.size());
148
149 const auto [estimatedNumNeighborInteractions, maxDiff, sumStddev, numEmptyCells, maxParticlesPerCell,
150 minParticlesPerCell] = [&]() {
151 double estimatedNumNeighborInteractionsLambda = 0.;
152 double maxDiffLambda = 0.;
153 double sumStddevLambda = 0.;
154 size_t numEmptyCellsLambda = 0;
155 size_t maxParticlesPerCellLambda = std::numeric_limits<size_t>::min();
156 size_t minParticlesPerCellLambda = std::numeric_limits<size_t>::max();
157 // go over all bins and calculate statistics
158 for (size_t i = 0; i < particleBins.size() - 1; i++) {
159 const auto particlesInBin = particleBins[i];
160 if (particlesInBin == 0) {
161 ++numEmptyCellsLambda;
162 } else {
163 const auto estimatedNumParticlesInVicinity = particlesInBin * (particlesInBin * 27 - 1);
164 // ratio of sphere volume to volume of 3x3x3 cubes where edge size of cube is same as the radius of sphere
165 constexpr double probabilityParticleWithinCutoff = 0.155;
166 estimatedNumNeighborInteractionsLambda += estimatedNumParticlesInVicinity * probabilityParticleWithinCutoff;
167 }
168 maxParticlesPerCellLambda = std::max(particlesInBin, maxParticlesPerCellLambda);
169 minParticlesPerCellLambda = std::min(particlesInBin, minParticlesPerCellLambda);
170 const auto diffFromAvg = avgParticlesPerCell - static_cast<int>(particlesInBin);
171 maxDiffLambda = std::max(diffFromAvg, maxDiffLambda);
172 sumStddevLambda += diffFromAvg * diffFromAvg;
173 }
174 estimatedNumNeighborInteractionsLambda /= 2;
175
176 return std::tuple{estimatedNumNeighborInteractionsLambda,
177 maxDiffLambda,
178 sumStddevLambda,
179 numEmptyCellsLambda,
180 maxParticlesPerCellLambda,
181 minParticlesPerCellLambda};
182 }();
183
184 infos["numEmptyCells"] = numEmptyCells;
185 infos["maxParticlesPerCell"] = maxParticlesPerCell;
186 infos["minParticlesPerCell"] = minParticlesPerCell;
187 infos["particlesPerCellStdDev"] =
188 numParticles == 0 ? 0.
189 : std::sqrt(sumStddev) / static_cast<double>(particleBins.size() - 1) / avgParticlesPerCell;
190 infos["avgParticlesPerCell"] = avgParticlesPerCell;
191
192 const auto [homogeneity, maxDensity] = autopas::utils::calculateHomogeneityAndMaxDensity(container);
193
194 infos["homogeneity"] = homogeneity;
195 infos["maxDensity"] = maxDensity;
196
197 infos["estimatedNumNeighborInteractions"] = static_cast<unsigned long>(estimatedNumNeighborInteractions);
198
199 const double sumStddevBlurred = [&]() {
200 double res = 0.;
201 for (auto numParticlesInBin : particleBinsBlurred) {
202 auto diff = avgParticlesPerBlurredCell - static_cast<int>(numParticlesInBin);
203 res += diff * diff;
204 }
205 return res;
206 }();
207 infos["particlesPerBlurredCellStdDev"] = numParticles == 0 ? 0.
208 : std::sqrt(sumStddevBlurred) /
209 static_cast<double>(particleBinsBlurred.size()) /
210 avgParticlesPerBlurredCell;
211
212 infos["threadCount"] = static_cast<size_t>(autopas::autopas_get_max_threads());
213
214 constexpr size_t particleSizeNeededByFunctor = calculateParticleSizeNeededByFunctor<Particle_T, PairwiseFunctor>(
215 std::make_index_sequence<PairwiseFunctor::getNeededAttr().size()>());
216 infos["particleSizeNeededByFunctor"] = particleSizeNeededByFunctor;
217 }
218
223 [[nodiscard]] const auto &get() const { return infos; }
224
229 [[nodiscard]] std::string toString() const {
230 auto typeToString = [](auto type) {
231 if constexpr (std::is_same_v<decltype(type), bool> or std::is_same_v<decltype(type), double> or
232 std::is_same_v<decltype(type), size_t>) {
233 return std::to_string(type);
234 } else if constexpr (std::is_base_of_v<Option<decltype(type)>, decltype(type)>) {
235 return type.to_string();
236 }
237 return std::string{"fail"};
238 };
239 auto pairToString = [&](const auto &pair) { return pair.first + "=" + std::visit(typeToString, pair.second); };
240 std::string res{"Live Info: "};
241 if (not infos.empty()) {
242 // We initialize the accumulation with the first element. Hence, the accumulation starts at next(begin).
243 res += std::accumulate(std::next(infos.begin()), infos.end(), pairToString(*infos.begin()),
244 [&](std::string s, const auto &elem) { return std::move(s) + " " + pairToString(elem); });
245 }
246 return res;
247 }
248
255 friend std::ostream &operator<<(std::ostream &out, const LiveInfo &info) {
256 out << info.infos.size() << ' ';
257 for (const auto &[name, val] : info.infos) {
258 // val.index here is the index of this value's type in the LiveInfo::InfoType variant type.
259 // This is needed for determining the type when reading this file via readIndex.
260 out << name << ' ' << val.index() << ' ';
261 std::visit([&](const auto &v) { out << v << ' '; }, val);
262 }
263 return out;
264 }
265
272 friend std::istream &operator>>(std::istream &in, LiveInfo &info) {
273 size_t numElements{0};
274 in >> numElements;
275 for (size_t i = 0; i < numElements; i++) {
276 std::string name;
277 in >> name;
278 size_t idx{0};
279 in >> idx;
280 const auto val =
281 readIndex<LiveInfo::InfoType>(in, idx, std::make_index_sequence<std::variant_size_v<LiveInfo::InfoType>>());
282 info.infos[name] = val;
283 }
284 return in;
285 }
286
292 [[nodiscard]] std::pair<std::string, std::string> getCSVLine() const {
293 // match all words (anything that is neither a ' ' or '='), that are followed by a '=',
294 // ignoring the 'Live Info: ' prefix
295 const auto keyRegex = std::regex("([^= ]+)=[^ ]*");
296 // match all words that are preceded by a '='
297 const auto valueRegex = std::regex("=([^ ]+)");
298
299 auto searchString = toString();
300 // remove leading Live Info:
301 searchString = searchString.substr(std::string("Live Info: ").size());
302
303 std::sregex_iterator keyIter(searchString.begin(), searchString.end(), keyRegex);
304 std::sregex_iterator valueIter(searchString.begin(), searchString.end(), valueRegex);
305 std::sregex_iterator end;
306
307 std::stringstream header;
308 std::stringstream line;
309
310 while (keyIter != end) {
311 // first submatch is the match of the capture group
312 header << keyIter->str(1) << ",";
313 ++keyIter;
314 }
315 while (valueIter != end) {
316 // first submatch is the match of the capture group
317 line << valueIter->str(1) << ",";
318 ++valueIter;
319 }
320
321 auto headerStr = header.str();
322 auto lineStr = line.str();
323 // drop trailing ','
324 headerStr.pop_back();
325 lineStr.pop_back();
326
327 return std::make_pair(headerStr, lineStr);
328 }
329
330 private:
339 template <class Particle_T, class PairwiseFunctor, size_t... Idx>
340 constexpr static auto calculateParticleSizeNeededByFunctor(std::index_sequence<Idx...>) {
341 return (0 + ... +
342 sizeof(typename std::tuple_element<PairwiseFunctor::getNeededAttr()[Idx],
343 typename Particle_T::SoAArraysType>::type::value_type));
344 }
345
356 template <class Variant, class Type, size_t Idx>
357 static void readIndexHelper(std::istream &in, size_t idx, Variant &var) {
358 if (Idx == idx) {
359 Type val;
360 in >> val;
361 var = val;
362 }
363 }
364
373 template <class Variant, size_t... Idx>
374 static Variant readIndex(std::istream &in, size_t idx, std::index_sequence<Idx...>) {
375 Variant var;
376 (readIndexHelper<Variant, std::variant_alternative_t<Idx, Variant>, Idx>(in, idx, var), ...);
377 return var;
378 }
379
383 std::map<std::string, InfoType> infos;
384};
385
386} // namespace autopas
static constexpr std::array< typename Particle_T::AttributeNames, 0 > getNeededAttr()
Get attributes needed for computation.
Definition: Functor.h:77
This class is able to gather and store important information for a tuning phase from a container and ...
Definition: LiveInfo.h:31
std::string toString() const
Creates a string containing all live info gathered.
Definition: LiveInfo.h:229
std::pair< std::string, std::string > getCSVLine() const
Generate a csv representation containing all values from the toString() method.
Definition: LiveInfo.h:292
friend std::istream & operator>>(std::istream &in, LiveInfo &info)
Stream operator to read the LiveInfo in from a stream.
Definition: LiveInfo.h:272
void gather(const autopas::ParticleContainerInterface< Particle_T > &container, const PairwiseFunctor &functor, unsigned int rebuildFrequency)
Gathers important information from a particle container and functor.
Definition: LiveInfo.h:81
friend std::ostream & operator<<(std::ostream &out, const LiveInfo &info)
Stream operator to write the LiveInfo to a stream.
Definition: LiveInfo.h:255
std::variant< bool, double, size_t, ContainerOption, TraversalOption, LoadEstimatorOption, DataLayoutOption, Newton3Option > InfoType
The type of an info.
Definition: LiveInfo.h:41
const auto & get() const
Returns a map of all infos.
Definition: LiveInfo.h:223
Class representing the load estimator choices.
Definition: LoadEstimatorOption.h:18
PairwiseFunctor class.
Definition: PairwiseFunctor.h:31
The ParticleContainerInterface class provides a basic interface for all Containers within AutoPas.
Definition: ParticleContainerInterface.h:37
virtual const std::array< double, 3 > & getBoxMin() const =0
Get the lower corner of the container without halo.
virtual double getCutoff() const =0
Return the cutoff of the container.
virtual const std::array< double, 3 > & getBoxMax() const =0
Get the upper corner of the container without halo.
virtual double getVerletSkin() const =0
Return the verletSkin of the container verletSkin.
virtual size_t getNumberOfParticles(IteratorBehavior behavior=IteratorBehavior::owned) const =0
Get the number of particles with respect to the specified IteratorBehavior.
constexpr std::array< int, SIZE > floorToInt(const std::array< T, SIZE > &a)
Floors all array elements and converts them to integers.
Definition: ArrayMath.h:332
constexpr std::array< int, SIZE > ceilToInt(const std::array< T, SIZE > &a)
Ceils all array elements and converts them to integers.
Definition: ArrayMath.h:348
std::pair< double, double > calculateHomogeneityAndMaxDensity(const ParticleContainerInterface< Particle > &container)
Calculates homogeneity and max density of given AutoPas container.
Definition: SimilarityFunctions.h:47
bool inBox(const std::array< T, 3 > &position, const std::array< T, 3 > &low, const std::array< T, 3 > &high)
Checks if position is inside of a box defined by low and high.
Definition: inBox.h:26
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
Type
Enum describing all types that are allowed in a rule program.
Definition: RuleBasedProgramTree.h:10