exma quick start

In this tutorial we will take the typical molecular dynamics case of a Lennard-Jones (LJ) fluid, in its solid and liquid phase, and we will see how to obtain different properties of them using this library.

This first part of the code will be common to all three sections. We are going to import the necessary libraries and define the variables that we will use next.

[1]:
import exma
import matplotlib.pyplot as plt
import numpy as np

We are going to define the strings that will direct us to where the files with the paths are located (assuming we are in exma/docs/source).

[2]:
fsolid = "tutorial_data/solid.xyz"
fliquid = "tutorial_data/liquid.xyz"

We have these variables defined as strings and we can read them with exma.read_xyz function. These trajectories were generated with a homemade code, in both cases there are 201 frames and 500 atoms.

[3]:
frames_solid = exma.read_xyz(fsolid, ftype="image")
frames_liquid = exma.read_xyz(fliquid, ftype="image")

Next we define the parameters of the simulation cell for each case (this is necessary since the file is xyz, if it was lammpstrj we could skip this cell).

[4]:
solid_box = np.full(3, 7.46901)
liquid_box = np.full(3, 8.54988)

In this case the distances are given in Lennard-Jones units.

Mean Square Displacement (MSD)

The mean square displacement (MSD) is a measure of the deviation of the position of the particles with respect to a reference positions over time. From it, it is possible to obtain, through a linear regression, the trace diffusion coefficient. For more information you can start reading the Wikipedia article of mean square displacement.

We start by importing the MeanSquareDisplacement class from exma.

[5]:
from exma import MeanSquareDisplacement

As in every Molecular Dynamics Observable (MDObservable), we have dedicated calculate and plot methods.

[6]:
# for both structures we discard the first 10 equilibration frames
solid_msd = MeanSquareDisplacement(frames_solid, 0.05, start=10)
liquid_msd = MeanSquareDisplacement(frames_liquid, 0.05, start=10)

At this point we just instantiate the class, we are able to calculate, for which it is necessary to pass the optional argument of the cell length in each direction.

[7]:
solid_msd.calculate(box=solid_box)
[7]:
t msd
0 0.00 0.000000
1 0.05 0.015579
2 0.10 0.012065
3 0.15 0.014467
4 0.20 0.014905
... ... ...
186 9.30 0.014900
187 9.35 0.015280
188 9.40 0.014324
189 9.45 0.014695
190 9.50 0.013101

191 rows × 2 columns

[8]:
liquid_msd.calculate(box=solid_box)
[8]:
t msd
0 0.00 0.000000
1 0.05 0.249304
2 0.10 0.420926
3 0.15 0.574253
4 0.20 0.730512
... ... ...
186 9.30 28.461536
187 9.35 28.706202
188 9.40 28.750016
189 9.45 28.760514
190 9.50 29.176495

191 rows × 2 columns

We can see directly from the numbers how in the liquid phase a larger quadratic displacement is obtained already in the first steps. But to be more illustrative, we can plot both curves on the same graph, using plt.gca() of matplotlib.

[9]:
ax = plt.gca()

# we pass the same axis to both plots and define the labels offered by the wrapper to the plot function
solid_msd.plot(ax=ax, plot_kws={"label": "solid"})
liquid_msd.plot(ax=ax, plot_kws={"label": "liquid"})

# we add the legend to the plot
plt.legend()
plt.show()
_images/tutorial_20_0.png

We obtain the expected response for a LJ fluid where the liquid phase diffuses with the expected linear behaivor and the solid phase does not diffuse.

Radial Distribution Function (RDF)

The pair radial distribution function (RDF), g(r), characterizes the local structure of a fluid, and describes the probability to find an atom in a shell at distance r from a reference atom. This quantity is calculated as the ratio between the average density at distance r from the reference atom and the density at that same distance of an ideal gas. For more information you can start reading the Wikipedia article of radial distribution function.

We start importing the RadialDistributionFunction class from exma.

[10]:
from exma import RadialDistributionFunction

And the mode of use is quite analogous to that of the MSD, some parameters of the inizializer are changed.

[11]:
# for both structures we discard the first 10 equilibration frames
solid_rdf = RadialDistributionFunction(frames_solid, start=10, rmax=solid_box[0] / 2)
liquid_rdf = RadialDistributionFunction(frames_liquid, start=10, rmax=liquid_box[0] / 2)

In this case we declare that the RDF is calculated up to half the distance from the box, i.e. atoms at a greater distance are ignored.

[12]:
solid_rdf.calculate(box=solid_box)
[12]:
r rdf
0 0.018673 0.000000
1 0.056018 0.000000
2 0.093363 0.000000
3 0.130708 0.000000
4 0.168053 0.000000
... ... ...
95 3.566452 0.779633
96 3.603797 0.791239
97 3.641142 0.879388
98 3.678487 1.032154
99 3.715832 1.297041

100 rows × 2 columns

[13]:
liquid_rdf.calculate(box=liquid_box)
[13]:
r rdf
0 0.021375 0.000000
1 0.064124 0.000000
2 0.106874 0.000000
3 0.149623 0.000000
4 0.192372 0.000000
... ... ...
95 4.082568 1.017443
96 4.125317 1.013300
97 4.168067 1.007181
98 4.210816 1.000252
99 4.253565 0.993149

100 rows × 2 columns

As before, we can obtain the corresponding graph.

[14]:
ax = plt.gca()

# we pass the same axis to both plots and define the labels offered by the wrapper to the plot function
solid_rdf.plot(ax=ax, plot_kws={"label": "solid"})
liquid_rdf.plot(ax=ax, plot_kws={"label": "liquid"})

# we add the legend to the plot
plt.legend()
plt.show()
_images/tutorial_32_0.png

We get the expected results. For the solid phase we have the defined peaks of an fcc crystal with noise given by the temperature and for the liquid phase we get the usual behavior of a liquid. For both systems we have that the g(r) oscillates around 1 for larger distances.

Coordination Number (CN)

The coordination number (CN), also called ligancy, of a given atom in a chemical system is defined as the number of atoms, molecules or ions bonded to it. This quantity is calculated considered the number of neighbors surrounding a given atom type a cutoff distance.

From the previous graph we can define the cut-off radius to consider only the first neighbors.

[15]:
solid_rcut = 1.29
liquid_rcut = 1.56

Now we import the CoordinationNumber class.

[16]:
from exma import CoordinationNumber

We have the same conduct as in the previos classes.

[17]:
solid_cn = CoordinationNumber(frames_solid, solid_rcut, start=10)
liquid_cn = CoordinationNumber(frames_liquid, liquid_rcut, start=10)

In this case there is no point in making a plot, so the calculate method directly gives us the mean and standard deviation of the number of coordination calculated over all the production frames.

[18]:
solid_cn.calculate(box=solid_box)
[18]:
(12.006931937172777, 0.007931589852952529)
[19]:
liquid_cn.calculate(box=liquid_box)
[19]:
(12.220104712041884, 0.10884996812826905)

For both cases we have roughly the same result of the CN close to 12 typical value of a compact packing structure, as is the fcc crystal and its amorphization.

If we want to access the coordination number of each atom we can use the function to_dataframe.

[ ]: