EDEMpy Setting Functions Tutorial

Renan
Renan
Altair Employee
edited April 3 in Altair Exchange

Overview

EDEMpy is a Python library for post-processing and analyzing EDEM simulation data. It leverages the open-source hdf5 file format, which has been used to store EDEM data since EDEM 2017. With EDEMpy, extracting specific data from a simulation deck and processing it in a customizable and reusable manner becomes effortless. For instance, users can extract the force acting on a specific geometry over time, compare results across multiple simulation decks, track particles' residence time throughout a simulation, or calculate post-processing properties like the segregation index using EDEM data.

Since the release of version 1.1.0, users can now write data to the simulation without relying on the EDEM graphical user interface (GUI). This allows them to automate the process of creating an EDEM simulation and makes EDEMpy more powerful than ever before. 

In this tutorial, you will learn how to utilize the new setting functions in EDEMpy to construct a complete simulation from scratch. We will cover topics such as creating materials, setting material properties and interactions, adding particles, defining geometries, creating factories, and configuring model parameters.

 

1. Importing the necessary libraries and opening the deck

The first step is to import the libraries that we'll be using throughout this tutorial: edempy and numpy. The edempy library enables us to work with EDEM simulation data, while numpy provides useful numerical computing functionalities. Once we have imported the required libraries, we need to open our simulation deck in write mode, denoted by mode='w', which allows us to modify and write data to the deck.

from edempy import Deck import numpy as np  deck = Deck("Setting_Functions_Example.dem", mode='w')

 

2. Creating the bulk material

2.1 Setting the bulk material properties

The first step in our simulation is to create the bulk material. We can achieve this by utilizing the createMaterial function. To designate it as a bulk material, we need to set the last argument of the createMaterial function to True. In this case, we will name our bulk material as BulkMaterial, and set its properties as per the following table:

Property Value
Material Name BulkMaterial
Poisson Ratio 0.25
Shear Modulus 10 MPa
Density 4100 kg/m³
Work function 0 J

 

# To create a bulk material, the last argument must be set to True deck.createMaterial('BulkMaterial', 0.25, 10_000_000, 4100, 0, True) 

 

2.2 Setting, particle size and shape

With the bulk material created, we can now add a particle to it. This can be achieved using the createMultisphereParticleType function. To utilize this function, we need to specify the bulk material and define the particle name. In this case, we will name it as MyParticle. By default, the createMultisphereParticleType function creates a triple-sphere shape.

Once the particle is created, we can adjust the radius and position of the spheres that constitute our particle using the setMultisphereSpherePosition and setMultispherePhysicalRadius functions. Then, we use the setAutoCalculateParticleProperties function to ensure the accurate calculation of the particle's properties.

 

# Add a particle to the bulk material deck.createMultisphereParticleType('BulkMaterial', particle_name = 'MyParticle')  # Set the position and radius of each sphere in MyParticle deck.setMultisphereSpherePosition('MyParticle', 0, [0.0, 0.004, 0.0]) deck.setMultispherePhysicalRadius('MyParticle', 0, 0.005) deck.setMultisphereSpherePosition('MyParticle', 1, [-0.00346, -0.002, 0.0]) deck.setMultispherePhysicalRadius('MyParticle', 1, 0.005) deck.setMultisphereSpherePosition('MyParticle', 2, [0.00346, -0.002, 0.0]) deck.setMultispherePhysicalRadius('MyParticle', 2, 0.005)  # Enable auto-calculation of particle properties deck.setAutoCalculateParticleProperties('MyParticle', True)

 

3. Defining the equipment material

After creating the bulk material and particles, the next step is to create the equipment material. We will name the equipment material as EquipMaterial. To create the equipment material, we will use the createMaterial function, but this time we need to set the last argument to False. The properties of EquipMaterial are shown in the table below:

Property Value
Material Name EquipMaterial
Poisson Ratio 0.3
Shear Modulus 70 GPa
Density 7800 kg/m³
Work function 0 J

 

# To create the equipment material, the last argument must be set to False deck.createMaterial('EquipMaterial', 0.30, 70_000_000_000, 7800, 0, False)

 

3.1. Defining the interaction parameters

Now that all the materials in our simulation have been created we can set the interaction parameters between them. This can be achieved using the createInteraction function. The interaction parameters are specified in the table below:

Contact parameter Particle-Particle Particle-Geometry
Coefficient of restitution 0.36 0.40
Coefficient of static friction 0.64 0.45
Coefficient of rolling friction 0.13 0.15

 

# Define particle-particle interactions deck.createInteraction('BulkMaterial', 'BulkMaterial', 0.36, 0.64, 0.13)  # Define particle-geometry interactions deck.createInteraction('BulkMaterial', 'EquipMaterial', 0.40, 0.45, 0.15)

 

4. Defining the geometries

To create the geometries, we need to specify the coordinates of each node as well as the connectivity matrix. The connectivity matrix represents the relationships between the vertices (points) and the triangles in the mesh. Each row of the connectivity matrix corresponds to a triangle in the mesh.

