SPH
EquilibriumSolver.cpp
Go to the documentation of this file.
2 #include "gravity/IGravity.h"
4 #include "physics/Eos.h"
5 #include "sph/Materials.h"
8 #include "sph/kernel/Kernel.h"
9 #include "system/Factory.h"
10 
12 
13 #ifdef SPH_USE_EIGEN
14 
16  const RunSettings& settings,
18  const Size boundaryThreshold)
19  : scheduler(scheduler)
20  , gravity(std::move(gravity))
21  , boundaryThreshold(boundaryThreshold) {
22  kernel = Factory::getKernel<3>(settings);
23  finder = Factory::getFinder(settings);
24 }
25 
27 
29  // compute gravity to use as right-hand side
30  gravity->build(scheduler, storage);
31  ArrayView<Vector> r, v, dv;
32  tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION);
33  gravity->evalAll(scheduler, dv, stats);
34 
36  // add centrifugal force
37  for (Size i = 0; i < r.size(); ++i) {
38  const Float R = sqrt(sqr(r[i][X]) + sqr(r[i][Y]));
39  dv[i] += getSqrLength(v[i]) / R * Vector(r[i][X], r[i][Y], 0._f) / R;
40  }
41 
42  finder->build(scheduler, r);
43  Float maxRadius = 0._f;
44  for (Size i = 0; i < r.size(); ++i) {
45  maxRadius = max(maxRadius, r[i][H]);
46  }
47 
49 
50 #if 0
51  SparseMatrix matrix(3 * r.size(), r.size());
52  Array<Float> b(3 * r.size());
54  Float lambda = 0._f;
55  for (Size i = 0; i < r.size(); ++i) {
56  neighs.clear();
57  finder->findAll(i, maxRadius * kernel.radius(), neighs);
58  /*if (neighs.size() < boundaryThreshold) {
59  // boundary -> dirichlet
60  for (int k = 0; k < 3; ++k) {
61  matrix.insert(3 * i + k, i, 1);
62  b[3 * i + k] = 0;
63  }
64  continue;
65  }*/
66  const bool isBoundary = neighs.size() < boundaryThreshold;
67 
68  Vector Aii(0._f);
69  Float weightSum = 0;
70  for (const NeighbourRecord& n : neighs) {
71  const Size j = n.index;
72  const Float hbar = 0.5_f * (r[i][H] + r[j][H]);
73  SPH_ASSERT(hbar > EPS, hbar);
74  if (i == j || n.distanceSqr >= sqr(kernel.radius() * hbar)) {
75  // aren't actual neighbours
76  continue;
77  }
78  const Vector grad = kernel.grad(r[i], r[j]);
79  const Float weight = m[i] / (r[i][H] * sqr(rho[i])) * kernel.value(r[i], r[j]);
80  Aii += m[j] * grad / sqr(rho[i]);
81  const Vector Aij = m[j] * grad / sqr(rho[j]);
82  if (!isBoundary) {
83  for (int k = 0; k < 3; ++k) {
84  matrix.insert(3 * i + k, j, Aij[k] - lambda * weight);
85  }
86  } else {
87  for (int k = 0; k < 3; ++k) {
88  matrix.insert(3 * i + k, j, -lambda * weight);
89  }
90  }
91  weightSum += weight;
92  }
93  // add diagonal element and right-hand side
94  if (!isBoundary) {
95  for (int k = 0; k < 3; ++k) {
96  matrix.insert(3 * i + k, i, Aii[k] + weightSum * lambda);
97  b[3 * i + k] = dv[i][k];
98  }
99  } else {
100  for (int k = 0; k < 3; ++k) {
101  matrix.insert(3 * i + k, i, 1.f + weightSum * lambda);
102  b[3 * i + k] = 0._f;
103  }
104  }
105  }
106  Expected<Array<Float>> X = matrix.solve(b, SparseMatrix::Solver::LSCG, 1.e-8f);
107  if (!X) {
108  return X.error();
109  }
110 
111 #else
112 
113  SparseMatrix matrix(r.size(), r.size());
114  Array<Float> b(r.size());
115  Array<NeighbourRecord> neighs;
116 
117  for (Size i = 0; i < r.size(); ++i) {
118  neighs.clear();
119  finder->findAll(i, maxRadius * kernel.radius(), neighs);
120 
121  Float Aii(0._f);
122  Float divDv = 0._f;
123  for (const NeighbourRecord& n : neighs) {
124  const Size j = n.index;
125  const Float hbar = 0.5_f * (r[i][H] + r[j][H]);
126  SPH_ASSERT(hbar > EPS, hbar);
127  if (i == j || n.distanceSqr >= sqr(kernel.radius() * hbar)) {
128  // aren't actual neighbours
129  continue;
130  }
131  const Vector grad = kernel.grad(r[i], r[j]);
132  const Float lapl = laplacian(1._f, grad, r[i] - r[j]);
133  Aii -= m[j] * lapl / sqr(rho[i]);
134  const Float Aij = m[j] * lapl / sqr(rho[j]);
135  matrix.insert(i, j, Aij);
136 
137  divDv -= m[j] / rho[j] * dot(dv[j] - dv[i], grad);
138  }
139 
140  /*if (neighs.size() < boundaryThreshold) {
141  for (const NeighbourRecord& n1 : neighs) {
142  const Size j1 = n1.index;
143  const Vector mirrorR = 2._f * r[i] - r[j1];
144  bool mirrorFound = false;
145  for (const NeighbourRecord& n2 : neighs) {
146  const Size j2 = n2.index;
147  if (getSqrLength(r[j2] - mirrorR) < 0.25_f * r[j2][H]) {
148  mirrorFound = true;
149  break;
150  }
151  }
152  if (!mirrorFound) {
153  const Vector grad = kernel.grad(r[i], mirrorR);
154  const Float lapl = laplacian(1._f, grad, r[i] - mirrorR);
155  Aii -= m[j1] * lapl / sqr(rho[i]);
156  }
157  }
158  }*/
159 
160 
161  if (neighs.size() < boundaryThreshold) {
162  // boundary -> dirichlet
163  Aii += 100._f * m[i] / sqr(rho[i]) * kernel.value(r[i], r[i]) / sqr(r[i][H]);
164  }
165 
166  // add diagonal element and right-hand side
167  matrix.insert(i, i, Aii);
168  b[i] = divDv;
169  }
171  if (!X) {
172  return makeFailed(X.error());
173  }
174 #endif
175 
178  for (Size matId = 0; matId < storage.getMaterialCnt(); matId++) {
179  MaterialView mat = storage.getMaterial(matId);
180  EosMaterial& eosMat = dynamic_cast<EosMaterial&>(mat.material());
181  const IEos& eos = eosMat.getEos();
182  for (Size i : mat.sequence()) {
183  p[i] = X.value()[i];
184  u[i] = eos.getInternalEnergy(rho[i], p[i]);
185  }
186  }
187 
188  return SUCCESS;
189 }
190 
191 #endif
192 
194 class DisplacementGradient : public DerivativeTemplate<DisplacementGradient> {
195 private:
197  ArrayView<const Float> m, rho;
200 
201  Float lambda, mu;
202 
203 public:
206 
210  }
211 
212  INLINE void additionalInitialize(const Storage& input, Accumulated& results) {
215 
218 
220  IMaterial& material = input.getMaterial(0);
221  lambda = material.getParam<Float>(BodySettingsId::ELASTIC_MODULUS);
222  mu = material.getParam<Float>(BodySettingsId::SHEAR_MODULUS);
223  }
224 
226  return true;
227  }
228 
229  template <bool Symmetrize>
230  INLINE void eval(const Size i, const Size j, const Vector& grad) {
232  const SymmetricTensor epsilon = symmetricOuter(u[j] - u[i], grad);
233  const SymmetricTensor sigma =
234  lambda * epsilon.trace() * SymmetricTensor::identity() + 2._f * mu * epsilon;
235  const Float tr3 = sigma.trace() / 3._f;
236  TracelessTensor ds(sigma - tr3 * SymmetricTensor::identity());
237  p[i] += m[j] / rho[j] * tr3;
238  s[i] += m[j] / rho[j] * ds;
239  if (Symmetrize) {
240  p[j] += m[i] / rho[i] * tr3;
241  s[j] += m[i] / rho[i] * ds;
242  }
243  }
244 };
245 
247 public:
248  virtual void setDerivatives(DerivativeHolder& derivatives, const RunSettings& settings) override {
249  derivatives.require(makeAuto<DisplacementGradient>(settings));
250  }
251 
252  virtual void initialize(IScheduler& UNUSED(scheduler),
253  Storage& UNUSED(storage),
254  const Float UNUSED(t)) override {}
255 
256  virtual void finalize(IScheduler& UNUSED(scheduler),
257  Storage& UNUSED(storage),
258  const Float UNUSED(t)) override {}
259 
260  virtual void create(Storage& storage, IMaterial& UNUSED(material)) const override {
262  storage.insert<TracelessTensor>(
265  }
266 };
267 
268 #ifdef SPH_USE_EIGEN
269 
270 static EquationHolder getEquations(const EquationHolder& additional) {
271  return additional + makeTerm<DisplacementTerm>() + makeTerm<ConstSmoothingLength>();
272 }
273 
275  const RunSettings& settings,
276  const EquationHolder& equations)
277  : scheduler(scheduler)
278  , equationSolver(scheduler, settings, getEquations(equations)) {
279  kernel = Factory::getKernel<3>(settings);
280  finder = Factory::getFinder(settings);
281  boundaryThreshold = 18;
282  // settings.get<int>(RunSettingsId::BOUNDARY_THRESHOLD);
283 }
284 
286 
289 
290  // build the neighbour finding structure
291  finder->build(scheduler, r);
292 
293  // compute right-hand side of equations by solving equations for acceleration
294  storage.zeroHighestDerivatives();
295  equationSolver.integrate(storage, stats);
296 
297  ArrayView<Float> m, rho;
300  Array<Float> b(dv.size() * 3);
301  Float b_avg = 0._f;
302 
303  // get number of neighbours for boundary detection
305 
306  for (Size i = 0; i < dv.size(); ++i) {
307  for (Size j = 0; j < 3; ++j) {
308  const Float x = -rho[i] * dv[i][j];
309  b[3 * i + j] = x;
310  b_avg += abs(x);
311  }
312  }
313  b_avg /= dv.size() * 3;
314 
315 
316  // The equation we are trying to solve is:
317  // (\lambda + \mu) \nabla (\nabla \cdot u) + \mu \nabla^2 u + f = 0
318  // where \lambda, \mu are Lame's coefficient, u is the displacement vector and f is the external force
319 
320  SPH_ASSERT(storage.getMaterialCnt() == 1);
321  IMaterial& material = storage.getMaterial(0);
322  const Float lambda = material.getParam<Float>(BodySettingsId::ELASTIC_MODULUS);
323  const Float mu = material.getParam<Float>(BodySettingsId::SHEAR_MODULUS);
324 
325  // fill the matrix with values
326  Array<NeighbourRecord> neighs;
327  matrix.resize(r.size() * 3, r.size() * 3);
328  for (Size i = 0; i < r.size(); ++i) {
329  finder->findLowerRank(i, kernel.radius() * r[i][H], neighs);
330 
331  for (Size k = 0; k < neighs.size(); ++k) {
332  const Size j = neighs[k].index;
333  const Vector grad = kernel.grad(r[i], r[j]);
334  const Vector dr = r[i] - r[j];
335  const Float f = dot(dr, grad) / getSqrLength(dr);
336  const Vector dr0 = getNormalized(dr);
337  SPH_ASSERT(isReal(f));
338  SymmetricTensor lhs = -5._f * (lambda + mu) * symmetricOuter(dr0, dr0) +
339  (lambda - mu) * SymmetricTensor::identity();
340  SPH_ASSERT(isReal(lhs));
341 
342  SymmetricTensor mij = m[j] / rho[j] * lhs * f;
343  SymmetricTensor mji = m[i] / rho[i] * lhs * f;
344  for (Size a = 0; a < 3; ++a) {
345  for (Size b = 0; b < 3; ++b) {
346  matrix.insert(3 * i + a, 3 * i + b, mij(a, b));
347  matrix.insert(3 * i + a, 3 * j + b, -mij(a, b));
348  matrix.insert(3 * j + a, 3 * j + b, mji(a, b));
349  matrix.insert(3 * j + a, 3 * i + b, -mji(a, b));
350  /*if (neighCnts[i] < boundaryThreshold) {
351  matrix.insert(3 * j + a, 3 * i + b, b_avg * LARGE);
352  matrix.insert(3 * j + a, 3 * i + b, b_avg * LARGE);
353  }
354  if (neighCnts[j] < boundaryThreshold) {
355  matrix.insert(3 * j + a, 3 * j + b, b_avg * LARGE);
356  matrix.insert(3 * i + a, 3 * j + b, b_avg * LARGE);
357  }*/
358  }
359  }
360  }
361  }
362 
363  // solve the system of equation for displacement
365  if (!a) {
366  // somehow cannot solve the system of equations, report the error
367  return makeFailed(a.error());
368  }
369 
370  // fill the displacement array with computed values
372  for (Size i = 0; i < u.size(); ++i) {
373  if (neighCnts[i] < boundaryThreshold) {
374  u[i] = Vector(0._f);
375  } else {
376  for (Size j = 0; j < 3; ++j) {
377  u[i][j] = a.value()[3 * i + j];
378  }
379  }
380  }
381 
382  // compute pressure and deviatoric stress from displacement
383  equationSolver.integrate(storage, stats);
384 
385  // compute internal energy based on pressure (pressure is computed every time step using the equation
386  // of state, so our computed values would be overriden)
387  ArrayView<Float> p, e;
389  for (Size matId = 0; matId < storage.getMaterialCnt(); ++matId) {
390  MaterialView mat = storage.getMaterial(matId);
391  EosMaterial& eosMat = dynamic_cast<EosMaterial&>(mat.material());
392  for (Size i : mat.sequence()) {
393  e[i] = eosMat.getEos().getInternalEnergy(rho[i], p[i]);
394  SPH_ASSERT(isReal(e[i])); // && e[i] >= 0._f, e[i]);
395  }
396  }
397  return SUCCESS;
398 }
399 
400 void EquilibriumStressSolver::create(Storage& storage, IMaterial& material) {
401  SPH_ASSERT(storage.getMaterialCnt() == 1);
402  equationSolver.create(storage, material);
403 }
404 
405 #endif
406 
@ UNIQUE
Only a single derivative accumulates to this buffer.
INLINE bool isReal(const AntisymmetricTensor &t)
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
Equations of state.
Right-hand side term of SPH equations.
Computes quantities to get equilibrium state.
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
Base class for solvers of gravity.
SPH kernels.
INLINE T laplacian(const T &value, const Vector &grad, const Vector &dr)
SPH approximation of laplacian, computed from a kernel gradient.
Definition: Kernel.h:519
INLINE Float weight(const Vector &r1, const Vector &r2)
SPH-specific implementation of particle materials.
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
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
INLINE auto abs(const T &f)
Definition: MathUtils.h:276
#define INLINE
Macros for conditional compilation based on selected compiler.
Definition: Object.h:31
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
@ DISPLACEMENT
Displacement vector, a solution of the stress analysis.
@ DEVIATORIC_STRESS
Deviatoric stress tensor, always a traceless tensor.
@ PRESSURE
Pressure, affected by yielding and fragmentation model, always a scalar quantity.
@ POSITION
Positions (velocities, accelerations) of particles, always a vector quantity,.
@ ENERGY
Specific internal energy, always a scalar quantity.
@ DENSITY
Density, always a scalar quantity.
@ MASS
Paricles masses, always a scalar quantity.
@ NEIGHBOUR_CNT
Number of neighbouring particles (in radius h * kernel.radius)
@ ZERO
Quantity without derivatives, or "zero order" of quantity.
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
INLINE SymmetricTensor symmetricOuter(const Vector &v1, const Vector &v2)
SYMMETRIZED outer product of two vectors.
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
@ Y
Definition: Vector.h:23
@ X
Definition: Vector.h:22
Storage for accumulating derivatives.
Definition: Accumulated.h:30
Array< TValue > & getBuffer(const QuantityId id, const OrderEnum order)
Returns the buffer of given quantity and given order.
Definition: Accumulated.cpp:53
void insert(const QuantityId id, const OrderEnum order, const BufferSource source)
Creates a new storage with given ID.
Object providing safe access to continuous memory of data.
Definition: ArrayView.h:17
INLINE TCounter size() const
Definition: ArrayView.h:101
void clear()
Removes all elements from the array, but does NOT release the memory.
Definition: Array.h:434
INLINE TCounter size() const noexcept
Definition: Array.h:193
Container holding derivatives and the storage they accumulate to.
Definition: Derivative.h:173
virtual void require(AutoPtr< IDerivative > &&derivative)
Adds derivative if not already present.
Definition: Derivative.cpp:77
Helper template for derivatives that define both the symmetrized and asymmetric variant.
Derivative computing components of stress tensor from known displacement vectors.
DisplacementGradient(const RunSettings &settings)
INLINE void additionalCreate(Accumulated &results)
INLINE void additionalInitialize(const Storage &input, Accumulated &results)
INLINE void eval(const Size i, const Size j, const Vector &grad)
INLINE bool additionalEquals(const DisplacementGradient &UNUSED(other)) const
virtual void initialize(IScheduler &UNUSED(scheduler), Storage &UNUSED(storage), const Float UNUSED(t)) override
virtual void finalize(IScheduler &UNUSED(scheduler), Storage &UNUSED(storage), const Float UNUSED(t)) override
virtual void setDerivatives(DerivativeHolder &derivatives, const RunSettings &settings) override
Sets derivatives required by this term.
virtual void create(Storage &storage, IMaterial &UNUSED(material)) const override
Material holding equation of state.
Definition: Materials.h:21
const IEos & getEos() const
Returns the equation of state.
Definition: Materials.cpp:26
Container holding equation terms.
Definition: EquationTerm.h:238
EquilibriumEnergySolver(IScheduler &scheduler, const RunSettings &settings, AutoPtr< IGravity > &&gravity, const Size boundaryThreshold)
Outcome solve(Storage &storage, Statistics &stats)
EquilibriumStressSolver(IScheduler &scheduler, const RunSettings &settings, const EquationHolder &equations)
Constructs the solver.
void create(Storage &storage, IMaterial &material)
Creates all the necessary quantities in the storage.
Outcome solve(Storage &storage, Statistics &stats)
Computed pressure and deviatoric stress are placed into the storage.
Wrapper of type that either contains a value of given type, or an error message.
Definition: Expected.h:25
const Error & error() const
Returns the error message.
Definition: Expected.h:94
Type & value()
Returns the reference to expected value.
Definition: Expected.h:69
void build(IScheduler &scheduler, ArrayView< const Vector > points)
Constructs the struct with an array of vectors.
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.
Base class for equations of state.
Definition: Eos.h:16
virtual Float getInternalEnergy(const Float rho, const Float p) const =0
Inverted function; computes specific internal energy u from given density rho and pressure p.
Represents a term or terms appearing in evolutionary equations.
Definition: EquationTerm.h:22
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
INLINE TValue getParam(const BodySettingsId paramIdx) const
Returns a parameter associated with given particle.
Definition: IMaterial.h:137
Interface that allows unified implementation of sequential and parallelized versions of algorithms.
Definition: Scheduler.h:27
void build(IScheduler &scheduler, ArrayView< const Vector > points, Flags< FinderFlag > flags=FinderFlag::MAKE_RANK)
Constructs the struct with an array of vectors.
virtual Size findLowerRank(const Size index, const Float radius, Array< NeighbourRecord > &neighbours) const =0
Finds all points within radius that have a lower rank in smoothing length.
Non-owning wrapper of a material and particles with this material.
Definition: IMaterial.h:30
INLINE IMaterial & material()
Returns the reference to the material of the particles.
Definition: IMaterial.h:43
INLINE IndexSequence sequence()
Returns iterable index sequence.
Definition: IMaterial.h:75
Sparse representation of matrix of arbitrary dimension.
Definition: SparseMatrix.h:16
void insert(const Size i, const Size j, const Float value)
Adds a values to given element of the matrix.
void resize(const Size rows, const Size cols)
Changes the size of the matrix, removing all previous entries.
Expected< Array< Float > > solve(const Array< Float > &values, const Solver solver, const Float tolerance=0.)
@ BICGSTAB
Stabilized bi-conjugate gradient method, can be used for any square matrix.
@ LSCG
Least-square conjugate gradient, can be used for any matrix.
Object holding various statistics about current run.
Definition: Statistics.h:22
Container storing all quantities used within the simulations.
Definition: Storage.h:230
Size getMaterialCnt() const
Return the number of materials in the storage.
Definition: Storage.cpp:437
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
auto getValues(const QuantityId first, const QuantityId second, const TArgs... others)
Retrieves an array of quantities from the key.
Definition: Storage.h:359
Array< TValue > & getD2t(const QuantityId key)
Retrieves a quantity second derivative from the storage, given its key and value type.
Definition: Storage.cpp:243
MaterialView getMaterial(const Size matIdx) const
Returns an object containing a reference to given material.
Definition: Storage.cpp:366
void zeroHighestDerivatives()
Sets all highest-level derivatives of quantities to zero.
Definition: Storage.cpp:556
Array< TValue > & getValue(const QuantityId key)
Retrieves a quantity values from the storage, given its key and value type.
Definition: Storage.cpp:191
virtual void create(Storage &storage, IMaterial &material) const override
Initializes all quantities needed by the solver in the storage.
virtual void integrate(Storage &storage, Statistics &stats) override
Computes derivatives of all time-dependent quantities.
Symmetric tensor of 2nd order.
INLINE Float trace() const
Return the trace of the tensor.
static INLINE SymmetricTensor identity()
Returns an identity tensor.
INLINE Float value(const Vector &r1, const Vector &r2) const
Definition: Kernel.h:573
INLINE Float radius() const
Definition: Kernel.h:583
INLINE Vector grad(const Vector &r1, const Vector &r2) const
Definition: Kernel.h:578
Symmetric traceless 2nd order tensor.
static INLINE TracelessTensor null()
Returns a tensor with all zeros.
Creating code components based on values from settings.
@ ELASTIC_MODULUS
Elastic modulus lambda (a.k.a Lame's first parameter) of the material.
@ SHEAR_MODULUS
Shear modulus mu (a.k.a Lame's second parameter) of the material.
constexpr Float gravity
Gravitational constant (CODATA 2014)
Definition: Constants.h:29
AutoPtr< ISymmetricFinder > getFinder(const RunSettings &settings)
Definition: Factory.cpp:156
Overload of std::swap for Sph::Array.
Definition: Array.h:578
Holds information about a neighbour particles.