Tutorial

This notebook contains the tutorial to use nearest-neighbors models developed in macchiato.

We will first install the macchiato package and then import all the libraries that will be used along the following analysis.

[1]:
%%capture
!pip install macchiato
[2]:
import macchiato
macchiato.VERSION
[2]:
'0.1.1'
[3]:
import matplotlib.pyplot as plt
import MDAnalysis as mda
import numpy as np

import macchiato

To perform the analysis we need trajectories of the LiSi system. In this case we will download the data from this repo.

[4]:
!git clone https://github.com/fernandezfran/DFTB_LiSi_amorphous.git
Cloning into 'DFTB_LiSi_amorphous'...
remote: Enumerating objects: 37, done.
remote: Counting objects: 100% (37/37), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 37 (delta 14), reused 24 (delta 7), pack-reused 0
Receiving objects: 100% (37/37), 1.11 MiB | 4.33 MiB/s, done.
Resolving deltas: 100% (14/14), done.
[5]:
!ls DFTB_LiSi_amorphous/
CITATION.bib  LICENSE  README.md  structures
[6]:
!ls DFTB_LiSi_amorphous/structures/
Li128Si64.out  Li160Si64.out  Li240Si64.out  Li57Si64.out
Li128Si64.xyz  Li160Si64.xyz  Li240Si64.xyz  Li57Si64.xyz
Li13Si64.out   Li210Si64.out  Li36Si64.out   Li96Si64.out
Li13Si64.xyz   Li210Si64.xyz  Li36Si64.xyz   Li96Si64.xyz

In this path we have trajectories in xyz format and outputs with thermodynamic information and with the defined box. To read the xyz files we will use MDAnalysis and for the out files we will only search for the box dimensions with the following function.

[7]:
def get_boxes(md_out):
    with open(md_out, "r") as f:
        lines = f.readlines()

    boxes = []
    for i, line in enumerate(lines):
        if "Lattice" in line:
            lattice = [list(map(float, lines[i + j + 1].split())) for j in range(3)]
            boxes.append(
                np.array([lattice[0][0], lattice[1][1], lattice[2][2], 90, 90, 90])
            )

    return np.array(boxes, dtype=object)

We will also define the path to the files and the numbers of lithium and silicon atoms

[8]:
path = "DFTB_LiSi_amorphous/structures/"
nli = [13, 36, 57, 96, 128, 160, 210, 240]
nsi = 64
xs = np.array(nli) / nsi

Now we can read each alloy as a Universe of MDAnalysis

[9]:
universes = []
for n in nli:
    alloy = f"Li{n}Si{nsi}"
    u = mda.Universe(path + alloy + ".xyz")

    boxes = get_boxes(path + alloy + ".out")

    for box, ts in zip(boxes, u.trajectory):
        u.dimensions = box

    universes.append(u)

Chemical Shift Spectra

The following model is inferred from the behavior of the Chemical Shift Spectra in the crystalline alloys, which are used to test the package. The total spectra for each alloy comes from a contribution of each Li atom where its shift depends of the types of the Si nearest-neighbour, a contribution appears at 18 ppm when the Si atom is bonded to another Si atom and this contribution appears at 6 ppm if the Si atom is isolated. For more details, the reader is reffered to our paper.

[10]:
ppm = np.arange(40, -40, -0.1)
intensities = []
for x, u in zip(xs, universes):
    # center of each Li atom
    csc = macchiato.experiments.chemical_shift.ChemicalShiftCenters(u)
    csc.fit(ppm)

    # width of the spectra
    csw = macchiato.experiments.chemical_shift.ChemicalShiftWidth(csc)

    # we won't fit to experimental data here, we just define the
    # attrs related to the width of the peaks
    csw._popt = 1.0, 1.0, 1.0
    intensity = csw.predict(ppm)

    intensities.append(intensity)

Now we can plot for each alloy the intensity versus the ppm.

[11]:
fig, ax = plt.subplots()

ax.vlines(18.0, -0.1, 1.1, color="k", linestyles="dashed")
ax.text(25, 1.12, "bonded Si")
ax.vlines(6.0, -0.1, 1.1, color="k", linestyles="dashed")
ax.text(8, 1.12, "isolated Si")

for i, (x, intensity) in enumerate(zip(xs, intensities)):
    ax.plot(ppm, intensity + 0.1 * i, label=f"{x:.2f}")

ax.set_xlabel(r"$\delta$ [ppm]")
ax.set_xlim((40, -20))
ax.set_ylim((-0.1, 1.1))
ax.set_yticks([])
ax.legend(title=r"$x$ in Li$_x$Si", loc="center left", bbox_to_anchor=(1, 0.5))
[11]:
<matplotlib.legend.Legend at 0x7f5369bfeaa0>
_images/tutorial_18_1.png

We see the shift in the spectra due to the breaking of Si-Si bonds as Li concentration increase, as is observed in experiments. For more details and contrast with experiments, read our paper.

Mössbauer Spectroscopy

Instead of full Mössbauer spectra, we are interested in the splitting that appears for different alloys due to quadrupole interaction.

[12]:
means = []
for u in universes:
    me = macchiato.experiments.mossbauer.MossbauerEffect(u)
    means.append(np.mean(me.fit_predict(None)))

We can plot this splitting inferred from the experiment

[13]:
fig, ax = plt.subplots()

ax.scatter(xs, means)

ax.set_xlim((0, 4))
ax.set_ylim((0.2, 1.2))

ax.set_xlabel(r"$x$ in Li$_x$Si")
ax.set_ylabel(r"$\Delta$ in Mössbauer peaks (mm/s)")
[13]:
Text(0, 0.5, '$\\Delta$ in Mössbauer peaks (mm/s)')
_images/tutorial_24_1.png

This reproduce the experimental behavior and is presented in the paper.

Check the Jupyter Notebook pipeline in the paper folder which reproduces the results of the published article.