ovito.data

This module contains object types that represent various kinds of data, which are produced and processed by OVITO’s data pipeline system. It also provides container classes for such data objects and some additional utility classes to work with neighbor lists and bonds.

Data collection types:

Data objects:

Auxiliary data classes:

Utility classes:

class ovito.data.DataCollection

A DataCollection is a generic container that holds together a set of data objects, each representing different pieces of data such as the simulation cell, particle properties or the list of bonds. OVITO knows various types of data objects including

All data objects of a DataCollection are stored in its objects array. This list can hold an arbitrary number of data objects of arbitrary type and in arbitrary order. The find() and find_all() methods allow to look up objects in the collection of a particular data type. For example, you can retrieve the SimulationCell instance that is typically present in a data collection using find():

data = pipeline.compute()
cell = data.find(SimulationCell)
assert(cell is None or cell in data.objects)

Sometimes you need to make sure that a SimulationCell is present in the data collection. Then you should use the expect() method instead of find(). It will raise an exception if there is no SimulationCell instance:

cell = data.expect(SimulationCell)
assert(cell is not None and cell in data.objects)

Additionally, the convenience accessor fields particle_properties and bond_properties provide filtered views of just the ParticleProperty and BondProperty objects in the collection:

pos_property = data.particle_properties['Position']
assert(pos_property in data.objects)

Note that DataCollection is an abstract base class (an interface). Concrete types that implement this interface are e.g. PipelineFlowState, FileSource and StaticSource. That means all these classes are containers for data objects, but they serve different purposes within the OVITO framework. The DataCollection base class provides the functionality that is common to all of them.

attributes

A dictionary of key-value pairs that represent global tokens of information which are not associated with any specific data object in the data collection.

An attribute is a value of type int, float, or str with a unique identifier name such as "Timestep" or "ConstructSurfaceMesh.surface_area". The attribute name serves as keys for the attributes dictionary of the data collection. Attributes are dynamically generated by modifiers in a data pipeline or by a data source as explained in the following.

Attributes loaded from input files

The Timestep attribute is loaded from LAMMPS dump files and other simulation file formats that store the simulation timestep. Such input attributes can be retrieved from the .attributes dictionary of a pipeline’s FileSource:

>>> pipeline = import_file('snapshot_140000.dump')
>>> pipeline.source.attributes['Timestep']
140000

Other attributes read from an input file are, for example, the key-value pairs found in the header line of extended XYZ files.

Dynamically computed attributes

Analysis modifiers like the CommonNeighborAnalysisModifier or the ClusterAnalysisModifier output scalar computation results as attributes. The reference documentation of each modifier type lists the attributes it produces.

For example, the number of clusters identified by the ClusterAnalysisModifier can be queried as follows:

pipeline.modifiers.append(ClusterAnalysisModifier(cutoff = 3.1))
data = pipeline.compute()
nclusters = data.attributes["ClusterAnalysis.cluster_count"]

Exporting attributes to a text file

The ovito.io.export_file() function supports writing attribute values to a text file, possibly as functions of time:

export_file(pipeline, "data.txt", "txt", 
    columns = ["Timestep", "ClusterAnalysis.cluster_count"], 
    multiple_frames = True)

User-defined attributes

The PythonScriptModifier allows you to generate your own attributes that are dynamically computed (typically on the basis of some other input information):

pipeline.modifiers.append(CommonNeighborAnalysisModifier())
            
def compute_fcc_fraction(frame, input, output):
    n_fcc = input.attributes['CommonNeighborAnalysis.counts.FCC']
    n_total = len(input.particle_properties['Position'])
    output.attributes['fcc_fraction'] = n_fcc / n_total

pipeline.modifiers.append(PythonScriptModifier(function = compute_fcc_fraction))
print(pipeline.compute().attributes['fcc_fraction'])

The CommonNeighborAnalysisModifier used in the example above generates the attribute CommonNeighborAnalysis.counts.FCC to report the number of atoms that form an FCC lattice. To compute the fraction of FCC atoms from that, we need to divide by the total number of atoms in the system. To this end, we insert a PythonScriptModifier into the pipeline behind the CommonNeighborAnalysisModifier. Our custom modifier function generates a new attribute named fcc_fraction. Finally, the value of the user-defined attribute can be queried from the pipeline or exported to a text file using the export_file() function as described above.

bond_properties

Returns a BondPropertiesView, which allows to access all BondProperty objects stored in this data collection by name. Furthermore, it provides convenience functions for adding new bond properties to the collection.

copy_if_needed(obj, deepcopy=False)

Makes a copy of a data object from this data collection if the object is not exclusively owned by the data collection but shared with other collections. After the method returns, the data object is exclusively owned by the collection and it becomes safe to modify the object without causing unwanted side effects.

Typically, this method is used in custom modifier functions (see PythonScriptModifier) that participate in OVITO’s data pipeline system. A modifier function receives an input collection of data objects from the system. However, modifying these input objects in place is not allowed, because they are owned by the pipeline and modifying them would lead do unexpected side effects. This is where this method comes into play: It makes a copy of a given data object and replaces the original in the data collection with the copy. The caller can now safely modify this copy in place, because no other data collection can possibly be referring to it.

The copy_if_needed() method first checks if obj, which must be a data object from this data collection, is shared with some other data collection. If yes, it creates an exact copy of obj and replaces the original in this data collection with the copy. Otherwise it leaves the object as is, because it is already exclusively owned by this data collection.

Parameters:obj (DataObject) – The object from this data collection to be copied if needed.
Returns:An exact copy of obj if it was shared with some other data collection. Otherwise the original object is returned.
expect(object_type)

Looks up the first data object in this collection of the given class type. Raises a KeyError if there is no instance matching the type. Use find() instead to test if the data collection contains the given type of data object.

Parameters:object_type – The DataObject subclass specifying the type of object to find.
Returns:The first instance of the given class or its subclasses from the objects list.
find(object_type)

