Core Workflow#

This tutorial covers the core workflow in automating NMR predictions via CASTEP, with examples on using CASTEP to calculate NMR parameters of two small cristobalite structures as well as an amorphous silica snapshot.

Overview#

Given a list of pymatgen structures (usually given in a .xyz file) and user-defined parameters, CASTEP is called with task: magres (see the CASTEP setup for how autoplex runs CASTEP) and produces .magres and .castep files. Afterwards magnetic shielding (MS) and electric field gradient (EFG) tensors are collected for all structures by writing to a single .extxyz file.

General workflow#

First we define our .param and .cell parameters using CastepMagresSetGenerator:

from autoplex.misc.castep.utils import CastepMagresSetGenerator

input_set_generator = CastepMagresSetGenerator(
    use_efg=True,
    user_param_settings={"xc_functional": "PBE", "cut_off_energy": 900.0},
    user_cell_settings={
        "species_pot": [
            ("O", "2|1.1|17|20|23|20:21(qc=8)"),
            ("Si", "3|1.8|5|6|7|30:31:32"),
        ],
        "kpoint_mp_spacing": 0.05,
    },
)

All allowed CASTEP keywords and definitions for setting up .param and .cell files can be found in the autoplex package at: /yourpath/autoplex/misc/castep/castep_keywords.json.

use_efg determines whether EFG tensors are calculated as well, if set to False it only calculates MS tensors.

Next, using input_set_generator we create a CastepMagresMaker:

from autoplex.misc.castep.jobs import CastepMagresMaker

magres_maker = CastepMagresMaker(
    input_set_generator=input_set_generator
)

Then we load our structures from e.g. structures.xyz:

from pymatgen.io.ase import AseAtomsAdaptor
from ase.io import read

structures = [AseAtomsAdaptor.get_structure(structure) for structure in read("structures.xyz", ":")]

This creates an array of pymatgen Structure objects which can then be passed into CastepMagresFlowMaker, which creates the Flow to be run locally or submitted to HPC (see the jobflow-remote setup):

from autoplex.misc.castep.flows import CastepMagresFlowMaker

flow = CastepMagresFlowMaker(magres_maker=magres_maker).make(structures)

Compressed castep.castep.gz and castep.magres.gz files are produced in the job’s CASTEP/ folder. CastepMagresFlowMaker.make() outputs a list of directories pointing at the CASTEP/ folder. Next, we collect from these directories using collect_nmr_data(), which reads castep.magres.gz from each directory (skipping runs that did not converge) and writes all structures to one extended XYZ file, containing NMR-labelled data:

from jobflow import Flow, run_locally
from autoplex.data.nmr.jobs import collect_nmr_data

collect = collect_nmr_data(nmr_dirs=flow.output, nmr_ref_file="nmr_ref.extxyz")
run_locally(Flow([flow, collect]), create_folders=True) # or jobflow-remote's submit_flow

Each atom carries its shielding and EFG tensors as the per-atom arrays REF_ms (ppm) and REF_efg (atomic units), each stored as 9 components (row by row), since extended XYZ cannot store 3×3 tensors. The file is written to the collector job’s directory; its path is returned as nmr_ref_dir, ready for processing.

Example systems#

We ran the workflow on cristobalite and amorphous SiO2 structures from the dataset of Ben Mahmoud et al., J. Chem. Phys. 163, 024118 (2025) (Zenodo, CC BY 4.0). The amorphous SiO2 snapshot is the first structure in test.xyz of that dataset, alpha and beta cristobalite structures are structures 1 and 3 in crystals.xyz of that dataset.

We used the settings shown above (PBE, 900 eV, shielding + EFG) with CASTEP 21.11 on 38–45 MPI processes on an HPC cluster, with additionally a fixed 3×3×3 k-point grid ("kpoint_mp_grid": [3, 3, 3] in user_cell_settings) for amorphous SiO2. The output and runtime of each structure is shown below:

Structure

Atoms

Si σiso (ppm)

O σiso (ppm)

Runtime

α-cristobalite

12

437.6

223.3

~5 min

β-cristobalite

24

459.1

230.9

~15 min

Amorphous SiO2

144

423.4 – 449.5

166.5 – 231.3

~42 h

Here σiso is the isotropic shielding in ppm, i.e. one third of the trace of the shielding tensor. It can be computed directly from the .extxyz file, using ase.io.read:

import numpy as np
from ase.io import read

structures_out = read("nmr_ref.extxyz", index=":")
ms = structures_out[0].arrays["REF_ms"].reshape(-1, 3, 3)
#Take e.g. ms of first structure, convert back to 3x3 tensor. ["REF_efg"] gives efg tensors (if present).

sigma_iso = np.trace(ms, axis1=1, axis2=2) / 3  
#Compute isotropic shielding for each atom in structure.
#NOTE: Atoms are listed in CASTEP’s order (grouped by element), which may differ from the order of the input structure. 

To obtain other NMR parameters from a .extxyz file, follow the similar process above to obtain MS and EFG tensors. They can then be analysed with e.g. Soprano (docs), a separate package that is not installed with autoplex. For large structures like amorphous SiO2, using fewer k-points can avoid large runtimes, at the cost of accuracy (using a single k-point reduces the runtime to ~1.5 hours in our case, with MAE in isotropic shielding ~0.05 vs ~0.007 ppm).

Example .magres files for the cristobalite structures are located in tests/test_data/castep/magres, and a snippet of the .magres output (see the magres file format) for amorphous SiO2 is provided below:

...
[magres_old]
============
Atom: O        1
============
O        1 Coordinates     10.723    1.886    8.916   A

TOTAL Shielding Tensor

             242.8870     46.2124     14.3341
              40.4691    230.3592      8.3922
              13.8238     10.3303    207.1829

O        1 Eigenvalue  sigma_xx     192.4360 (ppm)
O        1 Eigenvector sigma_xx       0.6695     -0.7202     -0.1820
O        1 Eigenvalue  sigma_yy     203.9185 (ppm)
O        1 Eigenvector sigma_yy       0.0379      0.2778     -0.9599
O        1 Eigenvalue  sigma_zz     284.0746 (ppm)
O        1 Eigenvector sigma_zz       0.7419      0.6357      0.2132

O        1 Isotropic:      226.8097 (ppm)
O        1 Anisotropy:      85.8973 (ppm)
O        1 Asymmetry:        0.2005

============
Atom: O        2
============
O        2 Coordinates      1.501    9.423    5.435   A

...

Next steps#

The core workflow can be extended to automatically train ML models for NMR prediction (Ben Mahmoud et al, J. Chem. Phys. 163, 024118 (2025)) in a similar workflow to RssMaker (see the RSS workflow). The outputted .magres files could be used in the same way for labelling the dataset, however there will be differences to RssMaker in data processing, generation and sampling as similar energy structures can have wildly different NMR parameters. This has not been implemented yet in autoplex.

Further reading#

  • CASTEP for NMR calculations (CCP-NC)

  • C. Bonhomme et al., “First-principles calculation of NMR parameters using the gauge including projector augmented wave method: a chemist’s point of view”, Chem. Rev. 112, 5733–5779 (2012), doi:10.1021/cr300108a

  • S. Sturniolo et al., “Visualization and processing of computed solid-state NMR parameters: MagresView and MagresPython”, Solid State Nucl. Magn. Reson. 78, 64–70 (2016), doi:10.1016/j.ssnmr.2016.05.004

  • C. Ben Mahmoud et al., J. Chem. Phys. 163, 024118 (2025), doi:10.1063/5.0274240