Using the HOOMD molucule dynamics software, I tried to reproduce the phase change of Argon. The model is a Lennard-Jones system with the constants defining the interaction from here: http://complex.elte.hu/~csabai/szamszim/simLec5.pdf .
First I tried to create a box of frozen Argon and warm it up, because trying to freeze a substance we always experience some overfreezing which makes it hard to detect the freezing point. In the other direction, this problem should not be that bad. But this way I could not reproduce an exact freezing point, the phase change was rather continuus. So in the end I'm showing system which were frozen with different freezing velocities.
I tried to analyze the phase change with constant pressure, so during the simulation I constantly downsize the box, to keep up with the changing temperature. But even with constant temperature the pressure has strong fluctuations, and it is not really possible to keep it very stable.
The exact temperature of phase change is not revealed in this simulation because cooling is too fast. I have tried to exactly simulate the boiling point too, but the strong fluctuation in the pressure, and the very-very slow convergence at a given temperature close to the boiling point made it unfeasible to exactly measure the real temperature of the phase transition.
Installing hoomd
conda config --add channels glotzer
conda install hoomd
Installing MDAnalysis
pip install mdanalysis
import MDAnalysis
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd
%matplotlib inline
def write_hoomd_script(n_steps):
with open('nsteps'+str(n_steps)+'.hoomd','w') as f_h:
f_h.write("""
#physical params
N=1000 #number of parts
L0=65 #box sizes
L_end=35
n_timesteps="""+str(n_steps)+""" #overall number of timesteps
dt=0.01 #integration dt
change_period=1e2 #changing properties at every period timestep
write_period=1e2 #writing at every period timestep
eps=1.0 # 1 is energy unit -> then T=E/kb= E * 119K
sigma=1.0
r_cut=3.0
T_0=90.0/119.8 #max temp in energy units
T_end=0.0/119.8 #min temp
#filenames
prefix='dump_dcd'
############################################
############################################
############################################
from hoomd_script import *
context.initialize()
# create random particles of name A
init.create_random(N=N, box=data.boxdim(L=L0), name='A')
# specify Lennard-Jones interactions between particle pairs
lj = pair.lj(r_cut=r_cut)
lj.pair_coeff.set('A', 'A', epsilon=eps, sigma=sigma)
# integrate mode
all = group.all();
integrate.mode_standard(dt=dt)
#recommended for rescale_temp
integrate.nve(group=all)
#integrate.nvt(group=all, T=1.2, tau=0.5)
# dump an xmle file
dump.xml(filename=prefix+'.xml',vis=True,velocity=True)
# dump a .dcd file for the trajectories
dump.dcd(filename=prefix+'.dcd', period=write_period)
#Cooling: goes from T_max at time step 0 to T_min at the end
update.rescale_temp(period=change_period,T=variant.linear_interp([(0, T_0), (n_timesteps, T_end)]))
update.box_resize(period=change_period, L = variant.linear_interp([(0,L0), (n_timesteps, L_end)]))
# analyze
analyze.log(filename='results"""+str(n_steps)+""".log', quantities=['pressure', 'temperature', 'kinetic_energy', 'potential_energy'],
period=write_period, header_prefix='#')
# run
run(n_timesteps)
""")
write_hoomd_script(int(1e4))
write_hoomd_script(int(1e5))
write_hoomd_script(int(1e6))
%%bash
rm dump_dcd.* hoomd.log
time hoomd nsteps10000.hoomd >> hoomd.log
rm dump_dcd.*
time hoomd nsteps100000.hoomd >> hoomd.log
rm dump_dcd.*
time hoomd nsteps1000000.hoomd >> hoomd.log
result4=pd.read_csv('results10000.log',sep='\t').iloc[1:,:] #skip first data
result5=pd.read_csv('results100000.log',sep='\t').iloc[1:,:] #skip first data
result6=pd.read_csv('results1000000.log',sep='\t').iloc[1:,:] #skip first data
e_unit=1.65e-21
beta=119.8
d_unit=3.41e-10
p_unit=e_unit/d_unit**3
m_unit=1.660538921e-27
result4['temperature']*=beta
result4['pressure']*=p_unit
speed4=90/(1e4*0.01*np.sqrt(m_unit*d_unit**2/e_unit))
result5['temperature']*=beta
result5['pressure']*=p_unit
speed5=90/(1e5*0.01*np.sqrt(m_unit*d_unit**2/e_unit))
result6['temperature']*=beta
result6['pressure']*=p_unit
speed6=90/(1e6*0.01*np.sqrt(m_unit*d_unit**2/e_unit))
In the liquid state, the potential energy should be much higher than in the random gas state. With more points the cooling process becomes slower and the system has more time to start to form the liquid state. Unfortunately even the 1 million point simulation is actually very-very fast, and runs quite long. Running a simulation which condesates close to the theoretical boiling point looks hopeless.
fig,ax=plt.subplots()
fig.set_size_inches(12,9)
ax.plot(result4['temperature'],result4['potential_energy'],
'.',mec='none',label='%e' % speed4 + r' $[K/s]$ (10K points)')
ax.plot(result5['temperature'],result5['potential_energy'],
'.',mec='none',label='%e' % speed5 + r' $[K/s]$ (100K points)')
ax.plot(result6['temperature'],result6['potential_energy'],
'.',mec='none',label='%e' % speed6 + r' $[K/s]$ (1M points)')
ax.axvline(83.81,c='m',lw=4,linestyle='dashed',label='theoretical melting point')
ax.axvline(87.302,c='orange',lw=4,linestyle='dashed',label='theoretical boiling point')
ax.legend(loc='best',fancybox=True,fontsize=16)
ax.set_ylabel(r'Potential energy [ $\epsilon$ ]',fontsize=16)
dump=ax.set_xlabel(r'$T [K]$',fontsize=16)
fig,ax=plt.subplots()
fig.set_size_inches(12,9)
ax.plot(result6['temperature'],result6['pressure'],'.',mec='none',label='')
ax.axhline(1e5,lw=4,label='atmospheric pressure',c='g')
ax.legend(loc='best',fancybox=True,fontsize=16)
ax.set_xlabel(r'$T [K]$',fontsize=16)
ax.set_ylabel(r'$p [Pa]$',fontsize=16)
ax.set_yscale('log')
dump=ax.set_ylim(1e3,1e6)
u = MDAnalysis.Universe('dump_dcd.xml','dump_dcd.dcd')
atoms=u.select_atoms('type A')
fig = plt.figure(figsize=(16,9))
u.trajectory[0]
ax = fig.add_subplot(121, projection='3d')
plt.axis('off')
ax.set_title('gas phase')
dump=ax.plot(atoms.positions[:,0],atoms.positions[:,1],atoms.positions[:,2],'.')
u.trajectory[-1]
ax = fig.add_subplot(122, projection='3d')
plt.axis('off')
ax.set_title('condensed phase')
dump=ax.plot(atoms.positions[:,0],atoms.positions[:,1],atoms.positions[:,2],'.')
I have managed to reconstruct the phase change of Argon, and the results look credible. Measuring the exact boiling point looks unfeasible because of the high pressure fluctuations, and the slow convergence around the boiling point. With much more particles, the pressure fluctuation could be remedied, and with much stronger hardware (i was running it on 1 core of an intel i7 ), the long simulations can also be carried out.