Looks up the first data object from this collection of the given class type.

Parameters:object_type – The DataObject subclass that should be looked up.
Returns:The first instance of the given class or its subclasses from the objects list; or None if there is no instance.

Method implementation:

def find(self, object_type):
    for o in self.objects:
        if isinstance(o, object_type): return o
    return None
find_all(object_type)

Looks up all data objects from this collection of the given class type.

Parameters:object_type – The DataObject subclass that should be looked up.
Returns:A Python list containing all instances of the given class or its subclasses from the objects list.

Method implementation:

def find_all(self, object_type):
    return [o for o in self.objects if isinstance(o, object_type)]
objects

The list of data objects that make up the data collection. Data objects are instances of DataObject-derived classes, for example ParticleProperty, Bonds or SimulationCell.

You can add or remove objects from the objects list to insert them or remove them from the DataCollection. However, it is your responsibility to ensure that the data objects are all in a consistent state. For example, all ParticleProperty objects in a data collection must have the same lengths at all times, because the length implicitly specifies the number of particles. The order in which data objects are stored in the data collection does not matter.

Note that the DataCollection class also provides convenience views of the data objects contained in the objects list: For example, the particle_properties dictionary lists all ParticleProperty instances in the data collection by name and the bond_properties does the same for all BondProperty instances. Since these dictionaries are views, they always reflect the current contents of the master objects list.

particle_properties

Returns a ParticlePropertiesView, which allows accessing all ParticleProperty objects stored in this data collection by name. Furthermore, it provides convenience functions for adding new particle properties to the collection.

class ovito.data.DataObject

Abstract base class for all data objects. A DataObject represents a piece of data processed and produced by a data pipeline. Typically, a data object is part of a DataCollection. The ovito.data module lists the different data object types implemented in OVITO.

Certain data objects are associated with a Display object, which is responsible for generating the visual representation of the data and rendering it in the viewports. The display property provides access to the attached display object, which can be configured as needed to change the visual appearance of the data. The different types of display objects that exist in OVITO are documented in the ovito.vis module.

display

The Display object associated with this data object, which is responsible for rendering the data. If this field contains None, the data is non-visual and doesn’t appear in rendered images or the viewports.

class ovito.data.PipelineFlowState
Base class:ovito.data.DataCollection

A specific form of DataCollection flowing down a data pipeline and being manipulated by modifiers on the way. A PipelineFlowState is returned by the Pipeline.compute() method.

Note that OVITO’s data pipeline system typically works with shallow copies of data collections. Thus, data objects may be shared by more than one data collection. This implies that modifying a data object is potentially unsafe as it can cause unexpected side effects. To avoid accidentally modifying data that is owned by the pipeline system and shared by multiple data collections, the following rule must be strictly followed: Before modifying a data object in a PipelineFlowState you have to use the copy_if_needed() method to ensure the object is really a deep copy exclusively owned by the PipelineFlowState:

data = pipeline.compute()

# WRONG: Modifying the SimulationCell data object is not permitted, 
# because it is owned by the pipeline:
cell = data.expect(SimulationCell)
cell.pbc = (True, True, True)

# CORRECT: copy first, then modify 
cell = data.copy_if_needed(data.expect(SimulationCell))
cell.pbc = (True, True, True)

Note: The rule does not apply to Display objects. These objects, which are typically attached to data objects, are not modified by a data pipeline. It typically is okay to modify them even though they are shallow copies shared by multiple data objects:

# No need to copy the DataObject when modifying only its Display object
cell = data.expect(SimulationCell)
cell.display.line_width = 0.5
class ovito.data.SimulationCell
Base class:ovito.data.DataObject

Stores the geometric shape and the boundary conditions of the simulation cell. A SimulationCell data object is typically part of a DataCollection and can be retrieved through its expect() method:

from ovito.io import import_file
from ovito.data import SimulationCell

pipeline = import_file("simulation.dump")
cell = pipeline.source.expect(SimulationCell)

# Print cell matrix to the console. [...] is for casting to Numpy. 
print(cell[...])

The simulation cell geometry is stored as a 3x4 matrix (with column-major ordering). The first three columns of the matrix represent the three cell vectors and the last column is the position of the cell’s origin. For two-dimensional datasets, the is2D flag ist set. In this case the third cell vector and the z-coordinate of the cell origin are ignored by OVITO.

import numpy.linalg

# Compute simulation cell volume by taking the determinant of the
# left 3x3 submatrix of the cell matrix:
vol = abs(numpy.linalg.det(cell[0:3,0:3]))

# The SimulationCell.volume property computes the same value.
assert numpy.isclose(cell.volume, vol)

The SimulationCell object behaves like a standard Numpy array of shape (3,4). Data access is read-only, however. If you want to manipulate the cell vectors, you have to use a with compound statement as follows:

# Make cell twice as large along the Y direction by scaling the second cell vector: 
with cell.modify() as mat:
    mat[:,1] *= 2.0

A SimulationCell instance are always associated with a corresponding SimulationCellDisplay, which controls the visual appearance of the simulation box. It can be accessed through the display attribute inherited from DataObject.

# Change display color of simulation cell to red:
cell.display.rendering_color = (1.0, 0.0, 0.0)
is2D

Specifies whether the system is two-dimensional (true) or three-dimensional (false). For two-dimensional systems the PBC flag in the third direction (z) and the third cell vector are ignored.

Default:false
pbc

A tuple of three boolean values, which specify periodic boundary flags of the simulation cell along each cell vector.

volume

Computes the volume of the three-dimensional simulation cell. It is the absolute value of the determinant of the 3x3 cell submatrix.

volume2D

Computes the area of the two-dimensional simulation cell (see is2D).

class ovito.data.Property
Base class:ovito.data.DataObject

Stores the values for an array of elements (e.g. particle or bonds).

In OVITO’s data model, an arbitrary number of properties can be associated with data elements such as particle or bonds, each property being represented by a Property object. A Property is basically an array of values whose length matches the number of data elements.

