Skip to content

Instantly share code, notes, and snippets.

@emleddin
Last active August 5, 2024 22:04
Show Gist options
  • Select an option

  • Save emleddin/d4a5829634f7c79f1e90b5ef2b38dad6 to your computer and use it in GitHub Desktop.

Select an option

Save emleddin/d4a5829634f7c79f1e90b5ef2b38dad6 to your computer and use it in GitHub Desktop.
Potential energy surface graph with matplotlib

PES Graph

The Python3 script in this gist will render a reaction path along a potential energy surface (PES) in 3D.

It should work with Matplotlib 1.5.0 and newer (i.e., the script was first written based on mpl 1.5.0).

Python 3.8.5
>>> np.__version__
'1.16.6'
>>> matplotlib.__version__
'3.3.2'
#!/usr/bin/env python3
## Potential Energy Surface
## Based on Matplotlib 3D plotting documentation at
## https://matplotlib.org/stable/gallery/index.html#d-plotting
## Load the libraries
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
## Set up the figure
fig = plt.figure(figsize=(8,4),dpi=300)
## Change the subplot space to remove white space
fig.subplots_adjust(left=0, right=1.25, bottom=0, top=1)
ax = fig.add_subplot(111, projection="3d")
#ax = fig.gca(projection="3d")
## Create the PES gridpoints
## Need to be negative for Z so that the TS is high and the
## reactant and product are low
x, y = np.mgrid[-1:1:30j, -1:1:30j]
z = -np.cos(np.pi*-x)*np.cos(np.pi*-y)
## Create the line defining the reaction path
## X = zero, y = evenly spaced, z matches the PES points
x_path = np.zeros(shape=100)
y_path = np.linspace(-1,1, num=100)
z_path = -np.cos(np.pi*x_path)*np.cos(np.pi*y_path)
## Create the chain-of-replica beads at a 5th of the reaction path interval
## Give them a slight offset so they're visible
x_bead = np.zeros(shape=20)
y_bead = np.linspace(-1,1, num=20)
z_bead = -np.cos(np.pi*x_bead)*np.cos(np.pi*y_bead) + -0.025*np.pi
## The PES contour plot
## Break into 3 groups so that the chain-of-replica beads loook right!
ax.plot_surface(-x[0:14], -y[0:14], -z[0:14], cmap="winter_r", lw=-1,
rstride=1, cstride=1, alpha=0.75)
ax.plot_surface(-x[13:17], -y[13:17], -z[13:17], cmap="winter_r", lw=-1,
rstride=1, cstride=1, alpha=0.75)
ax.plot_surface(-x[16:], -y[16:], -z[16:], cmap="winter_r", lw=-1,
rstride=1, cstride=1, alpha=0.75)
## The line defining the reaction path
ax.plot(-x_path, -y_path, -z_path, lw=2, c='black')
## Points for Chain-of-replica beads
ax.scatter(-x_bead, -y_bead, -z_bead, zdir='z', s=55, c='darkorange',
depthshade=False, alpha=1)
## Wire mesh
# ax.plot_wireframe(-x, -y, -z, rstride=1, cstride=1)
# Get current rotation angle and elevation
# print(ax.azim)
# print(ax.elev)
# Set viewpoint (GUI gives values in top right corner)
ax.view_init(azim=-40, elev=63.)
## Option to save a series of images at various rotations (to make a gif???)
# for ii in xrange(0,360,1):
# ax.view_init(elev=10., azim=ii)
# savefig("movie%d.png" % ii)
## Add the axis labels using the math format for subscript
ax.set_xlabel('$q_1$', labelpad=-5)
ax.set_ylabel('$q_2$', labelpad=-5)
ax.set_zlabel(r'Energy', labelpad=-10)
ax.get_xaxis().set_ticklabels([])
ax.get_yaxis().set_ticklabels([])
ax.set_zticklabels([])
## Show the plot so you can orient it properly (and re-save with GUI)
# plt.show()
## Save the figure
fig.savefig('PES_w_beads_image.png', dpi=fig.dpi, bbox_inches='tight',
transparent=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment