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