1 #pragma once
2
3 /// \file Function.h
4 /// \brief Helper functions used by the code
5 /// \author Pavel Sevecek
6 /// \date 2016-2021
7
8 #include "math/rng/Rng.h"
9 #include "objects/geometry/SymmetricTensor.h"
10 #include "objects/wrappers/Flags.h"
11 #include "physics/Constants.h"
12
13 NAMESPACE_SPH_BEGIN
14
15 /// Contains analytic solutions of equations.
16 namespace Analytic {
17
18 /// \brief Properties of a homogeneous sphere in rest (no temporal derivatives)
19 class StaticSphere {
20 private:
21 /// Radius
22 Float r0;
23
24 /// Density
25 Float rho;
26
27 public:
StaticSphere(const Float r0,const Float rho)28 StaticSphere(const Float r0, const Float rho)
29 : r0(r0)
30 , rho(rho) {}
31
32 /// \brief Return the pressure at given radius r of a sphere self-compressed by gravity.
getPressure(const Float r)33 INLINE Float getPressure(const Float r) const {
34 if (r > r0) {
35 return 0._f;
36 }
37 return 2._f / 3._f * PI * Constants::gravity * sqr(rho) * (sqr(r0) - sqr(r));
38 }
39
40 /// \brief Returns the gravitational acceleration at given radius r.
41 ///
42 /// The acceleration increases linearily up to r0 and then decreases with r^2.
getAcceleration(const Vector & r)43 INLINE Vector getAcceleration(const Vector& r) const {
44 const Float l = getLength(r);
45 const Float l0 = min(r0, l);
46 return -Constants::gravity * rho * sphereVolume(l0) * r / pow<3>(l);
47 }
48
49 /// \brief Returns the gravitational potential energy of the sphere.
getEnergy()50 INLINE Float getEnergy() const {
51 return -16._f / 15._f * sqr(PI) * sqr(rho) * Constants::gravity * pow<5>(r0);
52 }
53 };
54
55 } // namespace Analytic
56
57 /// Physics of rigid body
58 namespace Rigid {
59
60 /// \brief Computes the inertia tensor of a homogeneous sphere.
sphereInertia(const Float m,const Float r)61 INLINE SymmetricTensor sphereInertia(const Float m, const Float r) {
62 return SymmetricTensor::identity() * (0.4_f * m * sqr(r));
63 }
64
65 /// \brief Computes the inertia tensor with respect to given point
66 ///
67 /// \param I Inertia tensor with respect to the center of mass
68 /// \param m Total mass of the body
69 /// \param a Translation vector with respect to the center of mass
parallelAxisTheorem(const SymmetricTensor & I,const Float m,const Vector & a)70 INLINE SymmetricTensor parallelAxisTheorem(const SymmetricTensor& I, const Float m, const Vector& a) {
71 return I + m * (SymmetricTensor::identity() * getSqrLength(a) - symmetricOuter(a, a));
72 }
73
74 } // namespace Rigid
75
76 /// \brief Computes the critical (break-up) angular velocity of a body.
77 ///
78 /// This value is only based on the ratio of the gravity and centrifugal force. It is a function of density
79 /// only, is does not depend on the radius of the body. Note that bodies rotating above the break-up limit
80 /// exist, especially smaller monolithic bodies with D ~ 100m!
81 /// \return Angular velocity (in rev/s).
getCriticalFrequency(const Float rho)82 INLINE Float getCriticalFrequency(const Float rho) {
83 return sqrt(sphereVolume(1._f) * rho * Constants::gravity);
84 }
85
86 /// \brief Returns the critical energy Q_D^* as a function of body diameter.
87 ///
88 /// The critical energy is a kinetic energy for which half of the target is dispersed into the fragments.
89 /// In other words, impact with critical energy will produce largest remnant (or fragment), mass of which
90 /// is 50% mass of the parent body, The relation follows the scaling law of Benz & Asphaug (1999).
91 ///
92 /// \todo replace D with units, do not enforce SI
93 Float evalBenzAsphaugScalingLaw(const Float D, const Float rho);
94
95 /// \brief Computes the impact energy Q from impact parameters.
96 ///
97 /// \param R Radius of the target
98 /// \param r Radius of the impactor
99 /// \param v Impact speed.
100 Float getImpactEnergy(const Float R, const Float r, const Float v);
101
102 /// \brief Returns the the ratio of the cross-sectional area of the impact and the total area of the
103 /// impactor.
104 ///
105 /// This value is lower than 1 only at high impact angles, where a part of the impactor misses the target
106 /// entirely (hit-and-run collision) and does not deliver its kinetic energy into the target. For the same
107 /// Q/Q_D, oblique impacts will therefore look 'artificially' weaker than the impacts and lower impacts
108 /// angle. Effective impact area can be used to 'correct' the impact energy, so that impacts at high
109 /// impact angles are comparable with head-on impats. See \ref Sevecek_2017 for details.
110 /// \param Q Relative impact energy (kinetic energy of the impactor divided by the impactor mass)
111 /// \param R Radius of the target
112 /// \param r Radius of the impactor
113 /// \param phi Impact angle in radians
114 Float getEffectiveImpactArea(const Float R, const Float r, const Float phi);
115
116 /// \brief Calculates the impactor radius to satisfy required impact parameters.
117 ///
118 /// The radius is computed so that the total relative impact energy is equal to the given value, assuming
119 /// Benz & Asphaug scaling law.
120 /// \param R_pb Radius of the parent body (target)
121 /// \param v_imp Impact velocity
122 /// \param QOverQ_D Ratio of the impact energy and the critical energy given by the scaling law
123 /// \param rho Density of the parent bod
124 Float getImpactorRadius(const Float R_pb, const Float v_imp, const Float QOverQ_D, const Float rho);
125
126 /// \brief Calculates the impactor radius to satisfy required impact parameters.
127 ///
128 /// The radius is computed so that the total relative effective impact energy is equal to the given value,
129 /// assuming Benz & Asphaug scaling law. The effective impact energy takes into account the projected impact
130 /// area, see \ref getEffectiveImpactArea.
131 /// \param R_pb Radius of the parent body (target)
132 /// \param v_imp Impact velocity
133 /// \param phi Impact angle
134 /// \param Q_effOverQ_D Ratio of the relative impact energy and the critical energy given by the scaling law
135 /// \param rho Density of the parent bod
136 Float getImpactorRadius(const Float R_pb,
137 const Float v_imp,
138 const Float phi,
139 const Float Q_effOverQ_D,
140 const Float rho);
141
142
143 class ImpactCone {
144 private:
145 AffineMatrix frame;
146 Float v_c;
147
148 UniformRng rng;
149
150 public:
ImpactCone(const AffineMatrix & frame,const Float cutoffVelocity)151 explicit ImpactCone(const AffineMatrix& frame, const Float cutoffVelocity)
152 : frame(frame)
153 , v_c(cutoffVelocity) {}
154
155 /// \brief Generates fragments at the impact point.
156 ///
157 /// Particles are added into provided buffers, keeping the existing content untouched.
158 /// \param m_tot Total mass of ejected fragments
159 /// \param N Total number of fragments.
160 /// \param r Output array of particle positions.
161 /// \param r Output array of particle velocities.
162 /// \param r Output array of particle masses.
getFragments(const Float m_tot,const Size N,Array<Vector> & r,Array<Vector> & v,Array<Float> & m)163 void getFragments(const Float m_tot, const Size N, Array<Vector>& r, Array<Vector>& v, Array<Float>& m) {
164 constexpr Float theta = PI / 4._f;
165 const Float m_frag = m_tot / N;
166 for (Size i = 0; i < N; ++i) {
167 const Float phi = 2._f * PI * rng();
168 /// \todo sample velocity from power law
169 v.push(frame * sphericalToCartesian(v_c, theta, phi));
170 r.push(frame.translation());
171 m.push(m_frag);
172 }
173 }
174 };
175
176 class CollisionMC {
177 private:
178 UniformRng rng;
179
180 public:
operator()181 Array<Float> operator()(const Float QoverQ_D, const Float M_tot, const Float m_min) {
182 const Float largest = max(M_LR(QoverQ_D, M_tot), M_LF(QoverQ_D, M_tot));
183 const Float exponent = q(QoverQ_D) + 1._f;
184
185 /// \todo
186 Array<Float> fragments;
187 fragments.push(largest);
188 Float m_partial = largest;
189 while (M_tot - m_partial > m_min) {
190 const Float m = pow(rng(), 1._f / exponent) - m_min; /// \todo fix
191 if (m + m_partial > M_tot) {
192 continue;
193 }
194 fragments.push(m);
195 m_partial += m;
196 }
197 return fragments;
198 }
199
200 private:
M_LR(const Float QoverQ_D,const Float M_tot)201 Float M_LR(const Float QoverQ_D, const Float M_tot) {
202 if (QoverQ_D < 1._f) {
203 return (-0.5_f * (QoverQ_D - 1._f) + 0.5_f) * M_tot;
204 } else {
205 return (-0.35_f * (QoverQ_D - 1._f) + 0.5_f) * M_tot;
206 }
207 }
208
M_LF(const Float QoverQ_D,const Float M_tot)209 Float M_LF(const Float QoverQ_D, const Float M_tot) {
210 return 8.e-3_f * (QoverQ_D * exp(-sqr(0.25_f * QoverQ_D))) * M_tot;
211 }
212
q(const Float QoverQ_D)213 Float q(const Float QoverQ_D) {
214 return -10._f + 7._f * pow(QoverQ_D, 0.4_f) * exp(-QoverQ_D / 7._f);
215 }
216 };
217
218 NAMESPACE_SPH_END
219