35 , scheduler(scheduler)
36 , threadData(scheduler) {
37 collision.handler = std::move(collisionHandler);
39 overlap.handler = std::move(overlapHandler);
47 void HardSphereSolver::rotateLocalFrame(
Storage& storage,
const Float dt) {
53 for (
Size i = 0; i < L.size(); ++i) {
54 if (L[i] ==
Vector(0._f)) {
60 const Float dphi = omega * dt;
67 E[i] = convert<Tensor>(rotation * Em);
77 for (
Float totalRot = 0._f; totalRot < dphi; totalRot += rigidBody.maxAngle) {
80 const Float rot =
min(rigidBody.maxAngle, dphi - totalRot);
92 E[i] = convert<Tensor>(Em);
100 gravity->
build(scheduler, storage);
104 gravity->
evalAll(scheduler, dv, stats);
108 for (
Size i = 0; i < v.
size(); ++i) {
188 explicit operator bool()
const {
211 using Iterator = std::set<CollisionRecord>::const_iterator;
215 std::set<CollisionRecord> collisions;
218 std::multimap<Size, CollisionRecord> indexToCollision;
220 using Itc = std::multimap<Size, CollisionRecord>::const_iterator;
226 std::tie(iter, inserted) = collisions.insert(col);
230 indexToCollision.insert(std::make_pair(col.
i, col));
231 indexToCollision.insert(std::make_pair(col.
j, col));
234 template <
typename TIter>
236 for (TIter iter = first; iter != last; ++iter) {
243 return *collisions.begin();
247 SPH_ASSERT(collisions.empty() == indexToCollision.empty());
248 return collisions.empty();
252 return indexToCollision.count(idx) > 0;
256 removeIndex(col, col.
i);
257 removeIndex(col, col.
j);
258 const Size removed = collisions.erase(col);
266 std::tie(first, last) = indexToCollision.equal_range(idx);
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;
271 collisions.erase(itc->second);
273 removeIndex(itc->second, otherIdx);
274 itc = indexToCollision.erase(itc);
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);
294 void checkConsistency()
const {
295 SPH_ASSERT(2 * collisions.size() == indexToCollision.size());
305 for (
const auto& p : indexToCollision) {
306 SPH_ASSERT(collisions.find(p.second) != collisions.end());
310 void checkConsistency()
const {}
315 std::tie(first, last) = indexToCollision.equal_range(idx);
316 for (Itc itc = first; itc != last; ++itc) {
317 if (itc->second == col) {
328 if (!collision.handler) {
335 rotateLocalFrame(storage, dt);
345 collision.finder->buildWithRank(
SEQUENTIAL, r, [
this, dt](
const Size i,
const Size j) {
350 collision.handler->initialize(storage);
351 overlap.handler->initialize(storage);
353 searchRadii.
resize(r.size());
354 searchRadii.
fill(0._f);
356 for (ThreadData& data : threadData) {
357 data.collisions.clear();
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);
374 ThreadData*
main =
nullptr;
375 for (ThreadData& data : threadData) {
379 main->collisions.insert(
380 main->collisions.size(), data.collisions.begin(), data.collisions.end());
381 data.collisions.clear();
385 std::sort(
main->collisions.begin(),
main->collisions.end());
386 collisions.insert(
main->collisions.begin(),
main->collisions.end());
399 while (!collisions.empty()) {
409 r[i] += v[i] * t_coll;
410 r[j] += v[j] * t_coll;
416 overlap.handler->handle(i, j, removed);
420 result = collision.handler->collide(i, j, removed);
425 r[i] -= v[i] * t_coll;
426 r[j] -= v[j] * t_coll;
431 collisions.removeByCollision(col);
437 collisions.removeByIndex(i, invalidIdxs);
438 collisions.removeByIndex(j, invalidIdxs);
443 if (!interval.empty()) {
444 for (
Size idx : invalidIdxs) {
446 if (removed.find(idx) != removed.end()) {
450 this->findClosestCollision(idx, SearchEnum::USE_RADII, interval, neighs)) {
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)) {
458 collisions.insert(c);
465 if (!removed.empty()) {
488 for (
Size i = 0; i < r.size(); ++i) {
498 const SearchEnum opt,
502 if (opt == SearchEnum::FIND_LOWER_RANK) {
505 collision.finder->findLowerRank(i, 2._f *
radius, neighs);
508 if (searchRadii[i] > 0._f) {
509 collision.finder->findAll(i, 2._f * searchRadii[i], 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();
521 if (i == j || removed.
find(j) != removed.
end()) {
529 if (overlapValue >
sqr(overlap.allowedRatio)) {
530 if (overlap.handler->overlaps(i, j)) {
546 return closestCollision;
553 const Float dt)
const {
554 const Vector dr = r1 - r2;
555 const Vector dv = v1 - v2;
570 const Float root = (det > 1._f) ? 1._f + sqrtDet : 1._f - sqrtDet;
590 , scheduler(scheduler)
591 , threadData(scheduler) {
602 gravity->
build(scheduler, storage);
608 gravity->
evalAll(scheduler, dv, stats);
615 Float maxRadius = 0._f;
616 for (
Size i = 0; i < r.size(); ++i) {
617 maxRadius =
max(maxRadius, r[i][
H]);
620 auto functor = [
this, r, maxRadius, m, &v, &dv, &finder](
Size i, ThreadData& data) {
621 finder.
findAll(i, 2._f * maxRadius, data.neighs);
623 for (
auto& n : data.neighs) {
624 const Size j = n.index;
627 if (i == j || n.distanceSqr >=
sqr(hi + hj)) {
631 const Float hbar = 0.5_f * (hi + hj);
635 const Float radialForce = force.repel *
pow<6>((hi + hj) / dist);
637 f += unit * m[j] * dir * radialForce;
642 f -= unit * m[j] * velocityDir * force.friction;
651 parallelFor(scheduler, threadData, 0, r.size(), functor);
INLINE bool almostEqual(const AffineMatrix &m1, const AffineMatrix &m2, const Float eps=EPS)
INLINE bool isReal(const AntisymmetricTensor &t)
#define SPH_ASSERT(x,...)
#define SPH_ASSERT_UNEVAL(x,...)
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Simple gravity solver evaluating all particle pairs.
@ BOUNCE
Bounce/scatter collision, no merging and no fragmentation.
@ MERGER
Particles merged together.
@ NONE
No collision took place.
Looking for problems in SPH simulation and reporting potential errors.
uint32_t Size
Integral type used to index arrays (by default).
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Logging routines of the run.
#define VERBOSE_LOG
Helper macro, creating.
constexpr INLINE T max(const T &f1, const T &f2)
NAMESPACE_SPH_BEGIN constexpr INLINE T min(const T &f1, const T &f2)
Minimum & Maximum value.
INLINE Float root(const Float f)
constexpr INLINE T sqr(const T &f) noexcept
Return a squared value.
INLINE T sqrt(const T f)
Return a squared root of a value.
constexpr INLINE Float pow< 6 >(const Float v)
Solver performing N-body simulation.
#define NAMESPACE_SPH_END
const NothingType NOTHING
@ 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.
INLINE void parallelFor(IScheduler &scheduler, const Size from, const Size to, TFunctor &&functor)
Executes a functor concurrently from all available threads.
StaticArray< T0 &, sizeof...(TArgs)+1 > tie(T0 &t0, TArgs &... rest)
Creates a static array from a list of l-value references.
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.
INLINE Float getSqrLength(const Vector &v)
BasicVector< Float > Vector
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...
INLINE Vector getNormalized(const Vector &v)
int main(int argc, char *argv[])
static AffineMatrix rotateAxis(const Vector &axis, const Float angle)
bool isOrthogonal() const
INLINE TCounter size() const
INLINE Iterator< StorageType > begin()
INLINE Iterator< StorageType > end()
void resize(const TCounter newSize)
Resizes the array to new size.
void fill(const T &t)
Sets all elements of the array to given value.
void insert(TIter first, TIter last)
void insert(const CollisionRecord &col)
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.
Iterator< T > find(const T &value)
Solver computing gravitational interactions of hard-sphere particles.
HardSphereSolver(IScheduler &scheduler, const RunSettings &settings)
Creates the solver, using the gravity implementation specified by settings.
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.
~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.
Interface that allows unified implementation of sequential and parallelized versions of algorithms.
Object representing a 1D interval of real numbers.
INLINE Float lower() const
Returns lower bound of the interval.
INLINE Float upper() const
Returns upper bound of the interval.
INLINE Float size() const
Returns the size of the interval.
INLINE bool empty() const
Returns true if the interval is empty (default constructed).
Simple (forward) iterator over continuous array of objects of type T.
INLINE Type & value()
Returns the reference to the stored value.
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.
Solver computing gravitational interactions and repulsive forces between particles.
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.
Statistics & set(const StatisticsId idx, TValue &&value)
Sets new values of a statistic.
Container storing all quantities used within the simulations.
Outcome isValid(const Flags< ValidFlag > flags=ValidFlag::COMPLETE) const
Checks whether the storage is in valid state.
Quantity & insert(const QuantityId key, const OrderEnum order, const TValue &defaultValue)
Creates a quantity in the storage, given its key, value type and order.
StaticArray< Array< TValue > &, 3 > getAll(const QuantityId key)
Retrieves quantity buffers from the storage, given its key and value type.
Array< TValue > & getDt(const QuantityId key)
Retrieves a quantity derivative from the storage, given its key and value type.
void propagate(const Function< void(Storage &storage)> &functor)
Executes a given functor recursively for all dependent storages.
@ 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.
Array< TValue > & getD2t(const QuantityId key)
Retrieves a quantity second derivative from the storage, given its key and value type.
Array< TValue > & getValue(const QuantityId key)
Retrieves a quantity values from the storage, given its key and value type.
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.
Basic time-measuring tool. Starts automatically when constructed.
int64_t elapsed(const TimerUnit unit) const
Returns elapsed time in timer units. Does not reset the timer.
void restart()
Reset elapsed duration to zero.
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)
Provides a convenient way to construct objects from settings.
AutoPtr< IGravity > getGravity(const RunSettings &settings)
AutoPtr< ISymmetricFinder > getFinder(const RunSettings &settings)
AutoPtr< ICollisionHandler > getCollisionHandler(const RunSettings &settings)
AutoPtr< IOverlapHandler > getOverlapHandler(const RunSettings &settings)
INLINE SymmetricTensor sphereInertia(const Float m, const Float r)
Computes the inertia tensor of a homogeneous sphere.
Overload of std::swap for Sph::Array.
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
friend bool isReal(const CollisionRecord &col)
Size i
Indices of the collided particles.
Holds information about a neighbour particles.