SPH
NBodySolver.cpp
Go to the documentation of this file.
1 #include "gravity/NBodySolver.h"
3 #include "gravity/Collision.h"
4 #include "io/Logger.h"
6 #include "quantities/Quantity.h"
7 #include "sph/Diagnostics.h"
8 #include "system/Factory.h"
9 #include "system/Settings.h"
10 #include "system/Statistics.h"
11 #include "system/Timer.h"
12 #include <map>
13 #include <set>
14 
16 
18  : HardSphereSolver(scheduler, settings, Factory::getGravity(settings)) {}
19 
21  const RunSettings& settings,
23  : HardSphereSolver(scheduler,
24  settings,
25  std::move(gravity),
26  Factory::getCollisionHandler(settings),
27  Factory::getOverlapHandler(settings)) {}
28 
30  const RunSettings& settings,
32  AutoPtr<ICollisionHandler>&& collisionHandler,
33  AutoPtr<IOverlapHandler>&& overlapHandler)
34  : gravity(std::move(gravity))
35  , scheduler(scheduler)
36  , threadData(scheduler) {
37  collision.handler = std::move(collisionHandler);
38  collision.finder = Factory::getFinder(settings);
39  overlap.handler = std::move(overlapHandler);
40  overlap.allowedRatio = settings.get<Float>(RunSettingsId::COLLISION_ALLOWED_OVERLAP);
41  rigidBody.use = settings.get<bool>(RunSettingsId::NBODY_INERTIA_TENSOR);
42  rigidBody.maxAngle = settings.get<Float>(RunSettingsId::NBODY_MAX_ROTATION_ANGLE);
43 }
44 
46 
47 void HardSphereSolver::rotateLocalFrame(Storage& storage, const Float dt) {
52 
53  for (Size i = 0; i < L.size(); ++i) {
54  if (L[i] == Vector(0._f)) {
55  continue;
56  }
57  AffineMatrix Em = convert<AffineMatrix>(E[i]);
58 
59  const Float omega = getLength(w[i]);
60  const Float dphi = omega * dt;
61 
62  if (almostEqual(I[0], SymmetricTensor(Vector(I[0].trace() / 3._f), Vector(0._f)), 1.e-6_f)) {
63  // (almost) isotropic particle, we can skip the substepping and omega integration
64  const Vector dir = getNormalized(w[i]);
65  AffineMatrix rotation = AffineMatrix::rotateAxis(dir, dphi);
67  E[i] = convert<Tensor>(rotation * Em);
68  continue;
69  }
70 
71  // To ensure we never rotate more than maxAngle, we do a 'substepping' of angular velocity here;
72  // rotate the local frame around the current omega by maxAngle, compute the new omega, and so on,
73  // until we rotated by dphi
74  // To disable it, just set maxAngle to very high value. Note that nothing gets 'broken'; both angular
75  // momentum and moment of inertia are always conserved (by construction), but the precession might not
76  // be solved correctly
77  for (Float totalRot = 0._f; totalRot < dphi; totalRot += rigidBody.maxAngle) {
78  const Vector dir = getNormalized(w[i]);
79 
80  const Float rot = min(rigidBody.maxAngle, dphi - totalRot);
81  AffineMatrix rotation = AffineMatrix::rotateAxis(dir, rot);
82 
84  Em = rotation * Em;
85 
86  // compute new angular velocity, to keep angular velocity consistent with angular momentum
87  // note that this assumes that L and omega are set up consistently
88  const SymmetricTensor I_in = transform(I[i], Em);
89  const SymmetricTensor I_inv = I_in.inverse();
90  w[i] = I_inv * L[i];
91  }
92  E[i] = convert<Tensor>(Em);
93  }
94 }
95 
98 
99  Timer timer;
100  gravity->build(scheduler, storage);
101 
103  SPH_ASSERT_UNEVAL(std::all_of(dv.begin(), dv.end(), [](const Vector& a) { return a == Vector(0._f); }));
104  gravity->evalAll(scheduler, dv, stats);
105 
106  // null all derivatives of smoothing lengths (particle radii)
108  for (Size i = 0; i < v.size(); ++i) {
109  v[i][H] = 0._f;
110  dv[i][H] = 0._f;
111  }
113 }
114 
116 private:
117  Statistics& stats;
118 
119 public:
122 
125 
128 
131 
132  explicit CollisionStats(Statistics& stats)
133  : stats(stats) {}
134 
140  }
141 
142  void clasify(const CollisionResult result) {
143  collisionCount++;
144  switch (result) {
146  bounceCount++;
147  break;
149  mergerCount++;
150  break;
152  // do nothing
153  break;
154  default:
156  }
157  }
158 };
159 
160 
163  Size i = Size(-1);
164  Size j = Size(-1);
165 
166  Float collisionTime = INFINITY;
167  Float overlap = 0._f;
168 
169  CollisionRecord() = default;
170 
171  CollisionRecord(const Size i, const Size j, const Float overlap, const Float time)
172  : i(i)
173  , j(j)
174  , collisionTime(time)
175  , overlap(overlap) {}
176 
177  bool operator==(const CollisionRecord& other) const {
178  return i == other.i && j == other.j && collisionTime == other.collisionTime &&
179  overlap == other.overlap;
180  }
181 
182  bool operator<(const CollisionRecord& other) const {
183  return std::make_tuple(collisionTime, -overlap, i, j) <
184  std::make_tuple(other.collisionTime, -other.overlap, other.i, other.j);
185  }
186 
188  explicit operator bool() const {
189  return overlap > 0._f || collisionTime < INFTY;
190  }
191 
192  static CollisionRecord COLLISION(const Size i, const Size j, const Float time) {
193  return CollisionRecord(i, j, 0._f, time);
194  }
195 
196  static CollisionRecord OVERLAP(const Size i, const Size j, const Float time, const Float overlap) {
197  return CollisionRecord(i, j, overlap, time);
198  }
199 
200  bool isOverlap() const {
201  return overlap > 0._f;
202  }
203 
204  friend bool isReal(const CollisionRecord& col) {
205  return col.isOverlap() ? isReal(col.overlap) : isReal(col.collisionTime);
206  }
207 };
208 
210 public:
211  using Iterator = std::set<CollisionRecord>::const_iterator;
212 
213 private:
214  // holds all collisions
215  std::set<CollisionRecord> collisions;
216 
217  // maps particles to the collisions
218  std::multimap<Size, CollisionRecord> indexToCollision;
219 
220  using Itc = std::multimap<Size, CollisionRecord>::const_iterator;
221 
222 public:
223  void insert(const CollisionRecord& col) {
224  Iterator iter;
225  bool inserted;
226  std::tie(iter, inserted) = collisions.insert(col);
227  if (!inserted) {
228  return;
229  }
230  indexToCollision.insert(std::make_pair(col.i, col));
231  indexToCollision.insert(std::make_pair(col.j, col));
232  }
233 
234  template <typename TIter>
235  void insert(TIter first, TIter last) {
236  for (TIter iter = first; iter != last; ++iter) {
237  insert(*iter);
238  }
239  checkConsistency();
240  }
241 
242  const CollisionRecord& top() const {
243  return *collisions.begin();
244  }
245 
246  bool empty() const {
247  SPH_ASSERT(collisions.empty() == indexToCollision.empty());
248  return collisions.empty();
249  }
250 
251  bool has(const Size idx) const {
252  return indexToCollision.count(idx) > 0;
253  }
254 
256  removeIndex(col, col.i);
257  removeIndex(col, col.j);
258  const Size removed = collisions.erase(col);
259  SPH_ASSERT(removed == 1);
260  checkConsistency();
261  }
262 
263  void removeByIndex(const Size idx, FlatSet<Size>& removed) {
264  Itc first, last;
265  removed.insert(idx);
266  std::tie(first, last) = indexToCollision.equal_range(idx);
267  // last iterator may be invalidated, so we need to be careful
268  for (Itc itc = first; itc->first == idx && itc != indexToCollision.end();) {
269  const Size otherIdx = (itc->second.i == idx) ? itc->second.j : itc->second.i;
270  removed.insert(otherIdx);
271  collisions.erase(itc->second);
272  // erase the other particle as well
273  removeIndex(itc->second, otherIdx);
274  itc = indexToCollision.erase(itc);
275  }
276  checkConsistency();
277  }
278 
279 private:
280  void removeIndex(const CollisionRecord& col, const Size idx) {
281  SPH_ASSERT(col.i == idx || col.j == idx);
282  Itc first, last;
283  std::tie(first, last) = indexToCollision.equal_range(idx);
284  for (Itc itc = first; itc != last; ++itc) {
285  if (itc->second == col) {
286  indexToCollision.erase(itc);
287  return;
288  }
289  }
290  SPH_ASSERT(false, "Collision not found");
291  }
292 
293 #ifdef SPH_DEBUG
294  void checkConsistency() const {
295  SPH_ASSERT(2 * collisions.size() == indexToCollision.size());
296  // all collisions are in the index-to-colision map
297  for (const CollisionRecord& col : collisions) {
298  SPH_ASSERT(indexToCollision.count(col.i));
299  SPH_ASSERT(indexToCollision.count(col.j));
300  SPH_ASSERT(hasCollision(col, col.i));
301  SPH_ASSERT(hasCollision(col, col.j));
302  }
303 
304  // all entries in index-to-collision map have a corresponding collision
305  for (const auto& p : indexToCollision) {
306  SPH_ASSERT(collisions.find(p.second) != collisions.end());
307  }
308  }
309 #else
310  void checkConsistency() const {}
311 #endif
312 
313  bool hasCollision(const CollisionRecord& col, const Size idx) const {
314  Itc first, last;
315  std::tie(first, last) = indexToCollision.equal_range(idx);
316  for (Itc itc = first; itc != last; ++itc) {
317  if (itc->second == col) {
318  return true;
319  }
320  }
321  return false;
322  }
323 };
324 
325 void HardSphereSolver::collide(Storage& storage, Statistics& stats, const Float dt) {
327 
328  if (!collision.handler) {
329  // ignore all collisions
330  return;
331  }
332 
333  Timer timer;
334  if (rigidBody.use) {
335  rotateLocalFrame(storage, dt);
336  }
337 
339  tie(r, v, a) = storage.getAll<Vector>(QuantityId::POSITION);
340 
341  // find the radius of possible colliders
342  // const Float searchRadius = getSearchRadius(r, v, dt);
343 
344  // tree for finding collisions
345  collision.finder->buildWithRank(SEQUENTIAL, r, [this, dt](const Size i, const Size j) {
346  return r[i][H] + getLength(v[i]) * dt < r[j][H] + getLength(v[j]) * dt;
347  });
348 
349  // handler determining collision outcomes
350  collision.handler->initialize(storage);
351  overlap.handler->initialize(storage);
352 
353  searchRadii.resize(r.size());
354  searchRadii.fill(0._f);
355 
356  for (ThreadData& data : threadData) {
357  data.collisions.clear();
358  }
359 
360  // first pass - find all collisions and sort them by collision time
361  parallelFor(scheduler, threadData, 0, r.size(), [&](const Size i, ThreadData& data) {
362  if (CollisionRecord col =
363  this->findClosestCollision(i, SearchEnum::FIND_LOWER_RANK, Interval(0._f, dt), data.neighs)) {
364  SPH_ASSERT(isReal(col));
365  data.collisions.push(col);
366  }
367  });
368 
369  // Holds all detected collisions.
370  CollisionSet collisions;
371 
372  // reduce thread-local containers
373  {
374  ThreadData* main = nullptr;
375  for (ThreadData& data : threadData) {
376  if (!main) {
377  main = &data;
378  } else {
379  main->collisions.insert(
380  main->collisions.size(), data.collisions.begin(), data.collisions.end());
381  data.collisions.clear();
382  }
383  }
384  // sort to get a deterministic order in index-to-collision maps
385  std::sort(main->collisions.begin(), main->collisions.end());
386  collisions.insert(main->collisions.begin(), main->collisions.end());
387  }
388 
389  CollisionStats cs(stats);
390  removed.clear();
391 
397 
398  FlatSet<Size> invalidIdxs;
399  while (!collisions.empty()) {
400  // find first collision in the list
401  const CollisionRecord& col = collisions.top();
402  const Float t_coll = col.collisionTime;
403  SPH_ASSERT(t_coll < dt);
404 
405  Size i = col.i;
406  Size j = col.j;
407 
408  // advance the positions of collided particles to the collision time
409  r[i] += v[i] * t_coll;
410  r[j] += v[j] * t_coll;
411  SPH_ASSERT(isReal(r[i]) && isReal(r[j]));
412 
413  // check and handle overlaps
414  CollisionResult result;
415  if (col.isOverlap()) {
416  overlap.handler->handle(i, j, removed);
417  result = CollisionResult::BOUNCE;
418  cs.overlapCount++;
419  } else {
420  result = collision.handler->collide(i, j, removed);
421  cs.clasify(result);
422  }
423 
424  // move the positions back to the beginning of the timestep
425  r[i] -= v[i] * t_coll;
426  r[j] -= v[j] * t_coll;
427  SPH_ASSERT(isReal(r[i]) && isReal(r[j]));
428 
429  if (result == CollisionResult::NONE) {
430  // no collision to process
431  collisions.removeByCollision(col);
432  continue;
433  }
434 
435  // remove all collisions containing either i or j
436  invalidIdxs.clear();
437  collisions.removeByIndex(i, invalidIdxs);
438  collisions.removeByIndex(j, invalidIdxs);
439  SPH_ASSERT(!collisions.has(i));
440  SPH_ASSERT(!collisions.has(j));
441 
442  const Interval interval(t_coll + EPS, dt);
443  if (!interval.empty()) {
444  for (Size idx : invalidIdxs) {
445  // here we shouldn't search any removed particle
446  if (removed.find(idx) != removed.end()) {
447  continue;
448  }
449  if (CollisionRecord c =
450  this->findClosestCollision(idx, SearchEnum::USE_RADII, interval, neighs)) {
451  SPH_ASSERT(isReal(c));
452  SPH_ASSERT(removed.find(c.i) == removed.end() && removed.find(c.j) == removed.end());
453  if ((c.i == i && c.j == j) || (c.j == i && c.i == j)) {
454  // don't process the same pair twice in a row
455  continue;
456  }
457 
458  collisions.insert(c);
459  }
460  }
461  }
462  }
463 
464  // apply the removal list
465  if (!removed.empty()) {
467  // remove it also from all dependent storages, since this is a permanent action
468  storage.propagate([this](Storage& dependent) { dependent.remove(removed); });
469  }
470  SPH_ASSERT(storage.isValid());
471 
472  stats.set(StatisticsId::COLLISION_EVAL_TIME, int(timer.elapsed(TimerUnit::MILLISECOND)));
473 }
474 
475 void HardSphereSolver::create(Storage& storage, IMaterial& UNUSED(material)) const {
477 
478  // dependent quantity, computed from angular momentum
480 
481  if (rigidBody.use) {
483  storage.insert<SymmetricTensor>(
488  for (Size i = 0; i < r.size(); ++i) {
489  I[i] = Rigid::sphereInertia(m[i], r[i][H]);
490  }
491 
492  // zero order, we integrate the frame coordinates manually
494  }
495 }
496 
497 CollisionRecord HardSphereSolver::findClosestCollision(const Size i,
498  const SearchEnum opt,
499  const Interval interval,
500  Array<NeighbourRecord>& neighs) {
501  SPH_ASSERT(!interval.empty());
502  if (opt == SearchEnum::FIND_LOWER_RANK) {
503  // maximum travel of i-th particle
504  const Float radius = r[i][H] + getLength(v[i]) * interval.upper();
505  collision.finder->findLowerRank(i, 2._f * radius, neighs);
506  } else {
507  SPH_ASSERT(isReal(searchRadii[i]));
508  if (searchRadii[i] > 0._f) {
509  collision.finder->findAll(i, 2._f * searchRadii[i], neighs);
510  } else {
511  return CollisionRecord{};
512  }
513  }
514 
515  CollisionRecord closestCollision;
516  for (NeighbourRecord& n : neighs) {
517  const Size j = n.index;
518  if (opt == SearchEnum::FIND_LOWER_RANK) {
519  searchRadii[i] = searchRadii[j] = r[i][H] + getLength(v[i]) * interval.upper();
520  }
521  if (i == j || removed.find(j) != removed.end()) {
522  // particle already removed, skip
523  continue;
524  }
525  // advance positions to the start of the interval
526  const Vector r1 = r[i] + v[i] * interval.lower();
527  const Vector r2 = r[j] + v[j] * interval.lower();
528  const Float overlapValue = 1._f - getSqrLength(r1 - r2) / sqr(r[i][H] + r[j][H]);
529  if (overlapValue > sqr(overlap.allowedRatio)) {
530  if (overlap.handler->overlaps(i, j)) {
531  // this overlap needs to be handled
532  return CollisionRecord::OVERLAP(i, j, interval.lower(), overlapValue);
533  } else {
534  // skip this overlap, which also implies skipping the collision, so just continue
535  continue;
536  }
537  }
538 
539  Optional<Float> t_coll = this->checkCollision(r1, v[i], r2, v[j], interval.size());
540  if (t_coll) {
541  // t_coll is relative to the interval, convert to timestep 'coordinates'
542  const Float time = t_coll.value() + interval.lower();
543  closestCollision = min(closestCollision, CollisionRecord::COLLISION(i, j, time));
544  }
545  }
546  return closestCollision;
547 }
548 
549 Optional<Float> HardSphereSolver::checkCollision(const Vector& r1,
550  const Vector& v1,
551  const Vector& r2,
552  const Vector& v2,
553  const Float dt) const {
554  const Vector dr = r1 - r2;
555  const Vector dv = v1 - v2;
556  const Float dvdr = dot(dv, dr);
557  if (dvdr >= 0._f) {
558  // not moving towards each other, no collision
559  return NOTHING;
560  }
561 
562  const Vector dr_perp = dr - dot(dv, dr) * dv / getSqrLength(dv);
563  SPH_ASSERT(getSqrLength(dr_perp) < (1._f + EPS) * getSqrLength(dr), dr_perp, dr);
564  if (getSqrLength(dr_perp) <= sqr(r1[H] + r2[H])) {
565  // on collision trajectory, find the collision time
566  const Float dv2 = getSqrLength(dv);
567  const Float det = 1._f - (getSqrLength(dr) - sqr(r1[H] + r2[H])) / sqr(dvdr) * dv2;
568  // SPH_ASSERT(det >= 0._f);
569  const Float sqrtDet = sqrt(max(0._f, det));
570  const Float root = (det > 1._f) ? 1._f + sqrtDet : 1._f - sqrtDet;
571  const Float t_coll = -dvdr / dv2 * root;
572  SPH_ASSERT(isReal(t_coll) && t_coll >= 0._f);
573 
574  // t_coll can never be negative (which we check by assert), so only check if it is lower than the
575  // interval size
576  if (t_coll <= dt) {
577  return t_coll;
578  }
579  }
580  return NOTHING;
581 }
582 
584  : SoftSphereSolver(scheduler, settings, Factory::getGravity(settings)) {}
585 
587  const RunSettings& settings,
589  : gravity(std::move(gravity))
590  , scheduler(scheduler)
591  , threadData(scheduler) {
592  force.repel = settings.get<Float>(RunSettingsId::SOFT_REPEL_STRENGTH);
593  force.friction = settings.get<Float>(RunSettingsId::SOFT_FRICTION_STRENGTH);
594 }
595 
597 
599  VERBOSE_LOG;
600 
601  Timer timer;
602  gravity->build(scheduler, storage);
603 
605  ArrayView<Vector> r, v, dv;
606  tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION);
607  SPH_ASSERT_UNEVAL(std::all_of(dv.begin(), dv.end(), [](const Vector& a) { return a == Vector(0._f); }));
608  gravity->evalAll(scheduler, dv, stats);
609 
611  timer.restart();
612  const IBasicFinder& finder = *gravity->getFinder();
613 
614  // precompute the search radii
615  Float maxRadius = 0._f;
616  for (Size i = 0; i < r.size(); ++i) {
617  maxRadius = max(maxRadius, r[i][H]);
618  }
619 
620  auto functor = [this, r, maxRadius, m, &v, &dv, &finder](Size i, ThreadData& data) {
621  finder.findAll(i, 2._f * maxRadius, data.neighs);
622  Vector f(0._f);
623  for (auto& n : data.neighs) {
624  const Size j = n.index;
625  const Float hi = r[i][H];
626  const Float hj = r[j][H];
627  if (i == j || n.distanceSqr >= sqr(hi + hj)) {
628  // aren't actual neighbours
629  continue;
630  }
631  const Float hbar = 0.5_f * (hi + hj);
632  // dimensionless acceleration to SI
633  const Float unit = Constants::gravity / sqr(hbar);
634  const Float dist = sqrt(n.distanceSqr);
635  const Float radialForce = force.repel * pow<6>((hi + hj) / dist);
636  const Vector dir = getNormalized(r[i] - r[j]);
637  f += unit * m[j] * dir * radialForce;
638 
639  const Float vsqr = getSqrLength(v[i] - v[j]);
640  if (vsqr > EPS) {
641  const Vector velocityDir = getNormalized(v[i] - v[j]);
642  f -= unit * m[j] * velocityDir * force.friction;
643  }
644  }
645  dv[i] += f;
646 
647  // null all derivatives of smoothing lengths (particle radii)
648  v[i][H] = 0._f;
649  dv[i][H] = 0._f;
650  };
651  parallelFor(scheduler, threadData, 0, r.size(), functor);
653 }
654 
655 void SoftSphereSolver::create(Storage& UNUSED(storage), IMaterial& UNUSED(material)) const {}
656 
INLINE bool almostEqual(const AffineMatrix &m1, const AffineMatrix &m2, const Float eps=EPS)
Definition: AffineMatrix.h:278
INLINE bool isReal(const AntisymmetricTensor &t)
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define SPH_ASSERT_UNEVAL(x,...)
Definition: Assert.h:96
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Definition: Assert.h:100
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
Simple gravity solver evaluating all particle pairs.
Collision handling.
CollisionResult
Definition: Collision.h:15
@ BOUNCE
Bounce/scatter collision, no merging and no fragmentation.
@ MERGER
Particles merged together.
@ NONE
No collision took place.
const float radius
Definition: CurveDialog.cpp:18
Looking for problems in SPH simulation and reporting potential errors.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
Logging routines of the run.
#define VERBOSE_LOG
Helper macro, creating.
Definition: Logger.h:242
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
NAMESPACE_SPH_BEGIN constexpr INLINE T min(const T &f1, const T &f2)
Minimum & Maximum value.
Definition: MathBasic.h:15
INLINE Float root(const Float f)
constexpr Float EPS
Definition: MathUtils.h:30
constexpr INLINE T sqr(const T &f) noexcept
Return a squared value.
Definition: MathUtils.h:67
INLINE T sqrt(const T f)
Return a squared root of a value.
Definition: MathUtils.h:78
constexpr Float INFTY
Definition: MathUtils.h:38
constexpr INLINE Float pow< 6 >(const Float v)
Definition: MathUtils.h:144
constexpr Float E
Definition: MathUtils.h:365
Solver performing N-body simulation.
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const NothingType NOTHING
Definition: Optional.h:16
@ LOCAL_FRAME
Local coordinates of a particle; moment of inertia is typically expressed in these coordinates.
@ MOMENT_OF_INERTIA
Moment of inertia of particles, analogy of particle masses for rotation.
@ POSITION
Positions (velocities, accelerations) of particles, always a vector quantity,.
@ MASS
Paricles masses, always a scalar quantity.
Holder of quantity values and their temporal derivatives.
@ ZERO
Quantity without derivatives, or "zero order" of quantity.
SequentialScheduler SEQUENTIAL
Global instance of the sequential scheduler.
Definition: Scheduler.cpp:43
INLINE void parallelFor(IScheduler &scheduler, const Size from, const Size to, TFunctor &&functor)
Executes a functor concurrently from all available threads.
Definition: Scheduler.h:98
StaticArray< T0 &, sizeof...(TArgs)+1 > tie(T0 &t0, TArgs &... rest)
Creates a static array from a list of l-value references.
Definition: StaticArray.h:281
Statistics gathered and periodically displayed during the run.
@ COLLISION_EVAL_TIME
Wallclock duration of collision evaluation.
@ TOTAL_COLLISION_COUNT
Number of collisions in the timestep.
@ OVERLAP_COUNT
Number of particle overlaps detected during collision evaluation.
@ MERGER_COUNT
Number of mergers in the timestep.
@ GRAVITY_EVAL_TIME
Wallclock duration of gravity evaluation.
@ BOUNCE_COUNT
Number of bounce collisions.
INLINE SymmetricTensor transform(const SymmetricTensor &t, const AffineMatrix &transform)
Measuring time intervals and executing periodic events.
INLINE Float getLength(const Vector &v)
Returns the length of the vector. Enabled only for vectors of floating-point precision.
Definition: Vector.h:579
INLINE Float getSqrLength(const Vector &v)
Definition: Vector.h:574
BasicVector< Float > Vector
Definition: Vector.h:539
INLINE float dot(const BasicVector< float > &v1, const BasicVector< float > &v2)
Make sure the vector is trivially constructible and destructible, needed for fast initialization of a...
Definition: Vector.h:548
INLINE Vector getNormalized(const Vector &v)
Definition: Vector.h:590
@ H
Definition: Vector.h:25
int main(int argc, char *argv[])
Definition: main.cpp:7
static AffineMatrix rotateAxis(const Vector &axis, const Float angle)
Definition: AffineMatrix.h:159
bool isOrthogonal() const
Definition: AffineMatrix.h:111
INLINE TCounter size() const
Definition: ArrayView.h:101
INLINE Iterator< StorageType > begin()
Definition: ArrayView.h:55
INLINE Iterator< StorageType > end()
Definition: ArrayView.h:63
void resize(const TCounter newSize)
Resizes the array to new size.
Definition: Array.h:215
void fill(const T &t)
Sets all elements of the array to given value.
Definition: Array.h:187
void insert(TIter first, TIter last)
void insert(const CollisionRecord &col)
bool empty() const
void removeByIndex(const Size idx, FlatSet< Size > &removed)
void removeByCollision(const CollisionRecord &col)
bool has(const Size idx) const
const CollisionRecord & top() const
void clasify(const CollisionResult result)
Size collisionCount
Number of all collisions (does not count overlaps)
Size bounceCount
Out of all collisions, how many bounces.
CollisionStats(Statistics &stats)
Size overlapCount
Number of overlaps handled.
Size mergerCount
Out of all collisions, how many mergers.
bool insert(U &&value)
Definition: FlatSet.h:45
void clear()
Definition: FlatSet.h:99
Iterator< T > find(const T &value)
Definition: FlatSet.h:76
Iterator< T > end()
Definition: FlatSet.h:111
Solver computing gravitational interactions of hard-sphere particles.
Definition: NBodySolver.h:24
HardSphereSolver(IScheduler &scheduler, const RunSettings &settings)
Creates the solver, using the gravity implementation specified by settings.
Definition: NBodySolver.cpp:17
virtual void collide(Storage &storage, Statistics &stats, const Float dt) override
Checks and resolves particle collisions.
virtual void integrate(Storage &storage, Statistics &stats) override
Computes derivatives of all time-dependent quantities.
Definition: NBodySolver.cpp:96
~HardSphereSolver() override
virtual void create(Storage &storage, IMaterial &material) const override
Initializes all quantities needed by the solver in the storage.
Interface of objects finding neighbouring particles.
virtual Size findAll(const Size index, const Float radius, Array< NeighbourRecord > &neighbours) const =0
Finds all neighbours within given radius from the point given by index.
virtual RawPtr< const IBasicFinder > getFinder() const =0
Optionally returns a finder used by the gravity implementation.
virtual void build(IScheduler &scheduler, const Storage &storage)=0
Builds the accelerating structure.
virtual void evalAll(IScheduler &scheduler, ArrayView< Vector > dv, Statistics &stats) const =0
Evaluates the gravitational acceleration concurrently.
Material settings and functions specific for one material.
Definition: IMaterial.h:110
Interface that allows unified implementation of sequential and parallelized versions of algorithms.
Definition: Scheduler.h:27
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
INLINE Float lower() const
Returns lower bound of the interval.
Definition: Interval.h:74
INLINE Float upper() const
Returns upper bound of the interval.
Definition: Interval.h:79
INLINE Float size() const
Returns the size of the interval.
Definition: Interval.h:89
INLINE bool empty() const
Returns true if the interval is empty (default constructed).
Definition: Interval.h:104
Simple (forward) iterator over continuous array of objects of type T.
Definition: Iterator.h:18
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
TValue get(const TEnum idx, std::enable_if_t<!std::is_enum< std::decay_t< TValue >>::value, int >=0) const
Returns a value of given type from the settings.
Definition: Settings.h:326
Solver computing gravitational interactions and repulsive forces between particles.
Definition: NBodySolver.h:137
virtual void create(Storage &storage, IMaterial &material) const override
Initializes all quantities needed by the solver in the storage.
~SoftSphereSolver() override
SoftSphereSolver(IScheduler &scheduler, const RunSettings &settings)
virtual void integrate(Storage &storage, Statistics &stats) override
Computes derivatives of all time-dependent quantities.
Object holding various statistics about current run.
Definition: Statistics.h:22
Statistics & set(const StatisticsId idx, TValue &&value)
Sets new values of a statistic.
Definition: Statistics.h:52
Container storing all quantities used within the simulations.
Definition: Storage.h:230
Outcome isValid(const Flags< ValidFlag > flags=ValidFlag::COMPLETE) const
Checks whether the storage is in valid state.
Definition: Storage.cpp:609
Quantity & insert(const QuantityId key, const OrderEnum order, const TValue &defaultValue)
Creates a quantity in the storage, given its key, value type and order.
Definition: Storage.cpp:270
StaticArray< Array< TValue > &, 3 > getAll(const QuantityId key)
Retrieves quantity buffers from the storage, given its key and value type.
Definition: Storage.cpp:163
Array< TValue > & getDt(const QuantityId key)
Retrieves a quantity derivative from the storage, given its key and value type.
Definition: Storage.cpp:217
void propagate(const Function< void(Storage &storage)> &functor)
Executes a given functor recursively for all dependent storages.
Definition: Storage.cpp:424
@ INDICES_SORTED
Use if the given array is already sorted (optimization)
void remove(ArrayView< const Size > idxs, const Flags< IndicesFlag > flags=EMPTY_FLAGS)
Removes specified particles from the storage.
Definition: Storage.cpp:756
Array< TValue > & getD2t(const QuantityId key)
Retrieves a quantity second derivative from the storage, given its key and value type.
Definition: Storage.cpp:243
Array< TValue > & getValue(const QuantityId key)
Retrieves a quantity values from the storage, given its key and value type.
Definition: Storage.cpp:191
Symmetric tensor of 2nd order.
INLINE SymmetricTensor inverse() const
static INLINE SymmetricTensor null()
Returns a tensor with all zeros.
Generic 2nd-order tensor with 9 independent components.
Definition: Tensor.h:13
static Tensor identity()
Definition: Tensor.h:81
Basic time-measuring tool. Starts automatically when constructed.
Definition: Timer.h:27
int64_t elapsed(const TimerUnit unit) const
Returns elapsed time in timer units. Does not reset the timer.
Definition: Timer.cpp:55
void restart()
Reset elapsed duration to zero.
Definition: Timer.cpp:51
Creating code components based on values from settings.
Generic storage and input/output routines of settings.
@ SOFT_REPEL_STRENGTH
Magnitude of the repel force for the soft-body solver.
@ NBODY_MAX_ROTATION_ANGLE
@ COLLISION_ALLOWED_OVERLAP
@ SOFT_FRICTION_STRENGTH
Magnitude of the friction force for the soft-body solver.
constexpr Float gravity
Gravitational constant (CODATA 2014)
Definition: Constants.h:29
Provides a convenient way to construct objects from settings.
Definition: Factory.h:46
AutoPtr< IGravity > getGravity(const RunSettings &settings)
Definition: Factory.cpp:325
AutoPtr< ISymmetricFinder > getFinder(const RunSettings &settings)
Definition: Factory.cpp:156
AutoPtr< ICollisionHandler > getCollisionHandler(const RunSettings &settings)
Definition: Factory.cpp:379
AutoPtr< IOverlapHandler > getOverlapHandler(const RunSettings &settings)
Definition: Factory.cpp:395
INLINE SymmetricTensor sphereInertia(const Float m, const Float r)
Computes the inertia tensor of a homogeneous sphere.
Definition: Functions.h:61
Overload of std::swap for Sph::Array.
Definition: Array.h:578
CollisionRecord(const Size i, const Size j, const Float overlap, const Float time)
static CollisionRecord OVERLAP(const Size i, const Size j, const Float time, const Float overlap)
static CollisionRecord COLLISION(const Size i, const Size j, const Float time)
CollisionRecord()=default
bool operator==(const CollisionRecord &other) const
bool operator<(const CollisionRecord &other) const
bool isOverlap() const
friend bool isReal(const CollisionRecord &col)
Size i
Indices of the collided particles.
Holds information about a neighbour particles.