Property is the common base class for the ParticleProperty and BondProperty specializations.

Data access

A Property object behaves almost like a Numpy array. For example, you can access the property value for the i-th data element using indexing:

property = data.particle_properties['Velocity']
print('Velocity vector of first particle:', property[0])
print('Z-velocity of second particle:', property[1,2])
for v in property: print(v)

Element indices start at zero. Properties can be either vectorial (e.g. velocity vectors are stored as an N x 3 array) or scalar (1-d array of length N). Length of the first array dimension is in both cases equal to the number of data elements (number of particles in the example above). Array elements can either be of data type float or int.

If necessary, you can cast a Property to a standard Numpy array:

velocities = numpy.asarray(property)

No data is copied during the conversion; the Numpy array will refer to the same memory as the Property. By default, the memory of a Property is write-protected. Thus, trying to modify property values will raise an error:

property[0] = (0, 0, -4) # "TypeError: 'ParticleProperty' object does not
                         # support item assignment"

A direct modification is prevented by the system, because OVITO’s data pipeline uses shallow data copies and needs to know when data objects are being modified. Only then results that depend on the changing data can be automatically recalculated. We need to explicitly announce a modification by using the Property.modify() method and a Python with statement:

with property.modify() as arr:
    arr[0] = (0, 0, -4)

Within the with compound statement, the variable arr refers to a modifiable Numpy array, allowing us to alter the per-particle data stored in the Property object.

components

The number of vector components if this is a vector property; or 1 if this is a scalar property.

modify()

Creates a Python context manager that manages write access to the internal property values. The manager returns a mutable Numpy array when used in a Python with statement, which provides a read/write view of the internal data.

name

The name of the property.

class ovito.data.SurfaceMesh
Base class:ovito.data.DataObject

This data object type stores a triangle mesh describing a surface or, more precisely, a two-dimensional manifold that is closed and orientable. Typically, surface meshes are produced by modifers such as the ConstructSurfaceModifier, CreateIsosurfaceModifier or CoordinationPolyhedraModifier.

Periodic domains

What is special about surface meshes is that they may be embedded in a periodic domain, i.e. a simulation cell with periodic boundary conditions. That means triangles of a surface mesh can connect vertices on opposite sides of a simulation box and wrap around correctly. OVITO takes care of computing the intersections of the periodic surface with the box boundaries and automatically produces a non-periodic representation of the triangle mesh when it comes to visualizing the surface.

The domain the surface mesh is embedded in is represented by a SimulationCell object, which is attached to the SurfaceMesh instance. You can access it through the domain attribute.

Visual representation

The visual appearance of the surface mesh in rendered images is controlled by its attached SurfaceMeshDisplay instance, which is accessible through the display base class attribute.

Interior and exterior region

As surface meshes are closed orientable manifolds, one can define an interior and an exterior region of space that are separated by the manifold. For example, if the surface mesh is constructed by the ConstructSurfaceModifier from a set of particles, then the region enclosed by the surface is the “solid” region and the outside region is the one containing no particles.

It can be that there is no interior region and the exterior region is infinite and fills all space. In this case the surface mesh is degenerate and comprises no triangles. The opposite extreme is also possible in periodic domains: The interior region extends over the entire domain and there is no outside region. Again, the surface mesh will consist of zero triangles in this case. To discriminate between the two situations, the SurfaceMesh class provides the all_interior flag, which is set when the interior region fills the entire periodic domain.

The locate_point() method can be used to test whether some point in space belongs to the interior or the exterior region.

File export

A surface mesh can be written to a file in the form of a conventional triangle mesh. For this, a non-periodic version is produced by truncating triangles at the domain boundaries and generating “cap polygons” to fill the holes that occur at the intersection of the interior region with the domain boundaries. To export the mesh, use the ovito.io.export_file() function and select vtk/trimesh as output format:

from ovito.io import import_file, export_file
from ovito.data import SurfaceMesh
from ovito.modifiers import ConstructSurfaceModifier

# Load a particle set and construct the surface mesh:
pipeline = import_file("simulation.dump")
pipeline.modifiers.append(ConstructSurfaceModifier(radius = 2.8))
mesh = pipeline.compute().expect(SurfaceMesh)

# Export the mesh to a VTK file for visualization with ParaView.
export_file(mesh, 'surface_mesh.vtk', 'vtk/trimesh')

Cutting planes

An arbitrary number of cutting planes can be attached to a SurfaceMesh, which allow to cut away parts of the mesh for visualization purposes. This is sometimes useful, if you want to open a hole in a closed surface to allow a look inside. The SurfaceMesh maintains a list of cutting planes, which are accessible through the get_cutting_planes() and set_cutting_planes() methods. Note that the cuts are non-destructive and dynamically computed only on the transient version of the mesh produced for visualization and data export purposes. The SliceModifier, which can act on a SurfaceMesh, performs the slice by simply adding a new entry to the SurfaceMesh‘s list of cutting planes.

Mesh data access

The methods get_vertices(), get_faces() and get_face_adjacency() methods provide access to the internal data of the surface mesh.

all_interior

Boolean flag indicating that the SurfaceMesh is degenerate and the interior region extends over the entire domain.

domain

The SimulationCell describing the (possibly periodic) domain in which this surface mesh is embedded in.

get_cutting_planes()

Returns a N x 4 array containing the definitions of the N cutting planes attached to this SurfaceMesh.

Each plane is defined by its unit normal vector and a signed displacement magnitude, which determines the plane’s distance from the coordinate origin along the normal, giving four numbers per plane in total. Those parts of the surface mesh which are on the positive side of the plane (in the direction the normal vector) are cut away.

Note that the returned Numpy array is a copy of the internal data stored by the SurfaceMesh.

get_face_adjacency()

Returns a M x 3 array listing the indices of the three faces that are adjacent to each of the M triangle faces in the mesh. This information can be used to traverse the neighbors of triangle faces. Every triangle face has exactly three neighbors, because surface meshes are closed manifolds.

