Free cooling granular system (3D)

Model/Simulation:

The homogeneous and inhomogeneous regimes of a free cooling granular system (3D) are simulated. Just like the Free cooling granular system (2D), particles are initially randomized with a large kinetic energy and collisions occur dissipatively. This model is common in the study of the kinetic theory of granular gases [1-3].

Below, a short ParaView animation of the 3D free cooling demo is shown.


Simulation set up:

In the animation, the simulation volume consists of a box with equal side length and periodic boundary conditions in three dimensions (3D). Just like the 2D demo case (Free cooling granular system (2D)), the initial state with random particle positions and velocities is prepared in the following way:

  1. The particles first sit on a regular lattice and get a random velocity with a total momentum of zero.
  2. Then the simulation is started without dissipation and runs for a reasonable number of collisions per particle so that the system becomes homogeneous, "and the velocity distribution approaches a Maxwellian."
  3. The above state is then used as the initial configuration for the dissipative regimes.

The FreeCooling3DDemo class inherits from the Mercury3D class.

[FCD_3D:headers]
Definition: FreeCooling3DDemo.cpp:22
This adds on the hierarchical grid code for 3D problems.
Definition: Mercury3D.h:16

Thus, the following headers are included:

Data members of the class:\n

The FreeCooling3DDemo class has, but not limited to two (public) data members, namely a pointer to the species and the number of particles:

unsigned int N;
@ N
Definition: constructor.cpp:22

The key components of the class are explained in-turn, in the following:

step 1: Define Walls/Periodic Boundaries
For experimental purposes, particles can be contained by a three dimensional box made up of six infinite walls. If this is required, then please see the Free cooling granular system (3D) in walls. Otherwise, the simulation volume is replace by periodic boundaries.

pb.set(Vec3D(1,0,0), getXMin(), getXMax());
boundaryHandler.copyAndAddObject(pb);
pb.set(Vec3D(0,1,0), getYMin(), getYMax());
boundaryHandler.copyAndAddObject(pb);
pb.set(Vec3D(0,0,1), getZMin(), getZMax());
boundaryHandler.copyAndAddObject(pb);
Defines a pair of periodic walls. Inherits from BaseBoundary.
Definition: PeriodicBoundary.h:20
Definition: Kernel/Math/Vector.h:30
const char const int const int const RealScalar const RealScalar const int const RealScalar * pb
Definition: level2_impl.h:28

step 2: Create Particles
Next, the particle species is defined. The particles in this problem use a linear visco-elastic (normal) contact model. The dissipation and stiffness defining the contact model can be set in different ways. In this example these contact model parameters are defined.

species.setDensity(2e3);
species.setDissipation(0.0);
species.setStiffness(1e3);
problem.FC3D_Species = species;
problem.speciesHandler.copyAndAddObject(species);
void setDissipation(Mdouble dissipation)
Allows the normal dissipation to be changed.
Definition: LinearViscoelasticNormalSpecies.cc:96
void setStiffness(Mdouble new_k)
Allows the spring constant to be changed.
Definition: LinearViscoelasticNormalSpecies.cc:72
void setDensity(Mdouble density)
Definition: ParticleSpecies.cc:88
Constructor for SteadyAxisymAdvectionDiffusion problem
Definition: steady_axisym_advection_diffusion.cc:213

The particle properties are set subsequently. The particleHandler is cleared just to be sure it is empty, then the particle to be copied into the container is created and the set species is assigned to it.

particleHandler.clear();
p0.setSpecies(speciesHandler.getObject(0));
Vector3f p0
Definition: MatrixBase_all.cpp:2
A spherical particle is the most simple particle used in MercuryDPM.
Definition: SphericalParticle.h:16

step 3: Place Particles
After specifying the particle properties, the container is filled with copies of the particle. In this example, particles are placed in a lattice grid pattern, on evenly spaced positions.

for (unsigned int i = 0; i < N; ++i)
{
const unsigned int ix = (i % N1);
const unsigned int iz = static_cast<unsigned int>( static_cast<double>(i) / N1 / N1);
const unsigned int iy = (i - ix - N1 * N1 * iz) / N1;
// set particle position
const double x = (getXMax() - getXMin()) * (ix + 1) / (N1 + 1);
const double y = (getYMax() - getYMin()) * (iy + 1) / (N1 + 1);
const double z = (getZMax() - getZMin()) * (iz + 1) / (N1 + 1);
p0.setPosition(Vec3D(x, y, z));
// set random velocities for the particle
p0.setVelocity(Vec3D(random.getRandomNumber(-2.0,2.0), random.getRandomNumber(-2.0,2.0), random.getRandomNumber(-2.0,2.0)));
p0.setRadius(0.0025);
particleHandler.copyAndAddObject(p0);
}
int i
Definition: BiCGSTAB_step_by_step.cpp:9
Scalar * y
Definition: level1_cplx_impl.h:128
list x
Definition: plotDoE.py:28

step 4: Centre of mass velocity
Next, the center of mass velocity is subtracted to ensure a reduced random velocity. This results into a center of mass velocity nearly equal to zero.

// Compute the center of mass velocity
double particle_mass = p0.getMass();
double M_b = N*particle_mass; // mass of the bulk system
Vec3D V_com = {0,0,0};
for (int k = 0; k < particleHandler.getNumberOfObjects() ; k++){
BaseParticle* p = particleHandler.getObject(k);
V_com += (particle_mass*p->getVelocity())/M_b;
}
// Compute the reduced velocity for each particle
for (int k = 0; k < particleHandler.getNumberOfObjects() ; k++){
BaseParticle* p = particleHandler.getObject(k);
p->setVelocity(p->getVelocity() - V_com);
}
float * p
Definition: Tutorial_Map_using.cpp:9
Definition: BaseParticle.h:33
char char char int int * k
Definition: level2_impl.h:374

Actions After TimeStep:
The actionsAfterTimeStep() method specifies all actions that need to be performed in between time steps, i.e. Since, the simulation started with zero dissipation, after a reasonable number of collisions per particle, the system will be homogeneous, and the actionsAfterTimeStep() is used to set the initial configuration for the "next" dissipative regime.

void actionsAfterTimeStep() override{
if (getTime() > 4e5*getTimeStep()) {
FC3D_Species.setDissipation(0.232);
}
}

Main Function

In the main program, the FreeCooling3DDemo object is created, after which some of its basic properties are set: like, the number of particles, box dimensions, time step and saving parameters. Lastly, the problem is actually solved by calling its solve() method.

int main(int argc UNUSED, char* argv[] UNUSED)
{
// Problem setup
species.setDensity(2e3);
species.setDissipation(0.0);
species.setStiffness(1e3);
problem.FC3D_Species = species;
problem.speciesHandler.copyAndAddObject(species);
problem.N = 1000;
problem.setName("FreeCooling3DDemo");
problem.setGravity(Vec3D(0.0, 0.0, 0.0));
problem.setTimeStep(5e-5);
problem.setSaveCount(4000);
problem.setTimeMax(50.0);
problem.setMax(0.064,0.064,0.064);
problem.setHGridMaxLevels(1);
problem.setHGridCellOverSizeRatio(1.2);
problem.setHGridUpdateEachTimeStep(false);
problem.setParticlesWriteVTK(true);
problem.solve();
}
Array< double, 1, 3 > e(1./3., 0.5, 2.)
@ ONE_FILE
all data will be written into/ read from a single file called name_
#define UNUSED
Definition: GeneralDefine.h:18
int main(int argc, char *argv[])
Definition: T_protectiveWall.cpp:194

References:

  1. Luding, S. (2005). Structure and cluster formation in granular media. Pramana, 64(6), 893-902.
  2. Brilliantov, N. V., & Pöschel, T. (2010). Kinetic theory of granular gases. Oxford University Press.
  3. Pöschel, T., & Luding, S. (Eds.). (2001). Granular gases (Vol. 564). Springer Science & Business Media

(Return to Overview of advanced tutorials)