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
25
26namespace autopas {
27
32class LiveInfo {
33 // The class currently needs to be defined in a header only since iteration over a particle container requires to
34 // know the Particle type. Actually, no particle specific information can be used here, so only a pointer to
35 // ParticleBase would suffice, but iteration doesn't work that way at the moment.
36
37 private:
48 static utils::ParticleBinStructure buildCellBinStructure(const std::array<double, 3> &domainSize,
49 const double interactionLength,
50 const std::array<double, 3> &boxMin,
51 const std::array<double, 3> &boxMax, double cutoff) {
52 std::array<size_t, 3> cellsPerDim{};
53 std::array<double, 3> cellLength{};
54
55 for (int d = 0; d < 3; ++d) {
56 // The number of cells is rounded down because the cells will be stretched to fit.
57 // std::max to ensure there is at least one cell.
58 cellsPerDim[d] = std::max(static_cast<size_t>(std::floor(domainSize[d] / interactionLength)), 1ul);
59 cellLength[d] = domainSize[d] / static_cast<double>(cellsPerDim[d]);
60 }
61
62 return {cellsPerDim, cellLength, boxMin, boxMax, cutoff};
63 }
64
78 static utils::ParticleBinStructure buildParticleDependentBinStructure(const std::array<double, 3> &domainSize,
79 const size_t numParticles,
80 const std::array<double, 3> &boxMin,
81 const std::array<double, 3> &boxMax,
82 double cutoff) {
83 using namespace autopas::utils::ArrayMath::literals;
84
85 const auto targetNumberOfBins = std::max(std::ceil(static_cast<double>(numParticles) / 10.), 1.);
86 const auto targetNumberOfBinsPerDim = std::cbrt(targetNumberOfBins);
87 // This is probably not an integer, so we floor to get more than 10 particles per bin
88 const auto numberOfBinsPerDim = static_cast<size_t>(std::floor(targetNumberOfBinsPerDim));
89 const auto binDimensions = domainSize / static_cast<double>(numberOfBinsPerDim);
90
91 return {numberOfBinsPerDim, binDimensions, boxMin, boxMax, cutoff};
92 }
93
103 static utils::ParticleBinStructure buildBlurredBinStructure(const std::array<double, 3> &domainSize,
104 const std::array<double, 3> &boxMin,
105 const std::array<double, 3> &boxMax, double cutoff) {
106 using namespace autopas::utils::ArrayMath::literals;
107
108 const auto binLength = domainSize / 3.;
109
110 return {3, binLength, boxMin, boxMax, cutoff};
111 }
112
113 public:
117 using InfoType = std::variant<bool, double, size_t, ContainerOption, TraversalOption, LoadEstimatorOption,
118 DataLayoutOption, Newton3Option>;
119
187 template <class Particle_T>
188 void gather(ContainerIterator<Particle_T, true, false> particleIter, size_t rebuildFrequency,
189 size_t numOwnedParticles, std::array<double, 3> boxMin, std::array<double, 3> boxMax, double cutoff,
190 double skin) {
191 using namespace utils::ArrayMath::literals;
193
194 // Aliases and info of particle distribution independent information
195 const auto interactionLength = cutoff + skin;
196
197 infos["cutoff"] = cutoff;
198 infos["skin"] = skin;
199 infos["rebuildFrequency"] = rebuildFrequency;
200 infos["particleSize"] = sizeof(Particle_T);
201 infos["threadCount"] = static_cast<size_t>(autopas_get_max_threads());
202
203 infos["numOwnedParticles"] = numOwnedParticles;
204
205 const auto domainSize = boxMax - boxMin;
206 infos["domainSizeX"] = domainSize[0];
207 infos["domainSizeY"] = domainSize[1];
208 infos["domainSizeZ"] = domainSize[2];
209
210 // Build bin structures
211 auto cellBinStruct = buildCellBinStructure(domainSize, interactionLength, boxMin, boxMax, cutoff);
212 auto particleDependentBinStruct =
213 buildParticleDependentBinStructure(domainSize, numOwnedParticles, boxMin, boxMax, cutoff);
214 auto blurredBinStruct = buildBlurredBinStructure(domainSize, boxMin, boxMax, cutoff);
215
216 infos["numCells"] = cellBinStruct.getNumberOfBins();
217
218 // Count the number of owned particles per bin for each bin structure. Also include total count for halo particles.
219 size_t numOwnedParticlesCount = 0;
220 size_t numHaloParticlesCount = 0;
221 for (; particleIter.isValid(); ++particleIter) {
222 if (particleIter->isOwned()) {
223 numOwnedParticlesCount++;
224 const auto particlePos = particleIter->getR();
225 if (utils::inBox(particlePos, boxMin, boxMax)) {
226 cellBinStruct.countParticle(particlePos);
227 particleDependentBinStruct.countParticle(particlePos);
228 blurredBinStruct.countParticle(particlePos);
229 }
230 } else if (particleIter->isHalo()) {
231 numHaloParticlesCount++;
232 }
233 }
234
235 // Sanity Check
236 if (numOwnedParticlesCount != numOwnedParticles) {
237 AutoPasLog(ERROR,
238 "Number of owned particles tracked by AutoPas ({}) does not match number of owned particles "
239 "counted using the iterator ({}).",
240 numOwnedParticles, numOwnedParticlesCount);
241 }
242
243 infos["numOwnedParticles"] = numOwnedParticlesCount;
244 infos["numHaloParticles"] = numHaloParticlesCount;
245
246 // calculate statistics
247 cellBinStruct.calculateStatistics();
248 particleDependentBinStruct.calculateStatistics();
249 blurredBinStruct.calculateStatistics();
250
251 // write cellBinStruct statistics to live info
252 infos["numEmptyCells"] = cellBinStruct.getNumEmptyBins();
253 infos["maxParticlesPerCell"] = cellBinStruct.getMaxParticlesPerBin();
254 infos["minParticlesPerCell"] = cellBinStruct.getMinParticlesPerBin();
255 infos["medianParticlesPerCell"] = cellBinStruct.getMedianParticlesPerBin();
256 infos["lowerQuartileParticlesPerCell"] = cellBinStruct.getLowerQuartileParticlesPerBin();
257 infos["upperQuartileParticlesPerCell"] = cellBinStruct.getUpperQuartileParticlesPerBin();
258 infos["meanParticlesPerCell"] = cellBinStruct.getMeanParticlesPerBin();
259 infos["relativeParticlesPerCellStdDev"] = cellBinStruct.getRelStdDevParticlesPerBin();
260 infos["particlesPerCellStdDev"] = cellBinStruct.getStdDevParticlesPerBin();
261 infos["estimatedNumNeighborInteractions"] = cellBinStruct.getEstimatedNumberOfNeighborInteractions();
262
263 // write particle dependent bin statistics to live info
264 infos["particleDependentBinMaxDensity"] = particleDependentBinStruct.getMaxDensity();
265 infos["particleDependentBinDensityStdDev"] = particleDependentBinStruct.getStdDevDensity();
266
267 // write blurred bin statistics to live info
268 infos["maxParticlesPerBlurredBin"] = blurredBinStruct.getMaxParticlesPerBin();
269 infos["minParticlesPerBlurredBin"] = blurredBinStruct.getMinParticlesPerBin();
270 infos["medianParticlesPerBlurredBin"] = blurredBinStruct.getMedianParticlesPerBin();
271 infos["lowerQuartileParticlesPerBlurredBin"] = blurredBinStruct.getLowerQuartileParticlesPerBin();
272 infos["upperQuartileParticlesPerBlurredBin"] = blurredBinStruct.getUpperQuartileParticlesPerBin();
273 infos["meanParticlesPerBlurredBin"] = blurredBinStruct.getMeanParticlesPerBin();
274 infos["relativeParticlesPerBlurredBinStdDev"] = blurredBinStruct.getRelStdDevParticlesPerBin();
275 infos["particlesPerBlurredBinStdDev"] = blurredBinStruct.getStdDevParticlesPerBin();
276 }
277
282 [[nodiscard]] const auto &get() const { return infos; }
283
290 template <typename T>
291 T get(const std::string &key) const {
292 // Find the key in the map
293 const auto it = infos.find(key);
294
295 // If key is not found, log an error
296 if (it == infos.end()) {
297 AutoPasLog(ERROR, "Key '" + key + "' not found in infos map.");
298 }
299
300 // Use std::get<T> to extract the value of the desired type
301 try {
302 return std::get<T>(it->second);
303 } catch (const std::bad_variant_access &e) {
304 AutoPasLog(ERROR, "Type mismatch for key '" + key + "'. Requested type does not match the stored type.");
305 }
306 }
307
312 [[nodiscard]] std::string toString() const {
313 auto typeToString = [](auto type) {
314 if constexpr (std::is_same_v<decltype(type), bool> or std::is_same_v<decltype(type), double> or
315 std::is_same_v<decltype(type), size_t>) {
316 return std::to_string(type);
317 } else if constexpr (std::is_base_of_v<Option<decltype(type)>, decltype(type)>) {
318 return type.to_string();
319 }
320 return std::string{"fail"};
321 };
322 auto pairToString = [&](const auto &pair) { return pair.first + "=" + std::visit(typeToString, pair.second); };
323 std::string res{"Live Info: "};
324 if (not infos.empty()) {
325 // We initialize the accumulation with the first element. Hence, the accumulation starts at next(begin).
326 res += std::accumulate(std::next(infos.begin()), infos.end(), pairToString(*infos.begin()),
327 [&](std::string s, const auto &elem) { return std::move(s) + " " + pairToString(elem); });
328 }
329 return res;
330 }
331
338 friend std::ostream &operator<<(std::ostream &out, const LiveInfo &info) {
339 out << info.infos.size() << ' ';
340 for (const auto &[name, val] : info.infos) {
341 // val.index here is the index of this value's type in the LiveInfo::InfoType variant type.
342 // This is needed for determining the type when reading this file via readIndex.
343 out << name << ' ' << val.index() << ' ';
344 std::visit([&](const auto &v) { out << v << ' '; }, val);
345 }
346 return out;
347 }
348
355 friend std::istream &operator>>(std::istream &in, LiveInfo &info) {
356 size_t numElements{0};
357 in >> numElements;
358 for (size_t i = 0; i < numElements; i++) {
359 std::string name;
360 in >> name;
361 size_t idx{0};
362 in >> idx;
363 const auto val =
364 readIndex<LiveInfo::InfoType>(in, idx, std::make_index_sequence<std::variant_size_v<LiveInfo::InfoType>>());
365 info.infos[name] = val;
366 }
367 return in;
368 }
369
375 [[nodiscard]] std::pair<std::string, std::string> getCSVLine() const {
376 // match all words (anything that is neither a ' ' or '='), that are followed by a '=',
377 // ignoring the 'Live Info: ' prefix
378 const auto keyRegex = std::regex("([^= ]+)=[^ ]*");
379 // match all words that are preceded by a '='
380 const auto valueRegex = std::regex("=([^ ]+)");
381
382 auto searchString = toString();
383 // remove leading Live Info:
384 searchString = searchString.substr(std::string("Live Info: ").size());
385
386 std::sregex_iterator keyIter(searchString.begin(), searchString.end(), keyRegex);
387 std::sregex_iterator valueIter(searchString.begin(), searchString.end(), valueRegex);
388 std::sregex_iterator end;
389
390 std::stringstream header;
391 std::stringstream line;
392
393 while (keyIter != end) {
394 // first submatch is the match of the capture group
395 header << keyIter->str(1) << ",";
396 ++keyIter;
397 }
398 while (valueIter != end) {
399 // first submatch is the match of the capture group
400 line << valueIter->str(1) << ",";
401 ++valueIter;
402 }
403
404 auto headerStr = header.str();
405 auto lineStr = line.str();
406 // drop trailing ','
407 headerStr.pop_back();
408 lineStr.pop_back();
409
410 return std::make_pair(headerStr, lineStr);
411 }
412
413 private:
424 template <class Variant, class Type, size_t Idx>
425 static void readIndexHelper(std::istream &in, size_t idx, Variant &var) {
426 if (Idx == idx) {
427 Type val;
428 in >> val;
429 var = val;
430 }
431 }
432
441 template <class Variant, size_t... Idx>
442 static Variant readIndex(std::istream &in, size_t idx, std::index_sequence<Idx...>) {
443 Variant var;
444 (readIndexHelper<Variant, std::variant_alternative_t<Idx, Variant>, Idx>(in, idx, var), ...);
445 return var;
446 }
447
451 std::map<std::string, InfoType> infos;
452};
453
454} // namespace autopas
#define AutoPasLog(lvl, fmt,...)
Macro for logging providing common meta information without filename.
Definition: Logger.h:24
Public iterator class that iterates over a particle container and additional vectors (which are typic...
Definition: ContainerIterator.h:93
bool isValid() const
Check whether the iterator currently points to a valid particle.
Definition: ContainerIterator.h:295
This class is able to gather and store important information for a tuning phase from a container and ...
Definition: LiveInfo.h:32
std::string toString() const
Creates a string containing all live info gathered.
Definition: LiveInfo.h:312
T get(const std::string &key) const
Gets a single value from the infos that corresponds to the given string.
Definition: LiveInfo.h:291
std::pair< std::string, std::string > getCSVLine() const
Generate a csv representation containing all values from the toString() method.
Definition: LiveInfo.h:375
friend std::istream & operator>>(std::istream &in, LiveInfo &info)
Stream operator to read the LiveInfo in from a stream.
Definition: LiveInfo.h:355
friend std::ostream & operator<<(std::ostream &out, const LiveInfo &info)
Stream operator to write the LiveInfo to a stream.
Definition: LiveInfo.h:338
std::variant< bool, double, size_t, ContainerOption, TraversalOption, LoadEstimatorOption, DataLayoutOption, Newton3Option > InfoType
The type of an info.
Definition: LiveInfo.h:118
void gather(ContainerIterator< Particle_T, true, false > particleIter, size_t rebuildFrequency, size_t numOwnedParticles, std::array< double, 3 > boxMin, std::array< double, 3 > boxMax, double cutoff, double skin)
Gathers key statistics that define the computational profile of the simulation, in order to provide l...
Definition: LiveInfo.h:188
const auto & get() const
Returns a map of all infos.
Definition: LiveInfo.h:282
Class representing the load estimator choices.
Definition: LoadEstimatorOption.h:18
Particle-counting bin structure.
Definition: ParticleBinStructure.h:34
constexpr std::array< target_T, SIZE > ceilAndCast(const std::array< float_T, SIZE > &a)
Ceils all array elements and converts them to a different type.
Definition: ArrayMath.h:352
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