EDEMpy Introduction

EDEMpy is a python library for accessing EDEM data from .h5 files (The underlying EDEM file format). It enables complex and ad hoc post processing of EDEM data using the vast array of tools in python. EDEMpy does not require advanced programming knowledge.

 

EDEMpy is installed by default with EDEM.  This includes a version of python as well as NumPy and HDF libraries.  These enable the interaction between python and the EDEM dataset.  It is also recommended to install spyder and matplotlib as these are commonly used by EDEM users and are required for some of the examples (available on the EDEM knowledge base).

 

The officially supported platform for EDEMpy is currently Python 3.5.4.

 

The default Altair installation path includes the correct version installed at:

 

Windows: C:\Program Files\Altair\2022\common\python\python3.5\win64\python.exe

Linux:~/2022/altair/common/python/python3.5/linux64/bin/python

 

Install EDEMpy in a Custom Python Installation

The EDEM installation folder includes EDEMpy as a PIP package file ready to be installed. For the default EDEM installation path, this is located at:

 

Windows:C:\Program Files\Altair\2022\EDEM\EDEMpy\

Linux:~/2022/altair/EDEM/EDEMpy/

 

Open a console window from the folder and use the follwing command:

python -m pip install edempy-0.2.0.tar.gz

 

Getting Started

Accessing the documentation

  1. Extract content of edempy-0.2.0.tar.gz
  2. Open edempy-0.2.0\docs\html\index.html to access documentation
  3. Access tutorial example on the EDEM community page

 

Syntax

The sections below give an overview of the syntax used in EDEMpy:

Syntax

import edempy.Deck as Deck
deck = Deck(‘Rock_Box_Tutorial.dem’)
deck.numTimesteps
deck.timestepValues
deck.numGeoms
deck.domainMax
deck.domainMin
deck.materialNames
deck.particleNames
deck.geometryNames
deck.creatorData.*
deck.timestep[].*

 

Hint: dir(<obj>) Can be used to return a list of all valid attributes and functions for that object

 

Syntax – creatorData.*

Creator data is index by time step. For example, use a ‘0’ to access data from the first time step:

 

deck.creatorData[0].domain*
deck.creatorData[0].geometry*
deck.creatorData[0].interactions*
deck.creatorData[0].materials*
deck.creatorData[0].particle*
...

 

Note:

This is a breaking change from previous versions of EDEMpy, in which only the first time step used to be accessible. To fix old scripts simply change creatorData to creatorData[0] .

 

Syntax – creatorData.geometry*

deck.creatorData[0].geometryNames

  ['Receiving Conveyor',
   'Head Pulley',
   'Feed Conveyor',
   'Rock Box',
   'New Section 4'
]

deck.creatorData[0].geometry[0].getXCoords()
   or
deck.creatorData[0].geometry['Receiving Conveyor'].getXCoords()

  array([-0.06034673, -0.13356984, 0.02122977,
          ...
         1.55698291, -0.61505298, -0.7890473])

 

Throughout EDEMpy, references can be made either by index or by name of the object.

 

deck.creatorData[].geometry[].getXCoords
deck.creatorData[].geometry[].getYCoords
deck.creatorData[].geometry[].getZCoords
deck.creatorData[].geometry[].getXForce
deck.creatorData[].geometry[].getYForce
deck.creatorData[].geometry[].getZForce
deck.creatorData[].geometry[].getTriangleNodes
deck.creatorData[].geometry[].getMaterial
deck.creatorData[].geometry[].getFactoryNames
...

The following example script shows all of a deck’s geometries on a 3D plot:


import
matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from edempy import Deck
deck = Deck('RockBox_Example.dem')

ax = plt.axes(projection='3d')
for i in range(deck.numGeoms):
    x = deck.creatorData[0].geometry[i].getXCoords()
    y = deck.creatorData[0].geometry[i].getYCoords()
    z = deck.creatorData[0].geometry[i].getZCoords()
    tri = deck.creatorData[0].geometry[i].getTriangleNodes()
    ax.plot_trisurf(x, y, z, triangles=tri, alpha=0.2, color='lightgrey')

 

Syntax – creatorData.particle*

deck.creatorData[0].particleNames

 

  ['Rock Particle']

 

deck.creatorData[0].particle[0].getRawMass()
  or
deck.creatorData[0].particle['Rock Particle'].getRawMass()

 

  0.17916

 

deck.creatorData[0].particle[].getRawMass()
deck.creatorData[0].particle[].getRawVolume()
deck.creatorData[0].particle[].getRawInertia()
deck.creatorData[0].particle[].getMaterial()
deck.creatorData[0].particle[].getSpheres()

...

 

Syntax – timestep[]*

deck.timestepKeys

 

  ['0.0',
   '0.020066229407480374',
    ...
   '14.980040524525522',
   '15.000106753933084']

 

deck.timestep[1].particle[0].getPositions()

 

  array([[-3.43503618, -0.5798251, 0.78605941],
         [-3.4392864, -0.36739463, 0.70445507],
          ...
         [-3.48907014, -0.59069285, 1.00479687],
         [-3.49635857, -0.17363206, 0.22822767]])

 

 

NB. particle[0] Indexes particle type. Can use index number or the name of the particle type as a string.

 

Syntax – timestep[].particle[]*

deck.timestep[1].particle[0].getPositions()

 

  array([[-3.43503618, -0.5798251, 0.78605941],
         [-3.4392864, -0.36739463, 0.70445507],
          ...
         [-3.48907014, -0.59069285, 1.00479687],
         [-3.49635857, -0.17363206, 0.22822767]])

 

deck.timestep[1].particle[0].getIds()

 

  array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

 

deck.timestep[1].particle[0].getPositions(1)

 

  array([[-3.43503618, -0.5798251, 0.78605941]])

 

Syntax – timestep[].particle[]*

deck.timestep[].particle[].getIds()
deck.timestep[].particle[].getXPositions()
deck.timestep[].particle[].getYPositions()
deck.timestep[].particle[].getZPositions()
deck.timestep[].particle[].getXVelocities()
deck.timestep[].particle[].getYVelocities()
deck.timestep[].particle[].getZVelocities()
deck.timestep[].particle[].getXForce()
deck.timestep[].particle[].getYForce()
deck.timestep[].particle[].getZForce()
...

 

Syntax – timestep[].geometry[]*

deck.timestep[100].geometry[2].getXForce()

    array([0., 0., 0.,  ...  0., 0., 0.02990218])

sum(deck.timestep[100].geometry[2].getXForce())

    -27.48965316543911

deck.timestep[].geometry[].getXForce()
deck.timestep[].geometry[].getYForce()
deck.timestep[].geometry[].getZForce()
deck.timestep[].geometry[].getXCoords()
deck.timestep[].geometry[].getYCoords()
deck.timestep[].geometry[].getZCoords()
deck.timestep[].geometry[].getTranslationMatrix()
...

 

Syntax – timestep[].contact.*

deck.timestep[100].contact.surfSurf.getIds()

  array([[581, 597],
         [560, 581],
          ...
         [74, 96],
         [59, 72]])

deck.timestep[].contact.surfSurf.getIds()
deck.timestep[].contact.surfSurf.getXPositions()
deck.timestep[].contact.surfSurf.getYPositions()
deck.timestep[].contact.surfSurf.getZPositions()
deck.timestep[].contact.surfSurf.getNormalForce()
deck.timestep[].contact.surfSurf.getTangentialForce()
...

 

Contacts can be surfSurf or surfGeom.  getIds() Returns a list of elements in contact (particle or geometry).


Syntax – timestep[].bond*

deck.timestep[60].bond.getIds()

  array([[124, 148],
         [218, 230],
          ...
         [308, 360],
         [308, 364]])

deck.timestep[].bond.getIds()
deck.timestep[].bond.getState()
deck.timestep[].bond.getPositions()
deck.timestep[].bond.getNormalForce()
deck.timestep[].bond.getTangentialForce()
deck.timestep[].bond.getNormalTorque()
deck.timestep[].bond.getTangentialTorque()

...
 

 getIds() Returns a list of particles with bonds.
 

Syntax – timestep[].collision*

deck.timestep[20].collision.surfSurf.getNormalEnergy()

  array([0.01343155, 0.0421292,
          ...
         0.08775681, 0.08913101])

deck.timestep[20].collision.surfSurf.getNormalEnergy()
deck.timestep[20].collision.surfSurf.getTangentialEnergy()
deck.timestep[20].collision.surfSurf.getXPosition()
deck.timestep[20].collision.surfSurf.getYPosition()
deck.timestep[20].collision.surfSurf.getZPosition()

...

 

Collisions can be surfSurf or surfGeom.

 

Syntax – Binning

EDEMpy offers methods for binning objects using the BoxBin and CylinderBin classes. Users can import these classes using:

 

from edempy import BoxBin, CylinderBin

 

BoxBin is defined by an origin [x, y, z] and edge lengths.
CylinderBin is defined by start and end points [x, y, z] and a radius.

 

Any arbitrary list of objects can be searched for inside a bin by passing the getBinnedObjects() function two arguments. For example:

 

boxbin = BoxBin([-0.25, 0.0, 0.0], 0.3, 1.0, 0.2)

 

ids = deck.timestep[tStep].particle[0].getIds()
pos = deck.timestep[tStep].particle[0].getPositions()

 

binned_ids = boxbin.getBinnedObjects(ids, pos)

 