get_faces()

Returns a M x 3 array with the vertex indices of the M triangles in the mesh. Note that the returned Numpy array is a copy of the internal data stored by the SurfaceMesh. Also keep in mind that a triangle face can cross domain boundaries if PBCs are used.

get_vertices()

Returns a N x 3 array with the xyz coordinates of the N vertices in the mesh. Note that the returned Numpy array is a copy of the internal data stored by the SurfaceMesh.

locate_point(pos, eps=1e-6)

Determines whether a spatial location is inside the region enclosed by the surface, outside of it, or exactly on the surface itself.

Parameters:
  • pos – The (x,y,z) coordinates of the test point
  • eps – Numerical precision threshold for point-on-surface test
Returns:

-1 if pos is inside the region enclosed by the surface, +1 if outside, 0 if exactly on the surface

set_cutting_planes(planes)

Sets the cutting planes to be applied to this SurfaceMesh. The array planes must follow the same format as the one returned by get_cutting_planes().

class ovito.data.DislocationNetwork
Base class:ovito.data.DataObject

This data object types stores the network of dislocation lines extracted by a DislocationAnalysisModifier.

Instances of this class are associated with a DislocationDisplay that controls the visual appearance of the dislocation lines. It can be accessed through the display attribute of the DataObject base class.

Example:

from ovito.io import import_file, export_file
from ovito.modifiers import DislocationAnalysisModifier

pipeline = import_file("simulation.dump")

# Extract dislocation lines from a crystal with diamond structure:
modifier = DislocationAnalysisModifier()
modifier.input_crystal_structure = DislocationAnalysisModifier.Lattice.CubicDiamond
pipeline.modifiers.append(modifier)
data = pipeline.compute()

total_line_length = data.attributes['DislocationAnalysis.total_line_length']
cell_volume = data.attributes['DislocationAnalysis.cell_volume']
print("Dislocation density: %f" % (total_line_length / cell_volume))

# Print list of dislocation lines:
network = data.dislocations
print("Found %i dislocation segments" % len(network.segments))
for segment in network.segments:
    print("Segment %i: length=%f, Burgers vector=%s" % (segment.id, segment.length, segment.true_burgers_vector))
    print(segment.points)

# Export dislocation lines to a CA file:
export_file(pipeline, "dislocations.ca", "ca")

# Or export dislocations to a ParaView file:
export_file(pipeline, "dislocations.vtk", "vtk/disloc")

File export

A dislocation network can be written to a data file in the form of polylines using the ovito.io.export_file() function (select the vtk/disloc output format). During export, a non-periodic version is produced by clipping dislocation lines at the domain boundaries.

segments

The list of dislocation segments in this dislocation network. This list-like object is read-only and contains DislocationSegment objects.

class ovito.data.DislocationSegment

A single dislocation line from a DislocationNetwork.

The list of dislocation segments is returned by the DislocationNetwork.segments attribute.

cluster_id

The numeric identifier of the crystal cluster of atoms containing this dislocation segment.

The true Burgers vector of the segment is expressed in the local coordinate system of this crystal cluster.

id

The unique identifier of this dislocation segment.

is_infinite_line

This property indicates whether this segment is an infinite line passing through a periodic simulation box boundary. A segment is considered infinite if it is a closed loop and its start and end points do not coincide.

See also the is_loop property.

is_loop

This property indicates whether this segment forms a closed dislocation loop. Note that an infinite dislocation line passing through a periodic boundary is also considered a loop.

See also the is_infinite_line property.

length

Returns the length of this dislocation segment.

points

The list of space points that define the shape of this dislocation segment. This is a N x 3 Numpy array, where N is the number of points along the segment. For closed loops, the first and the last point coincide.

spatial_burgers_vector

The Burgers vector of the segment, expressed in the global coordinate system of the simulation. This vector is calculated by transforming the true Burgers vector from the local lattice coordinate system to the global simulation coordinate system using the average orientation matrix of the crystal cluster the dislocation segment is embedded in.

true_burgers_vector

The Burgers vector of the segment, expressed in the local coordinate system of the crystal. Also known as the True Burgers vector.

class ovito.data.ParticleProperty
Base class:ovito.data.Property

Stores an array of per-particle values. This class derives from Property, which provides the base functionality shared by all property types in OVITO.

In OVITO’s data model, an arbitrary number of properties can be associated with the particles, each property being represented by a separate ParticleProperty object. A ParticleProperty is basically an array of values whose length matches the number of particles.

The set of properties currently associated with all particles is exposed by the DataCollection.particle_properties view, which allows accessing them by name and adding new properties.

Standard properties

OVITO differentiates between standard properties and user-defined properties. The former have a special meaning to OVITO, a prescribed name and data layout. Certain standard properties control the visual representation of particles. Typical examples are the Position property, the Color property and the Radius property. User-defined properties, on the other hand, may have arbitrary names (as long as they do not collide with one of the standard names) and the property values have no special meaning to OVITO, only to you, the user. Whether a ParticleProperty is a standard or a user-defined property is indicated by the value of its type attribute.

Creating particle properties

New properties can be created and assigned to particles with the ParticlePropertiesView.create() factory method. User-defined modifier functions, for example, use this to output their computation results.

Particle type property

The standard property 'Particle Type' stores the types of particles encoded as integer values, e.g.:

>>> data = node.compute()
>>> tprop = data.particle_properties['Particle Type']
>>> print(tprop[...])
[2 1 3 ..., 2 1 2]

Here, each number in the property array refers to a defined particle type (e.g. 1=Cu, 2=Ni, 3=Fe, etc.). The defined particle types, each one represented by an instance of the ParticleType auxiliary class, are stored in the types array of the ParticleProperty object. Each type has a unique id, a human-readable name and other attributes like color and radius that control the visual appearance of particles belonging to the type:

