Note
Go to the end to download the full example code.
Equilibrating an MD box of acetonitrile#
Goals#
In this tutorial we learn how to perform a thermal equilibration of a box of acetonitrile molecules using ASE. We will:
build a coarse-grained CH₃–C≡N triatomic model.
set up a periodic box at the experimental density (298 K).
apply rigid-molecule constraints.
use the ACN force field.
equilibrate with Langevin dynamics.
scale small box of 27 molecules to 216 molecules.
ACN model#
The acetonitrile force field implemented in ASE (ase.calculators.acn)
is an interaction potential between three-site linear molecules. The atoms
of the methyl group are treated as a single site centered on the methyl
carbon (hydrogens are not explicit). Therefore:
assign the methyl mass to the outer carbon (
m_me),use the atomic sequence Me–C–N repeated for all molecules,
keep molecules rigid during MD with
FixLinearTriatomic.
import matplotlib.pyplot as plt
import numpy as np
import ase.units as units
from ase import Atoms
from ase.calculators.acn import ACN, m_me, r_cn, r_mec
from ase.constraints import FixLinearTriatomic
from ase.io import Trajectory
from ase.md import Langevin
from ase.visualize.plot import plot_atoms
Step 1: molecule#
Build one CH3–C≡N as a linear triatomic “C–C–N”. The first carbon is the methyl site; we assign it the CH3 mass. Rotate slightly to avoid perfect alignment with the cell axes.
pos = [[0, 0, -r_mec], [0, 0, 0], [0, 0, r_cn]]
atoms = Atoms('CCN', positions=pos)
atoms.rotate(30, 'x')
masses = atoms.get_masses()
masses[0] = m_me
atoms.set_masses(masses)
mol = atoms.copy()
mol.set_pbc(False)
mol.set_cell([20, 20, 20])
mol.center()
fig, ax = plt.subplots(figsize=(4, 4))
plot_atoms(
mol,
ax=ax,
rotation='30x,30y,0z',
show_unit_cell=0,
radii=0.75,
)
ax.set_axis_off()
plt.tight_layout()
plt.show()

Step 2: Set up small box of 27-molecules#
Match density 0.776 g/cm^3 at 298 K. Compute cubic box length L from mass and density. Build 3×3×3 supercell and enable PBC.
density = 0.776 / 1e24 # g / Å^3
L = ((masses.sum() / units.mol) / density) ** (1 / 3.0)
atoms.set_cell((L, L, L))
atoms.center()
atoms = atoms.repeat((3, 3, 3))
atoms.set_pbc(True)
box27 = atoms.copy()
fig, ax = plt.subplots(figsize=(5, 5))
plot_atoms(
box27,
ax=ax,
rotation='35x,35y,0z',
show_unit_cell=2,
radii=0.75,
)
ax.set_axis_off()
plt.tight_layout()
plt.show()

Step 3: Set constraints#
Keep each “C–C–N” rigid during MD using FixLinearTriatomic.
Step 4: MD run for 27-molecules system#
Assign ACN with cutoff = half the smallest box edge. Langevin MD at 300 K, 1 fs timestep. Save a frame every step.
True
Step 5: scale system size to 216 molecules#
Repeat 2×2×2 to reach 216 molecules. Reapply constraints and update the ACN cutoff for the new cell.
Step 6: MD run for 216-molecules system#
tag = 'acn_216mol_300K'
md = Langevin(
atoms,
2 * units.fs,
temperature_K=300,
friction=0.01,
logfile=tag + '.log',
)
traj = Trajectory(tag + '.traj', 'w', atoms)
md.attach(traj.write, interval=10)
times_ps, epots, ekins, etots, temps = [], [], [], [], []
sample_interval = 10 # sample every 10 MD steps for lighter plots
def sample():
# Time in ps (same as MDLogger: dyn.get_time() / (1000 * units.fs))
t_ps = md.get_time() / (1000.0 * units.fs)
ep = atoms.get_potential_energy() # eV total
ek = atoms.get_kinetic_energy() # eV total
T = atoms.get_temperature() # K
times_ps.append(t_ps)
epots.append(ep)
ekins.append(ek)
etots.append(ep + ek)
temps.append(T)
# initial sample at t=0
sample()
md.attach(sample, interval=sample_interval)
md.run(1000) # 6 ps @ 2 fs
True
Plot Instantaneous temperature vs time. Does the system equilibrated well? What is the average temperature? Should we run longer simulations?

Next steps#
View trajectories:
Plot other thermodynamic quantities (p.e., k.e., and total energy).
Total running time of the script: (4 minutes 22.098 seconds)