The getBinnedObjects() function also accepts arguments for queryType (‘max’, ‘min’, ‘average’, or ‘total’) and transform where the user can specify a 4x4 transformation matrix for translating and rotating the bin – this can be used with the getTransformMatrix() function on geometries to have bins track geometry motion.
 

Syntax – getBinnedProperty

The getBinnedProperty() function can be used to collect binned particle, contact and bond data. For example:

 

binnedAveVel, x, y, z = 
      deck.timestep[tstep].particle[0].getBinnedProperty(50, 50, 50,
                                                         option='velocity,
                                                         average=True)

 

This returns binned average velocity for particle type 0, as well as the x, y, and z position of the bins. It is useful for displaying EDEM data as a continuum, plotted using Matplotlib functions such as contour() . You can also use it to calculate custom properties, such as segregaton index.

By default the bin’s max and min coordinates match the simulation domain, but custom limits can be passed. The default option returns IDs.

 

 

Custom Properties

EDEMpy permits both reading and writing of custom properties from a deck.

 

There are four different types of custom property:

 

deck.particleCustomProperties
deck.geometryCustomProperties
deck.simulationCustomProperties
deck.contactCustomProperties

 

Contact custom properties are further divided into two subtypes; particle-particle contacts and particle-geometry contacts:

 

deck.contactCustomProperties.surfSurf
deck.contactCustomProperties.surfGeom

 

Creating custom properties

Properties can be created with the ‘createCustomProperty()’ function:

 

deck.particleCustomProperties.createCustomProperty(
    name="Property Name",
    defaultValue=[0.0, 0.0, 0.0], # This would be a 3D property
)


Alternatively, if ‘defaultValue’ is to have the same value repeated for each dimension, the property’s dimensionality can instead be expressed as a number:

 

deck.particleCustomProperties.createCustomProperty(
    name="Property Name",
    defaultValue=0.0,
    numElements=3,  # This is still a 3D property
)

Reading custom properties

A list of existing properties is available for each property type as ‘customPropertyNames’:

 

# Returns a list of the names of all particle custom properties
deck.particleCustomProperties.customPropertyNames

 

The data for a custom property can be obtained as a Numpy array using the ‘getData’ function:

 

deck.particleCustomProperties.getData(
    property="Property Name",  # Can alternatively use property ID
    tstep=0,  # Timestep index
    particleType=0,  # Must specify a particle type
)


The third parameter has different meaning for each property type:

Writing custom properties

Writing data to a custom property is done using ‘setData’ instead of ‘getData’, adding a Numpy array (or list) of the desired data as an additional parameter. For example:

 

deck.particleCustomProperties.setData(

    property="Property Name",  # Can alternatively use property ID

    tstep=0,  # Timestep index

    particleType=0,  # Must specify a particle type

    value=[

        # Assuming 3D property and 4 particles

        [0.1, 0.1, 0.1],

        [0.1, 0.1, 0.1],

        [0.1, 0.1, 0.1],

        [0.1, 0.1, 0.1],

    ]

)

 

It is important to note that, due to the multi-threaded nature of simulations, the number and ordering of particles will not be the same in each timestep.

 

Deleting custom properties

Properties are deleted using ‘deleteCustomProperty’:

 

# Can alternatively give the property ID instead of the name
deck.particleCustomProperties.deleteCustomProperty("My Property")

 

Changing properties

Renaming a property

An existing property can be renamed:

 

deck.particleCustomProperties.renameCustomPropertyIndex(
    prev_name,  # Current name
    new_name  # New name
)

 

Changing the order of properties

The index of two properties can be switched:

deck.particleCustomProperties.changeCustomPropertyIndex(
    old_index,
    new_index
)

 

To get the current index of a property, use the ‘getPropertyIndexForName()’ function:

deck.particleCustomProperties.getPropertyIndexForName("My Property")
 

 

Complete example 

The following code creates a particle custom property “My Property” which starts as 0.0 for each particle and increases by 1 every timestep while the particle is in the simulation: 

 

import edempy 

deck = edempy.Deck("path_to_deck.dem"

particle_type = 0 

prop_name = "My Property" 

properties = deck.particleCustomProperties 

properties.createCustomProperty( 

   prop_name

   0.0,  # Default value (scalar) 

for time_id in range(1, deck.numTimesteps): 

   # Get current values 

   old_data = properties.getData( 

      property=prop_name, 

      tstep=time_id, 

      particleType=particle_type, 

   ) 

   # Add 1 

   new_data = old_data + 1.0 

   # Write new values 

   properties.setData( 

      property=prop_name, 

      tstep=time_id, 

      particleType=particle_type, 

      value=new_data 

   ) 

 

 

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:

 

http://www.apache.org/licenses/LICENSE-2.0

 

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and limitations under the License.
 

 


 

 

     
 

(c) 2022 Altair Engineering Inc. All Rights Reserved.

Intellectual Property Rights Notice | Technical Support