>>> for type in tprop.types:
...     print(type.id, type.name, type.color, type.radius)
... 
1 Cu (0.188 0.313 0.972) 0.74
2 Ni (0.564 0.564 0.564) 0.77
3 Fe (1 0.050 0.050) 0.74

IDs of types typically start at 1 and form a consecutive sequence as in the example above. Note, however, that the types list may store the ParticleType objects in an arbitrary order. Thus, in general, it is not valid to directly use a type ID as an index into the types array. Instead, the type_by_id() method should be used to look up the ParticleType:

>>> for i,t in enumerate(tprop): # (loop over the type ID of each particle)
...     print('Atom', i, 'is of type', tprop.type_by_id(t).name)
...
Atom 0 is of type Ni
Atom 1 is of type Cu
Atom 2 is of type Fe
Atom 3 is of type Cu

Similarly, a type_by_name() method exists that looks up a ParticleType by name. For example, to count the number of Fe atoms in a system:

>>> Fe_type_id = tprop.type_by_name('Fe').id   # Determine ID of the 'Fe' type
>>> numpy.count_nonzero(tprop == Fe_type_id)   # Count particles having that type ID
957

Note that OVITO supports multiple type classifications. For example, in addition to the 'Particle Type' standard particle property, which stores the chemical types of atoms (e.g. C, H, Fe, ...), the 'Structure Type' property may hold the structural types computed for atoms (e.g. FCC, BCC, ...) maintaining its own list of known structure types in the types array.

type

The type of the particle property. One of the following constants:

Type constant Property name Data type Component names
ParticleProperty.Type.User (a user-defined property with a non-standard name) int/float  
ParticleProperty.Type.ParticleType Particle Type int  
ParticleProperty.Type.Position Position float X, Y, Z
ParticleProperty.Type.Selection Selection int  
ParticleProperty.Type.Color Color float R, G, B
ParticleProperty.Type.Displacement Displacement float X, Y, Z
ParticleProperty.Type.DisplacementMagnitude Displacement Magnitude float  
ParticleProperty.Type.PotentialEnergy Potential Energy float  
ParticleProperty.Type.KineticEnergy Kinetic Energy float  
ParticleProperty.Type.TotalEnergy Total Energy float  
ParticleProperty.Type.Velocity Velocity float X, Y, Z
ParticleProperty.Type.Radius Radius float  
ParticleProperty.Type.Cluster Cluster int  
ParticleProperty.Type.Coordination Coordination int  
ParticleProperty.Type.StructureType Structure Type int  
ParticleProperty.Type.Identifier Particle Identifier int  
ParticleProperty.Type.StressTensor Stress Tensor float XX, YY, ZZ, XY, XZ, YZ
ParticleProperty.Type.StrainTensor Strain Tensor float XX, YY, ZZ, XY, XZ, YZ
ParticleProperty.Type.DeformationGradient Deformation Gradient float XX, YX, ZX, XY, YY, ZY, XZ, YZ, ZZ
ParticleProperty.Type.Orientation Orientation float X, Y, Z, W
ParticleProperty.Type.Force Force float X, Y, Z
ParticleProperty.Type.Mass Mass float  
ParticleProperty.Type.Charge Charge float  
ParticleProperty.Type.PeriodicImage Periodic Image int X, Y, Z
ParticleProperty.Type.Transparency Transparency float  
ParticleProperty.Type.DipoleOrientation Dipole Orientation float X, Y, Z
ParticleProperty.Type.DipoleMagnitude Dipole Magnitude float  
ParticleProperty.Type.AngularVelocity Angular Velocity float X, Y, Z
ParticleProperty.Type.AngularMomentum Angular Momentum float X, Y, Z
ParticleProperty.Type.Torque Torque float X, Y, Z
ParticleProperty.Type.Spin Spin float  
ParticleProperty.Type.CentroSymmetry Centrosymmetry float  
ParticleProperty.Type.VelocityMagnitude Velocity Magnitude float  
ParticleProperty.Type.Molecule Molecule Identifier int  
ParticleProperty.Type.AsphericalShape Aspherical Shape float X, Y, Z
ParticleProperty.Type.VectorColor Vector Color float R, G, B
ParticleProperty.Type.ElasticStrainTensor Elastic Strain float XX, YY, ZZ, XY, XZ, YZ
ParticleProperty.Type.ElasticDeformationGradient Elastic Deformation Gradient float XX, YX, ZX, XY, YY, ZY, XZ, YZ, ZZ
ParticleProperty.Type.Rotation Rotation float X, Y, Z, W
ParticleProperty.Type.StretchTensor Stretch Tensor float XX, YY, ZZ, XY, XZ, YZ
ParticleProperty.Type.MoleculeType Molecule Type int  
type_by_id(id)

Looks up the ParticleType with the given numeric ID in the types list. Raises a KeyError if the ID does not exist.

type_by_name(name)

Looks up the ParticleType with the given name in the types list. If multiple types exists with the same name, the first type is returned. Raises a KeyError if there is no type with such a name.

types

A (mutable) list of ParticleType instances.

Note that the particle types may be stored in arbitrary order in this list. Thus, it is not valid to use a numeric type ID as an index into this list.

class ovito.data.Bonds
Base class:ovito.data.DataObject

Stores the list of bonds between pairs of particles.

Typically, bonds are loaded from a simulation data file or are dynamically created by modifiers in a data pipeline. The following code example demonstrates how to access the bonds produced by a CreateBondsModifier:

from ovito.io import import_file
from ovito.modifiers import CreateBondsModifier
from ovito.data import Bonds, SimulationCell

# Load a set of atoms and create bonds between pairs of atoms that are within 
# a given cutoff distance of each other using the Create Bonds modifier.
pipeline = import_file('trajectory.dump')
pipeline.modifiers.append(CreateBondsModifier(cutoff = 3.4))

# Evaluate pipeline and retrieve the Bonds data object.
bonds = pipeline.compute().expect(Bonds)
print("Number of generated bonds: ", len(bonds))

