1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Radu Serban
13 // =============================================================================
14 //
15 // Utility functions to facilitate the creation of mixtures of granular material
16 // using different samplers for initial positions, geometry shapes, material
17 // properties, etc.
18 //
19 // =============================================================================
20 
21 #ifndef CH_UTILS_GENERATORS_H
22 #define CH_UTILS_GENERATORS_H
23 
24 #include <cmath>
25 #include <memory>
26 #include <random>
27 #include <string>
28 #include <utility>
29 #include <vector>
30 
31 #include "chrono/core/ChApiCE.h"
32 #include "chrono/core/ChQuaternion.h"
33 #include "chrono/core/ChVector.h"
34 
35 #include "chrono/physics/ChBody.h"
36 #include "chrono/physics/ChMaterialSurfaceNSC.h"
37 #include "chrono/physics/ChMaterialSurfaceSMC.h"
38 #include "chrono/physics/ChSystem.h"
39 #include "chrono/physics/ChSystemSMC.h"
40 
41 #include "chrono/utils/ChUtilsCreators.h"
42 #include "chrono/utils/ChUtilsGeometry.h"
43 #include "chrono/utils/ChUtilsInputOutput.h"
44 #include "chrono/utils/ChUtilsSamplers.h"
45 
46 namespace chrono {
47 namespace utils {
48 
49 /// @addtogroup chrono_utils
50 /// @{
51 
52 /// Enumeration of various geometric shapes available for mixtures.
53 enum class MixtureType { SPHERE, ELLIPSOID, BOX, CYLINDER, CONE, CAPSULE, BISPHERE, ROUNDEDCYLINDER };
54 
55 // Forward declarations
56 class Generator;
57 class MixtureIngredient;
58 
59 /// Encapsulation of an ingredient of one of the supported types in a mixture.
60 /// Such an object defines size, mass properties and material properties for, all of which can be constant for all
61 /// mixture components of this type or else obtained from associated truncated normal distributions. In addition, a
62 /// mixture ingredient defines the ratio of this particular type in the containing mixture.
63 class ChApi MixtureIngredient {
64   public:
65     MixtureIngredient(Generator* generator, MixtureType type, double ratio);
66     ~MixtureIngredient();
67 
68     void setDefaultMaterial(std::shared_ptr<ChMaterialSurface> mat);
69     void setDefaultDensity(double density);
70     void setDefaultSize(const ChVector<>& size);
71 
72     void setDistributionFriction(float friction_mean, float friction_stddev, float friction_min, float friction_max);
73     void setDistributionCohesion(float cohesion_mean, float cohesion_stddev, float cohesion_min, float cohesion_max);
74     void setDistributionYoung(float young_mean, float young_stddev, float young_min, float young_max);
75     void setDistributionPoisson(float poisson_mean, float poisson_stddev, float poisson_min, float poisson_max);
76     void setDistributionRestitution(float restitution_mean,
77                                     float restitution_stddev,
78                                     float restitution_min,
79                                     float restitution_max);
80     void setDistributionDensity(double density_mean, double density_stddev, double density_min, double density_max);
81     void setDistributionSize(double size_mean,
82                              double size_stddev,
83                              const ChVector<>& size_min,
84                              const ChVector<>& size_max);
85 
86     /// Class to be used as a callback interface for some user-defined action to be taken each
87     /// time the generator creates and adds a body based on this mixture ingredient to the system.
88     class ChApi AddBodyCallback {
89       public:
~AddBodyCallback()90         virtual ~AddBodyCallback() {}
91 
92         /// Callback used to process bodies as they are created and added to the system.
93         virtual void OnAddBody(std::shared_ptr<ChBody> body) = 0;
94     };
95 
96     /// Specify a callback object to be used each time a body is generated using this
97     /// mixture ingredient specification.
RegisterAddBodyCallback(std::shared_ptr<AddBodyCallback> callback)98     void RegisterAddBodyCallback(std::shared_ptr<AddBodyCallback> callback) { add_body_callback = callback; }
99 
100   private:
101     void freeMaterialDist();
102     ChVector<> getSize();
103     double getDensity();
104     void calcGeometricProps(const ChVector<>& size, double& volume, ChVector<>& gyration);
105     double calcMinSeparation();
106 
107     void setMaterialProperties(std::shared_ptr<ChMaterialSurfaceNSC> mat);
108     void setMaterialProperties(std::shared_ptr<ChMaterialSurfaceSMC> mat);
109 
110     Generator* m_generator;
111 
112     MixtureType m_type;
113     double m_ratio;
114     double m_cumRatio;
115 
116     std::shared_ptr<ChMaterialSurfaceNSC> m_defMaterialNSC;
117     std::shared_ptr<ChMaterialSurfaceSMC> m_defMaterialSMC;
118 
119     float m_minFriction, m_maxFriction;
120     float m_minCohesion, m_maxCohesion;
121     float m_minYoung, m_maxYoung;
122     float m_minPoisson, m_maxPoisson;
123     float m_minRestitution, m_maxRestitution;
124     std::normal_distribution<float>* m_frictionDist;
125     std::normal_distribution<float>* m_cohesionDist;
126     std::normal_distribution<float>* m_youngDist;
127     std::normal_distribution<float>* m_poissonDist;
128     std::normal_distribution<float>* m_restitutionDist;
129 
130     double m_defDensity;
131     double m_minDensity, m_maxDensity;
132     std::normal_distribution<>* m_densityDist;
133 
134     ChVector<> m_defSize;
135     ChVector<> m_minSize, m_maxSize;
136     std::normal_distribution<>* m_sizeDist;
137 
138     std::shared_ptr<AddBodyCallback> add_body_callback;
139 
140     friend class Generator;
141 };
142 
143 /// Provides functionality for generating sets of bodies with positions drawn from a specified sampler and various
144 /// mixture properties. Bodies can be generated in different bounding volumes (boxes or cylinders) which can be
145 /// degenerate (to a rectangle or circle, repsectively).
146 class ChApi Generator {
147   public:
148     typedef Types<double>::PointVector PointVector;
149 
150     Generator(ChSystem* system);
151     ~Generator();
152 
153     /// Add a new mixture ingredient of the specified type and in the given ratio (note that the ratios are normalized
154     /// before creating bodies).
155     std::shared_ptr<MixtureIngredient> AddMixtureIngredient(MixtureType type, double ratio);
156 
157     /// Get/Set the identifier that will be assigned to the next body.
158     /// Identifiers are incremented for successively created bodies.
getBodyIdentifier()159     int getBodyIdentifier() const { return m_crtBodyId; }
setBodyIdentifier(int id)160     void setBodyIdentifier(int id) { m_crtBodyId = id; }
161 
162     /// Create bodies, according to the current mixture setup, with initial positions given by the specified sampler in
163     /// the box domain specified by 'pos' and 'hdims'. Optionally, a constant initial linear velocity can be set for all
164     /// created bodies.
165     void CreateObjectsBox(Sampler<double>& sampler,
166                           const ChVector<>& pos,
167                           const ChVector<>& hdims,
168                           const ChVector<>& vel = ChVector<>(0, 0, 0));
169 
170     /// Create bodies, according to the current mixture setup, with initial positions on a uniform grid with given
171     /// separations (in x,y,z directions) in the box domain specified by 'pos' and 'hdims'. Optionally, a constant
172     /// initial linear velocity can be set for all created bodies.
173     void CreateObjectsBox(const ChVector<>& dist,
174                           const ChVector<>& pos,
175                           const ChVector<>& hdims,
176                           const ChVector<>& vel = ChVector<>(0, 0, 0));
177 
178     /// Create bodies, according to the current mixture setup, with initial positions given by the specified sampler in
179     /// the X-aligned cylinder domain specified by 'pos', 'radius' and 'halfHeight'. Optionally, a constant initial
180     /// linear velocity can be set for all created bodies.
181     void CreateObjectsCylinderX(Sampler<double>& sampler,
182                                 const ChVector<>& pos,
183                                 float radius,
184                                 float halfHeight,
185                                 const ChVector<>& vel = ChVector<>(0, 0, 0));
186 
187     /// Create bodies, according to the current mixture setup, with initial positions given by the specified sampler in
188     /// the Y-aligned cylinder domain specified by 'pos', 'radius' and 'halfHeight'. Optionally, a constant initial
189     /// linear velocity can be set for all created bodies.
190     void CreateObjectsCylinderY(Sampler<double>& sampler,
191                                 const ChVector<>& pos,
192                                 float radius,
193                                 float halfHeight,
194                                 const ChVector<>& vel = ChVector<>(0, 0, 0));
195 
196     /// Create bodies, according to the current mixture setup, with initial positions given by the specified sampler in
197     /// the Z-aligned cylinder domain specified by 'pos', 'radius' and 'halfHeight'. Optionally, a constant initial
198     /// linear velocity can be set for all created bodies.
199     void CreateObjectsCylinderZ(Sampler<double>& sampler,
200                                 const ChVector<>& pos,
201                                 float radius,
202                                 float halfHeight,
203                                 const ChVector<>& vel = ChVector<>(0, 0, 0));
204 
205     /// Create bodies, according to the current mixture setup, with initial positions given by the specified sampler in
206     /// the spherical domain specified by 'pos' and 'radius'. Optionally, a constant initial linear velocity can be set
207     /// for all created bodies.
208     void CreateObjectsSphere(Sampler<double>& sampler,
209                              const ChVector<>& pos,
210                              float radius,
211                              const ChVector<>& vel = ChVector<>(0, 0, 0));
212 
213     /// Class to be used as a callback interface for user-defined filtering of initial positions.
214     class ChApi CreateObjectsCallback {
215       public:
~CreateObjectsCallback()216         virtual ~CreateObjectsCallback() {}
217 
218         /// Callback used to process the initial position points generated by the underlying sampler.
219         /// The provided vector of boolean flags is set to all 'true'. Set the i-th entry to 'false'
220         /// if a body should not be created at the i-th position.
221         virtual void OnCreateObjects(
222             const PointVectorD& points,  ///< vector of positions generated by the sampler
223             std::vector<bool>& flags     ///< change to 'false' for positions where a body should not be generated
224             ) = 0;
225     };
226 
227     /// Specify a callback object to be used before creating the bodies. The OnCreateObjects() method of the
228     /// provided callback object will be called before object creation, allowing the user to filter the points
229     /// at which bodies will be initialized.
RegisterCreateObjectsCallback(std::shared_ptr<CreateObjectsCallback> callback)230     void RegisterCreateObjectsCallback(std::shared_ptr<CreateObjectsCallback> callback) { m_callback = callback; }
231 
232     /// Write information about the bodies created so far to the specified file (CSV format).
233     void writeObjectInfo(const std::string& filename);
234 
getTotalNumBodies()235     unsigned int getTotalNumBodies() const { return m_totalNumBodies; }
getTotalMass()236     double getTotalMass() const { return m_totalMass; }
getTotalVolume()237     double getTotalVolume() const { return m_totalVolume; }
238 
239   private:
240     struct BodyInfo {
BodyInfoBodyInfo241         BodyInfo(MixtureType t, double density, const ChVector<>& size, const std::shared_ptr<ChBody>& b)
242             : m_type(t), m_density(density), m_size(size), m_body(b) {}
243         MixtureType m_type;
244         double m_density;
245         ChVector<> m_size;
246         std::shared_ptr<ChBody> m_body;
247     };
248 
249     void normalizeMixture();
250     int selectIngredient();
251     double calcMinSeparation(double sep);
252     ChVector<> calcMinSeparation(const ChVector<>& sep);
253     void createObjects(const PointVector& points, const ChVector<>& vel);
254 
255     ChSystem* m_system;
256 
257     std::uniform_real_distribution<> m_mixDist;
258 
259     std::vector<std::shared_ptr<MixtureIngredient>> m_mixture;
260     std::vector<BodyInfo> m_bodies;
261     unsigned int m_totalNumBodies;
262     double m_totalMass;
263     double m_totalVolume;
264 
265     std::shared_ptr<CreateObjectsCallback> m_callback;
266 
267     int m_crtBodyId;
268 
269     friend class MixtureIngredient;
270 };
271 
272 /// @} chrono_utils
273 
274 }  // end namespace utils
275 }  // end namespace chrono
276 
277 #endif
278