%%capture
! pip install -U ovito
Recipe 2: Plotting RDF and BAD with rendered scene
How to Script with OVITO
Frequently you’ll want to know what the distribution is of structural quantities. For example you may want to know where the average first nearest neighbor is or the bond-angle formed by a pair os nearest neighbors. This reciepe will allow you to 1.) perform the analysis, 2.) add the plots to the rendered scene.
Import OVITO modules and Matplotlib
from ovito.io import import_file
from ovito.modifiers import CreateBondsModifier, BondAnalysisModifier, CoordinationAnalysisModifier
from ovito.vis import Viewport, PythonViewportOverlay
from ovito.vis import ViewportOverlayInterface
from ovito.vis import TachyonRenderer, OSPRayRenderer, OpenGLRenderer
from PySide6.QtGui import QImage, QPainter
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
Step 1: Download LAMMPS dump and create OVITO pipeline
As usually load your file into a OVITO pipeline and the nadd it to scene.
%%capture
! wget 'https://drive.google.com/uc?id=1GZS1AOqJHYe4TVgqfkWR1YbRdYrPBPqB&export=download' -O dump.nacl-melt
= import_file('dump.nacl-melt')
pipeline pipeline.add_to_scene()
Step 2: Functions for BAD and RDF
Plotting using matplotlib is handled as normal python code. The main thing is using the OVITO calculated data (see below). Here we define the matplotlib plotting functions for the radial distribution function (RDF) and the bond-angle distribution (BAD). These will get used by the Overlay
class.
Note, that if you know something about the range of values for the y
and x
axis, you would modify that here, so that during animations the “flickering” of the plots is limited. I haven’t done so here.
def plot_bond_angle_distribution(data):
=(5/2,3.5/2))
plt.figure(figsize0], data[:,1], width=data[1,0]-data[0,0], align='center')
plt.bar(data[:,'Bond angle (degrees)', fontsize=8)
plt.xlabel('Count', fontsize=8)
plt.ylabel(= plt.gca()
ax ='both', which='major', labelsize=6, direction='in')
ax.tick_params(axis0.0,300)
ax.set_ylim(
ax.xaxis.set_major_locator(ticker.AutoLocator())
ax.yaxis.set_major_locator(ticker.AutoLocator())
plt.tight_layout()'bond_angle_plot.png', dpi=300, transparent=True)
plt.savefig(
plt.close()
def plot_radial_distribution_function(data):
=(5/2, 3.5/2))
plt.figure(figsize0], data[:,1])
plt.plot(data[:,'Distance', fontsize=8)
plt.xlabel('g(r)', fontsize=8)
plt.ylabel(= plt.gca()
ax ='both', which='major', labelsize=6, direction='in')
ax.tick_params(axis0.0,None)
ax.set_xlim(0.0,5.0)
ax.set_ylim(
plt.tight_layout()'rdf_plot.png', dpi=300, transparent=True)
plt.savefig( plt.close()
Step 3: Class for Viewport Overlay of RDF and BAD plots
Now we define a Overlay
class that has a method called render
. This gets called when a Viewport
object is rendered.
Notice that we have our pipeline.compute
here, so all the modifiers that are set in a pipelien get called. We also cal our plotting functions and then open a canvas to add the plots to the scene.
class Overlay(ViewportOverlayInterface):
def render(self, canvas, **kwargs):
= kwargs['frame']
frame = pipeline.compute(frame=frame)
data 'coordination-rdf'].xy())
plot_radial_distribution_function(data.tables['bond-angle-distr'].xy())
plot_bond_angle_distribution(data.tables[with canvas.qt_painter() as painter:
= QImage('bond_angle_plot.png')
bond_angle_image = QImage('rdf_plot.png')
rdf_image # Define the position and size of the images on the canvas
= (1.0, 1.0) # right-top corner
bond_angle_pos = (1.0, 0.0) # right-bottom corner
rdf_pos = (0.45, 0.45) # fractional coordinates
size =bond_angle_pos, size=size, anchor="north east")
canvas.draw_image(bond_angle_image, pos=rdf_pos, size=size, anchor="south east") canvas.draw_image(rdf_image, pos
Step 4: OVITO Modifiers
OVITO modifiers are analysis or property calculation/setting routines. For anything you want to do, in terms of analyzing your data from a atomistic simulation, you will use a modifier which is appended to the pipeline via pipeline.modifiers.append(...)
. Here the modifiers we use are the BondAnalysisModifier
and CoordinationAnalysisModifier
to grab the data for plotting the BAD and RDF.
# Create bonds if not already in the dump
=3.5))
pipeline.modifiers.append(CreateBondsModifier(cutoff
# Calculate the bond-angle distribution
= BondAnalysisModifier()
bond_angle_modifier
pipeline.modifiers.append(bond_angle_modifier)
# Calculate the radial distribution function
= CoordinationAnalysisModifier(cutoff=6.5, number_of_bins=100)
rdf_modifier pipeline.modifiers.append(rdf_modifier)
Step 5: Viewport and Render
As usually we need to create our Viewport
object and then configure it based on how we want to view the scene.
Then because we are overlaying additional images that have been created by matplotlib we need to provide our overlay object to the viewport. Then we add the pipeline to the scene.
= Viewport(type=Viewport.Type.Ortho, camera_dir=(2, 1, -1))
viewport
viewport.zoom_all()= [15, -1, 15]
viewport.camera_pos
= PythonViewportOverlay(delegate=Overlay())
overlay
viewport.overlays.append(overlay)
; pipeline.add_to_scene()
We just use the code below to display an image in a notebook.
from IPython.display import Image
Render Single Frame
# Render image
=9
frame= f'nacl_melt_rendered_frame_{frame}.png'
fstatic =(800, 600),
viewport.render_image(size=frame,
frame=fstatic,
filename=OSPRayRenderer()); renderer
open(fstatic, 'rb').read()) Image(
Animation
= 'nacl_melt_animation.gif'
fanim =(800, 600),
viewport.render_anim(size=5,
every_nth=fanim,
filename=OSPRayRenderer(),
renderer=10) fps
Citation
@online{bringuier2024,
author = {Bringuier, Stefan},
publisher = {Github Pages},
title = {Recipe 2: {Plotting} {RDF} and {BAD} with Rendered Scene},
date = {2024-02-22},
url = {https://stefanbringuier.github.io/HowToSOVITO},
langid = {en}
}