The Bonds object can be used like a Numpy array of shape N x 2, where N is the number bonds:

for i in range(len(bonds)):
    print('Bond %i from atom %i to atom %i' % (i, bonds[i,0], bonds[i,1]))

Access to the bonds array is read-only. Each bond in the arrray is defined by a pair of 0-based indices into the particles array. Note that bonds are stored in the array in an arbitrary order. If you need to enumerate all bonds of a particular particle, you should use the BondsEnumerator utility class.

Bond properties

Like particles, bonds may be associated with bond properties. These properties are stored in separate data objects of type BondProperty, which are accessible through the DataCollection.bond_properties field of the data collection.

Bond display settings

Every Bonds object is associated with a BondsDisplay instance, which controls the visual appearance of the bonds in rendered images. It can be accessed through the display base class attribute:

# Configure visual appearance of bonds:
bonds.display.enabled = True
bonds.display.shading = BondsDisplay.Shading.Flat
bonds.display.width = 0.3

Computing bond vectors

Note that the Bonds class stores only the indices of the particles connected by bonds (the topology). Sometimes it may be necessary to determine the corresponding spatial bond vectors. They can be computed from the current positions of the particles:

# Computing bond vectors.
data = pipeline.compute()
bonds = data.expect(Bonds)
particle_positions = data.particle_properties['Position']
bond_vectors = particle_positions[bonds[:,1]] - particle_positions[bonds[:,0]]

Here, the first and the second column of the bonds array are used to index into the particle positions array. The subtraction of the two indexed arrays yields the list of bond vectors. Each vector in this list points from the first particle to the second particle of the corresponding bond.

Finally, we must correct for the effect of periodic boundary conditions when bonds cross the box boundaries. This is achieved by adding the product of the cell matrix and the stored PBC shift vectors to the bond vectors:

cell = data.expect(SimulationCell)
bond_vectors += numpy.dot(cell[:,:3], bonds.pbc_vectors.T).T

The PBC vectors array is transposed here to facilitate the transformation of the entire array of vectors with a single 3x3 cell matrix. In the code snippet we perform the following calculation for every bond (a, b) in parallel:

v = x(b) - x(a) + dot(H, pbc)

where H is the cell matrix and pbc is the bond’s PBC shift vector of the form (nx, ny, nz). See the pbc_vectors array for its meaning.

pbc_vectors

A NumPy array providing read access to the PBC shift vectors of bonds.

The returned array’s shape is N x 3, where N is the number of bonds. It contains the periodic shift vector for each bond.

A PBC shift vector consists of three integers, which specify how many times (and in which direction) the corresonding bond crosses the periodic boundaries of the simulation cell when going from the first particle to the second. For example, a shift vector (0,-1,0) indicates that the bond crosses the periodic boundary in the negative Y direction once. In other words, the particle where the bond originates from is located close to the lower edge of the simulation cell in the Y direction while the second particle is located close to the opposite side of the box.

The PBC shift vectors are important for visualizing the bonds between particles with wrapped coordinates, which are located on opposite sides of a periodic cell. When the PBC shift vector of a bond is (0,0,0), OVITO assumes that both particles connected by the bond are part of the same periodic image and the bond is rendered such that it directly connects the two particles without going through a cell boundary.

class ovito.data.ParticleType

Represents a particle or atom type. A ParticleType instance is always owned by a ParticleTypeProperty.

color

The display color to use for particles of this type.

id

The identifier of the particle type.

name

The display name of this particle type.

radius

The display radius to use for particles of this type.

class ovito.data.BondProperty
Base class:ovito.data.Property

Stores an array of per-bond values. This class derives from Property, which provides the base functionality shared by all property types in OVITO.

In OVITO’s data model, an arbitrary set of properties can be associated with bonds, each property being represented by a BondProperty object. A BondProperty is basically an array of values whose length matches the numer of bonds in the data collection (i.e. the length of the Bonds array).

BondProperty objects have the same fields and behave the same way as ParticleProperty objects. Both property classes derives from the common Property base class. Please see its documentation on how to access per-bond values.

The set of properties currently associated with the bonds is exposed by the DataCollection.bond_properties view, which allows accessing them by name and adding new properties.

Note that the topological definition of bonds, i.e. the connectivity between particles, is stored separately from the bond properties in the Bonds data object.

type

The type of the bond property (user-defined or one of the standard types). One of the following constants:

Type constant Property name Data type
BondProperty.Type.User (a user-defined property with a non-standard name) int/float
BondProperty.Type.BondType Bond Type int
BondProperty.Type.Selection Selection int
BondProperty.Type.Color Color float
BondProperty.Type.Length Length float
type_by_id(id)

Returns the BondType with the given numeric ID from the types list. Raises a KeyError if the ID does not exist.

type_by_name(name)

Returns the BondType with the given name from the types list. If multiple types exists with the same name, the first type is returned. Raises a KeyError if there is no type with such a name.

types

A (mutable) list of BondType instances.

Note that the bond types may be stored in arbitrary order in this type list.

class ovito.data.BondType

Represents a bond type. A BondType instance is always owned by a BondTypeProperty.

color

The display color to use for bonds of this type.

id

The identifier of the bond type.

name

The display name of this bond type.

class ovito.data.BondsEnumerator

Utility class that allows to efficiently iterate over the bonds connected to specific particles.

The constructor takes a Bonds object as input, which contains an unordered list of all bonds in a system. The BondsEnumerator will build a lookup table for quick enumeration of bonds of particular particles.

All bonds connected to a given particle can be visited using the bonds_of_particle() method.

Warning: Do not modify the Bonds instance while using the BondsEnumerator. Adding or deleting bonds would render the internal lookup table of the BondsEnumerator invalid.

Usage example

from ovito.io import import_file
from ovito.data import Bonds, BondsEnumerator

# Load a system of atoms and bonds.
pipeline = import_file('bonds.data.gz', atom_style = 'bond')
bonds = pipeline.source.expect(Bonds)
positions = pipeline.source.particle_properties['Position']

# Create bond enumerator object.
bonds_enum = BondsEnumerator(bonds)

# Loop over atoms.
for particle_index in range(len(positions)):
    # Loop over bonds of current atom.
    for bond_index in bonds_enum.bonds_of_particle(particle_index):
        atomA = bonds[bond_index, 0]
        atomB = bonds[bond_index, 1]
        assert(atomA == particle_index or atomB == particle_index)
        print("Atom %i has a bond to atom %i" % (atomA, atomB))
bonds_of_particle()

Returns an iterator that yields the indices of the bonds connected to the given particle. They can be used to index into the Bonds array.

class ovito.data.CutoffNeighborFinder(cutoff, data_collection)

A utility class that computes particle neighbor lists.

This class allows to iterate over the neighbors of each particle within a given cutoff distance. You can use it to build neighbors lists or perform other kinds of analyses that require neighbor information.

The constructor takes a positive cutoff radius and a DataCollection containing the input particle positions and the cell geometry (including periodic boundary flags).

Once the CutoffNeighborFinder has been constructed, you can call its find() method to iterate over the neighbors of a specific particle, for example:

from ovito.io import import_file
from ovito.data import CutoffNeighborFinder

# Load input simulation file.
pipeline = import_file("simulation.dump")
data = pipeline.compute()
positions = data.particle_properties['Position']

# Initialize neighbor finder object:
cutoff = 3.5
finder = CutoffNeighborFinder(cutoff, data)

# Loop over all particles:
for index in range(len(positions)):
    print("Neighbors of particle %i:" % index)
    # Iterate over the neighbors of the current particle:
    for neigh in finder.find(index):
        print(neigh.index, neigh.distance, neigh.delta, neigh.pbc_shift)

If you want to determine the N nearest neighbors of a particle, use the NearestNeighborFinder class instead.

find(index)

Returns an iterator over all neighbors of the given particle.

Parameters:index (int) – The index of the central particle whose neighbors should be iterated. Particle indices start at 0.
Returns:A Python iterator that visits all neighbors of the central particle within the cutoff distance. For each neighbor the iterator returns an object with the following attributes:
  • index: The index of the current neighbor particle (starting at 0).
  • distance: The distance of the current neighbor from the central particle.
  • distance_squared: The squared neighbor distance.
  • delta: The three-dimensional vector connecting the central particle with the current neighbor (taking into account periodicity).
  • pbc_shift: The periodic shift vector, which specifies how often each periodic boundary of the simulation cell is crossed when going from the central particle to the current neighbor.

Note that all periodic images of particles within the cutoff radius are visited. Thus, the same particle index may appear multiple times in the neighbor list of a central particle. In fact, the central particle may be among its own neighbors in a sufficiently small periodic simulation cell. However, the computed vector (delta) and PBC shift (pbc_shift) taken together will be unique for each visited image of a neighboring particle.

class ovito.data.NearestNeighborFinder(N, data_collection)

A utility class that finds the N nearest neighbors of a particle or a spatial location.

The constructor takes the (maximum) number of requested nearest neighbors, N, and a DataCollection containing the input particle positions and the cell geometry (including periodic boundary flags). N must be a positive integer not greater than 30 (which is the built-in maximum supported by this class).

Once the NearestNeighborFinder has been constructed, you can call its find() method to iterate over the sorted list of nearest neighbors of a specific particle, for example:

from ovito.io import import_file
from ovito.data import NearestNeighborFinder

# Load input simulation file.
pipeline = import_file("simulation.dump")
data = pipeline.source
positions = data.particle_properties['Position']

# Initialize neighbor finder object.
# Visit the 12 nearest neighbors of each particle.
N = 12
finder = NearestNeighborFinder(N, data)

# Loop over all input particles:
for index in range(len(positions)):
    print("Nearest neighbors of particle %i:" % index)
    # Iterate over the neighbors of the current particle, starting with the closest:
    for neigh in finder.find(index):
        print(neigh.index, neigh.distance, neigh.delta)

Furthermore, the class provides the find_at() method, which allows to query the nearest neighbors at an arbitrary spatial position (doesn’t have to be a physical particle):

# Find particles closest to some spatial point (x,y,z):
coords = (0, 0, 0)
for neigh in finder.find_at(coords):
    print(neigh.index, neigh.distance, neigh.delta)

Note, if you want to find all neighbor particles within a certain cutoff radius of a central particle, use the CutoffNeighborFinder class instead.

find(index)

Returns an iterator that visits the N nearest neighbors of the given particle in order of ascending distance.

Parameters:index (int) – The index of the central particle whose neighbors should be iterated. Particle indices start at 0.
Returns:A Python iterator that visits the N nearest neighbors of the central particle in order of ascending distance. For each visited neighbor the iterator returns an object with the following attributes:
  • index: The index of the current neighbor particle.
  • distance: The distance of the current neighbor from the central particle.
  • distance_squared: The squared neighbor distance.
  • delta: The three-dimensional vector connecting the central particle with the current neighbor (correctly taking into account periodic boundary conditions).

Note that several periodic images of the same particle may be visited. Thus, the same particle index may appear multiple times in the neighbor list of a central particle. In fact, the central particle may be among its own neighbors in a sufficiently small periodic simulation cell. However, the computed neighbor vector (delta) will be unique for each visited image of a neighboring particle.

The number of neighbors actually visited may be smaller than the requested number, N, if the system contains too few particles and has no periodic boundary conditions.

find_at(coords)

Returns an iterator that visits the N nearest particles around a spatial point given by coords in order of ascending distance. Unlike the find() method, which queries the nearest neighbors of a physical particle, the find_at() method allows searching for neareby particles at arbitrary locations in space.