In this example, we create two geometries: a virtual square named MySquare and a physical cube named MyCube. After specifying the coordinates and connectivity matrix for each geometry, we can use the createGeometry function to create them. Additionally, if you have an existing geometry, you can import it into your simulation using Python libraries like trimesh. These libraries provide access to the geometry's nodes and connectivity list, allowing you to import the geometry into your simulation seamlessly.

 

# Define the 4 corners of the square square_coords = np.array([[-0.5, -0.5, 0.5],[0.5, -0.5, 0.5],[0.5, 0.5, 0.5],[-0.5, 0.5, 0.5]])  # Define the 2 triangles that make up the square square_triangles = np.array([[0,3,1], [1,3,2]])  # Define the 8 corners of the cube cube_coords= np.array([[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]])      # Define the 12 triangles that make up the cube  cube_triangles= np.array([[0,3,1],[1,3,2],[0,4,7],[0,7,3],[4,5,6],[4,6,7],[5,1,2],[5,2,6],[2,3,6],[3,7,6],[0,1,5],[0,5,4]])  # Create the square deck.createGeometry('MySquare', 'EquipMaterial', square_coords, square_triangles, geometry_type='Virtual')  # Create the cube deck.createGeometry('MyCube', 'EquipMaterial', cube_coords, cube_triangles, geometry_type='Physical')

 

4.1. Defining geometry motion, particle factory and particle size distribution

Now that the geometries are created, we can add a linear rotation to MyCube and a particle factory to MySquare. To add a linear rotation to MyCube, we use the createKinematic function and specify the parameters listed in the table below:

   
Name Box_rotation
Type Linear Rotation
Start Time 2 s
Initial Velocity 10 rpm
Axis Start Point (0, 0, -1)
Axis End Point (0, 0, 1)

 

To add a particle factory to MySquare, we use the createFactory function and specify the parameters listed in the table below:

   
Name BulkMaterial_Factory
Material Name BulkMaterial
Velocity fixed
Fixed Velocity (0, 0, -1) m/s
Start Time 0 s
Total to Create 30000 particles
Creation Rate 15000 particles/s


Finally, we can define the particle size distribution using the setParticleUserDefinedSizeDistribution function. Here, we specify the scale and the percentage mass in each size class. The last argument of the function is set to True so that the scales are applied to the particle radius.

# Set geometry motion - linear rotation rotation_in_rpm = 10 rotation_in_rad_s = rotation_in_rpm * (2*np.pi/60) deck.createKinematic('MyCube', kinematic_name= 'Cube_rotation', kinematic_type= 'Linear Rotation', start_time= 2.0,                       rotation_init_angular_velocity_value= rotation_in_rad_s, rotation_start_point= [0.0, 0.0, -1.0],                       rotation_end_point= [0.0, 0.0, 1.0])    # Create particle factory deck.createFactory('MySquare', 'BulkMaterial_Factory', 'BulkMaterial', velocity= 'fixed', fixed_velocity=[0.0, 0.0, -1.0],                     start_time= 0, is_mass_creation= False, total_to_create= 30_000, is_mass_generation= False,                    creation_rate= 15_000)  # Define the particle size distribution scales = np.array([1.0, 1.2, 1.5, 1.7]) mass_percentages = np.array([45, 30, 5, 20]) deck.setParticleUserDefinedSizeDistribution('MyParticle', scales, mass_percentages, is_scaled_by_radius= True)

 

5. Defining the physics model

Finally, we need to define the physics models that we'll use in our simulation. For particle-particle contacts, we'll use the Hertz-Mindlin model with JKR cohesion. Since the default Hertz-Mindlin model is already present, we first need to delete it using the deletePhysicsModelSurfSurf function. Then, we specify the surface energy value for the particle-particle contacts and add the model to our simulation using the createPhysicsModelSurfSurf function. For the particle-geometry contacts, we'll keep the default Hertz-Mindlin model and add the Relative Wear model. Since the Relative Wear model doesn't require any additional parameters, we can simply add it to our simulation using the createPhysicsModelSurfGeom function.

 

# Delete the default Hertz-Mindling model deck.deletePhysicsModelSurfSurf('Hertz-Mindlin (no slip)')  # Define the JKR surface energy model_data=np.array([('BulkMaterial', 'BulkMaterial', 2)], dtype=([('name1', 'S256'), ('name2', 'S256'), ('value', '<f8')]))  # Add the particle-particle contact model deck.createPhysicsModelSurfSurf('Hertz-Mindlin with JKR',{"data": model_data})  # Add the particle-geometry contact model deck.createPhysicsModelSurfGeom('Relative Wear',{})

 

6. Setting the environment and saving the deck

In the final step of our simulation setup, we need to define the minimum and maximum values of the simulation domain. This can be done using the setDomainMin and setDomainMax functions. The domain represents the bounding box within which the simulation will take place. We also need to save our work using the save function to store the simulation deck for future use.

 

# Set the minimum values of the simulation domain deck.setDomainMin([-1.01, -1.01, -1.01])  # Set the maximum values of the simulation domain deck.setDomainMax([1.01, 1.01, 1.01])  # Save the simulation deck deck.save()

Pre-Requisite

EDEM 2023.1 or later installed

Usage/Installation Instructions

Download the attached zip file and extract all files. Open EDEM and go to the Analyst tab. Click on File, Run EDEMpy script and select the script.

Note: the deck and the script should be in the same folder.