Analysis

Submodules

pygran_analysis.core

A submodule that provides the fundamental classes used in the analysis module

pygran_analysis.dynamics

This module provides classes for time-dependent analysis.

pygran_analysis.equilibrium

This module provides static/structural methods of analysis.

Introduction

The analysis module enables programmers to read and analyze DEM trajectory files. The trajectory file stores sequential snapshots of the system positions, velocities, forces, stresses, or other physical quantities of interest. The most fundamental class in this module is System, which uses a factory class to instantiate a SubSystem subclass. In principal, any implementation of SubSystem can be instantiated with this factory. A System instance is always created by passing the filename of a specific SubSystem:

Granular = System(SubSystem='path/to/file')

System is an iterator. Thus, it can be iterated/looped over when the supplied SubSystem contains a time series. For example, the statement:

for frame in Granular:
        # do something with Granular.SubSystem.property

loops over every frame stored in the suppled trajectory file, returning the frame number at each instant.

SubSystem is an abstract class that encapsulates common attributes and methods for basic DEM objects such as Particles and Mesh, both being derived classes of SubSystem. While SubSystem is a mutable object, its properties cannot be directly modified by the user, i.e. they can modified only by the methods in SubSystem. The basic data structure in this object is a Python dictionary (SubSystem.data) which contains references to the SubSystem attributes and is used to generate the dynamic interface of a SubSystem object. The attributes of SubSystem change from one frame to another for the same system, thus, SubSystem.data is updated every single time the System is evolved in time.

Systems and SubSystems form the core classes used in the analysis module (Fig. 8), and they provide numerous methods for temporal, structural, and image analyses as covered in more detail in the next sections.

basic classes of PyGran.analysis module

Fig. 8 A UML diagram of the three fundamental classes and some of their methods and attributes in the analysis module. The state of a DEM system is determined by the System class, which creates and stores one or more instances of classes derived from SubSystem that describe basic DEM elements such as particles or surface mesh(es).

Systems & SubSystems

System constructor

The System class is the most fundamental class in PyGran. It uses a factory to create objects derived from SubSystem that describe the state of a granular system (Fig. 8). These subclasses can be instantiated from an input data dictionary or copied from another instance of SubSystem. System creates an instance (or a list of instances) of SubSystem from input filename strings (or list of strings) that are passed to System.__init__ by a factory object.

System contains all the objects, methods, and properties that describe the state of a DEM system. This class also handles I/O operations and ensures proper frame to frame propagation when reading input trajectory files. The frame is controlled only by System when the latter is looped over via methods defined in a SubSystem sublass (read/write functions). Since DEM simulations consist of a set of particles in contact with surface triangulations (representing walls), System creates subclasses of SubSystem such as Particles and emph{Mesh} (Fig. 8) based on input trajectory files. The 4 different unit systems supported by this class are summarized in Table table_units.

SubSystem constructor

This is an abstract class that encapsulates common attributes and methods for basic DEM objects such as Particles and Mesh, both being derived classes of SubSystem. While SubSystem is a mutable object, its properties cannot be directly modified by the user, i.e. they can modified only by the methods in SubSystem. The basic data structure in this object is a Python dictionary (SubSystem.data) which contains the SubSystem attributes and is used to instantiate a SubSystem object, i.e.

NewSS = SubSystem(**input_data)

Alternatively, SubSystem objects can be used to create new SubSystem objects (i.e. copy constructor):

CopySS = SubSystem(SubSystem=OriginalSS)

Todo

A System object can also be sliced (by frames), e.g. the following statement

SlicedSys = System[start:end]

yields a new System object (SlicedSys) that contains all frames from start to end-1.

Particles

The Particles class provides a way to store, manipulate, and operate on particle attributes generated by DEM simulation. This class is a subclass of System and can therefore be sliced and looped over. Furthermore, this class provides several basic routines for computing properties usually encountered in powder technology (such as mass density, radial distribution function, radius of gyration, etc.) as well as particle-based operators discussed below.

Binary operations

Extended assignments can be made to Particles with +=. For example, Particles_i is appended to Particles with the following statement:

Particles += Particles_i

If Particles_i has fewer attributes than those in Particles , then this assignment is rejected. Otherwise, any additional attributes of Particles_i not found in Particles are neglected.

2 Particles objects can be concatenated with the + operator. This operation can lead to reduction in the number of attributes if one of the classes being added has fewer attributes than the other(s). In this case, the resultant Particles will acquire concentenated attributes specified by the class with minimum number of attributes. 2 Particles objects can also be multiplied wth * to yield a new object whose vector attributes are the geometric mean of the external product of the vector attributes of the two objects being multiplied. For instance, if 3 objects Particles_i, Particles_j, and Particles_k contain \(n_i\), \(n_j\), and \(n_k\) particles, respectively, then the following code

Particles = Particles_i + Particles_j * Particles_k

yields a new Particles object containing \(n_i + n_j n_k\) particles and with vector attributes \([a_{i,1}, ... , a_{i,n_i}, \sqrt{a_{j,1} \times a_{k,1}}, ... \sqrt{a_{j,n_j n_k} \times a_{k,n_j n_k}}]\).

Basic methods

Some of the basic methods available to Particles are shown in Fig. 8. Furthermore, the PyGran.analysis module provides a Neighbors class that is instantiated with a Particles object to provide methods for nearest neighbor analysis. With this class, properties such as coordination numbers, overlap distances, and force chains can be readily computed.

Input/output

Any class derived from SubSystem must implement read/write methods. In the current version, PyGran supports reading and writing particle trajectory files for LIGGGHTS. The input trajectory can be a dump or a vtk [SLM04] file.

Custom SubSystems

User-defined subclasses of SubSystem can be easily created by using Python’s inheritence feature. The keyword module must be passed to the subclass constructor in order to make sure PyGran imports the module containing the subclass.

PyGran’s extensible and object-oriented design makes it ideal for creating user-defined particles. Since System uses a Factory class to instantiate a Particles or Mesh object, it can in principle be used to instantiate a user-defined class. This is demonstrated in the code below for a simple coarse-grained class that demonstrates the use of the filter method to eliminate particles overlapping by a certain %.

A simple user-defined CoarseParticles class can be defined as a subclass of Particles with two key arguments: scale, which controls the level of coarse-graining (or reduction) and percent which is used to eliminate the resultant coarse-grained particles overlapping by a certain percentage with respect to their radius. A script that implements this class is shown below:

class CoarseParticles(analysis.Particles):
        def __init__(self, **args):
                super().__init__(**args)

                if 'scale' in args and 'percent' in args:
                        self.scale(args['scale'], ('radius',))
                        CG = analysis.equilibrium.Neighbors(self).filter(percent=args['percent'])

                        self.__init__(CoarseParticles=CG)

The CoarseParticles object uses a recursive call to instantiate a derivative of the Particles class and therefore inherits all of the latter’s properties and methods.

Mesh

The Mesh class uses the VTK library to read input mesh files and expose the stored attributes (nodes, positions, stresses, etc.) to the user.

Surface walls are represented in PyGran by the Mesh class, a subclass of SubSystem (see Fig. 8). This class uses the VTK library [SLM04] to read an input mesh trajectory (one or more sequence of VTK file(s)) and expose all of the stored file variables to the user. This is particularly useful for analyzing DEM simulation involving mesh-particle interaction or coupled CFD-DEM simulations.