Parameters:coords – A (x,y,z) coordinate triplet specifying the spatial location where the N nearest particles should be queried.
Returns:A Python iterator that visits the N nearest neighbors in order of ascending distance. For each visited particle the iterator returns an object with the following attributes:
  • index: The index of the current particle (starting at 0).
  • distance: The distance of the current neighbor from the query location.
  • distance_squared: The squared distance to the query location.
  • delta: The three-dimensional vector from the query point to the current particle (correctly taking into account periodic boundary conditions).

If there exists a particle that is exactly located at the query position given by coords, then it will be returned by this function. This is in contrast to the find() function, which does not visit the central particle itself.

The number of neighbors actually visited may be smaller than the requested number, N, if the system contains too few particles and has no periodic boundary conditions.

class ovito.data.ParticlePropertiesView(data_collection)

A dictionary view of all ParticleProperty objects in a DataCollection. An instance of this class is returned by DataCollection.particle_properties.

It implements the collections.abc.Mapping interface. That means it can be used like a standard read-only Python dict object to access the particle properties by name, e.g.:

data = pipeline.compute()

positions = data.particle_properties['Position']
has_selection = 'Selection' in data.particle_properties
name_list = data.particle_properties.keys()

New particle properties can be added with the create() method.

create(name, dtype=None, components=None, data=None)

Adds a new particle property to the data collection and optionally initializes it with the per-particle data provided by the data parameter. The method returns the new ParticleProperty instance.

The method allows to create standard as well as user-defined particle properties. To create a standard particle property, one of the standard property names must be provided as name argument:

num_particles = len(data.particle_properties['Position'])
colors = numpy.random.random_sample(size = (num_particles, 3))
data.particle_properties.create('Color', data=colors)

The size of the provided data array must match the number of particles in the data collection, i.e., it must equal the length of all other particle properties that already exist in the same data collection. In the example above the length of the existing Position particle property is used as reference to create an array of random RGB values with the correct size. Alternatively, you can set the property values after construction:

prop = data.particle_properties.create('Color')
with prop.modify() as arr:
    arr[...] = numpy.random.random_sample(size = prop.shape)

To create a user-defined particle property, use a non-standard property name:

values = numpy.arange(0, num_particles, dtype=int)
data.particle_properties.create('myint', data=values)

In this case the data type and the number of vector components of the new property are inferred from the provided data Numpy array. Providing a one-dimensional array creates a scalar property while a two-dimensional array creates a vectorial property. Alternatively, the dtype and components parameters can be specified explicitly if initialization of the property values should happen after property creation:

prop = data.particle_properties.create('myvector', dtype=float, components=3)
with prop.modify() as arr:
    arr[...] = numpy.random.random_sample(size = prop.shape)

If the property to be created already exists in the data collection, it is replaced with a new one. The existing per-particle data from the old property is however retained if data is None.

Note: If the data collection contains no particles yet, that is, if not even the Position standard property is present in the data collection, then the Position standard property can still be created from scratch as a first particle property by the create() method. The data array has to be provided in this case to specify the number of particles to create:

# An empty data collection to begin with:
data = ovito.pipeline.StaticSource()

# Create 10 particles with random xyz coordinates:
positions = numpy.random.random_sample(size = (10,3))
data.particle_properties.create('Position', data=positions)

After the initial Positions property has been created, the number of particles is now specified and any subsequently added properties must have the exact same length.

Parameters:
  • name – Either a standard property type constant or a name string.
  • data – An optional data array with per-particle values for initializing the new property. The size of the array must match the number of particles in the data collection and the shape must be consistent with the number of components of the property.
  • dtype – The element data type when creating a user-defined property. Must be either int or float.
  • components (int) – The number of vector components when creating a user-defined property.
Returns:

The newly created ParticleProperty

class ovito.data.BondPropertiesView(data_collection)

A dictionary view of all BondProperty objects in a DataCollection. An instance of this class is returned by DataCollection.bond_properties.

It implements the collections.abc.Mapping interface. That means it can be used like a standard read-only Python dict object to access the bond properties by name, e.g.:

data = pipeline.compute()

bond_lengths = data.bond_properties['Length']
has_selection = 'Selection' in data.bond_properties
name_list = data.bond_properties.keys()

New bond properties can be added with the create() method.

create(name, dtype=None, components=None, data=None)

Adds a new bond property to the data collection and optionally initializes it with the per-bond data provided by the data parameter. The method returns the new BondProperty instance.

The method can create standard and user-defined bond properties. To create a standard bond property, one of the standard property names must be provided as name argument:

num_bonds = len(data.expect(Bonds))
colors = numpy.random.random_sample(size = (num_bonds, 3))
data.bond_properties.create('Color', data=colors)

The size of the provided data array must match the number of bond in the data collection, i.e., it must equal the length of the Bonds objects as well as all other bond properties that already exist in the same data collection. Alternatively, you can set the property values after construction:

prop = data.bond_properties.create('Color')
with prop.modify() as arr:
    arr[...] = numpy.random.random_sample(size = prop.shape)

To create a user-defined bond property, use a non-standard property name:

values = numpy.arange(0, num_bonds, dtype=int)
data.bond_properties.create('myint', data=values)

In this case the data type and the number of vector components of the new property are inferred from the provided data Numpy array. Providing a one-dimensional array creates a scalar property while a two-dimensional array creates a vectorial property. Alternatively, the dtype and components parameters can be specified explicitly if initialization of the property values should happen after property creation:

prop = data.bond_properties.create('myvector', dtype=float, components=3)
with prop.modify() as arr:
    arr[...] = numpy.random.random_sample(size = prop.shape)

If the property to be created already exists in the data collection, it is replaced with a new one. The existing per-bond data from the old property is however retained if data is None.

Parameters:
  • name – Either a standard property type constant or a name string.
  • data – An optional data array with per-bond values for initializing the new property. The size of the array must match the number of bonds in the data collection and the shape must be consistent with the number of components of the property.
  • dtype – The element data type when creating a user-defined property. Must be either int or float.
  • components (int) – The number of vector components when creating a user-defined property.
Returns:

The newly created BondProperty