castep_outputs demo

castep_outputs demo#

In this Jupyter Notebook we’ll look at how to use the castep_outputs module to process CASTEP output files.

First we’ll set up some data to process.

import pathlib

data_path = pathlib.Path.cwd() / "example_data"
examples = data_path / "pair-pot-lj"

param_file = examples.with_suffix(".param")
param_file
PosixPath('/home/runner/work/castep_outputs/castep_outputs/docs/example_data/pair-pot-lj.param')
print(param_file.read_text())
task : moleculardynamics # Comment

md_delta_t = 5 fs
md_ensemble = nvt
md_thermostat = langevin
md_ion_t = 50 fs
md_num_iter = 10
md_sample_iter = 5

md_temperature 100 K


cut_off_energy : 100 eV
write_checkpoint : none
write_bib : false

%block DEVEL_CODE
PP=T
# Another comment
PP: LJ=T LJ_EPS_Ar=120. LJ_SIG_Ar=0.3405 :ENDPP
%endblock DEVEL_CODE
popn_write : minimal

It is possible to use castep_outputs directly by importing the top-level castep_outputs and using parse_single to parse a single datafile.

import castep_outputs

lj_data = castep_outputs.parse_single(param_file)
lj_data
[{'task': 'moleculardynamics',
  'md_delta_t': (5, 'fs'),
  'md_ensemble': 'nvt',
  'md_thermostat': 'langevin',
  'md_ion_t': (50, 'fs'),
  'md_num_iter': 10,
  'md_sample_iter': 5,
  'md_temperature': (100, 'K'),
  'cut_off_energy': (100, 'eV'),
  'write_checkpoint': 'none',
  'write_bib': False,
  'devel_code': {'pp': {'lj': True, 'lj_eps_ar': 120.0, 'lj_sig_ar': 0.3405},
   '_pp': True},
  'popn_write': 'minimal'}]

Note that many of the data are parsed into the appropriate Python types. e.g logicals to bools and numeric types to their most likely type.

parse_single works by determining the type of parser from the file extension. If we don’t have a file extension, the parser will fail.

from io import StringIO
cell_file = examples.with_suffix(".cell")

# Read the file into a file-like buffer, which doesn't contain the extension or details.
file = StringIO(cell_file.read_text())

try:
    castep_outputs.parse_single(file)
except ValueError as err:
    print(f"Oh no! {err=}")
Oh no! err=ValueError('Unable to determine parser. Please specify through arguments.')

To rectify this, we can explicitly pass the parser (by file extension) we want to use.

# Rewind to the beginning of the buffer
file.seek(0)
castep_outputs.parse_single(file, parser="cell")
[{'lattice_cart': {'data': [(17.395297, 0.0, 0.0),
    (0.0, 17.395297, 0.0),
    (0.0, 0.0, 17.395297)]},
  'positions_abs': {('Ar', 1): {'pos': (4.023973, 2.257202, 2.476523),
    'units': 'Ang'},
   ('Ar', 2): {'pos': (4.29215, 0.741938, -1.542184), 'units': 'Ang'},
   ('Ar', 3): {'pos': (0.834362, 4.894828, 5.031917), 'units': 'Ang'},
   ('Ar', 4): {'pos': (-7.896037, -0.914764, -0.186325), 'units': 'Ang'},
   ('Ar', 5): {'pos': (-5.92112, -1.280009, 5.918613), 'units': 'Ang'}},
  'fix_all_cell': True,
  'kpoint_mp_spacing': (1.0, '1/ang')}]

Or pass it as the callable directly.

# Rewind to the beginning of the buffer
file.seek(0)
castep_outputs.parse_single(file, parser=castep_outputs.parse_cell_file)
[{'lattice_cart': {'data': [(17.395297, 0.0, 0.0),
    (0.0, 17.395297, 0.0),
    (0.0, 0.0, 17.395297)]},
  'positions_abs': {('Ar', 1): {'pos': (4.023973, 2.257202, 2.476523),
    'units': 'Ang'},
   ('Ar', 2): {'pos': (4.29215, 0.741938, -1.542184), 'units': 'Ang'},
   ('Ar', 3): {'pos': (0.834362, 4.894828, 5.031917), 'units': 'Ang'},
   ('Ar', 4): {'pos': (-7.896037, -0.914764, -0.186325), 'units': 'Ang'},
   ('Ar', 5): {'pos': (-5.92112, -1.280009, 5.918613), 'units': 'Ang'}},
  'fix_all_cell': True,
  'kpoint_mp_spacing': (1.0, '1/ang')}]

Alternatively, we can just apply the correct parser directly.

# Rewind to the beginning of the buffer
file.seek(0)
castep_outputs.parse_cell_file(file)
[{'lattice_cart': {'data': [(17.395297, 0.0, 0.0),
    (0.0, 17.395297, 0.0),
    (0.0, 0.0, 17.395297)]},
  'positions_abs': {('Ar', 1): {'pos': (4.023973, 2.257202, 2.476523),
    'units': 'Ang'},
   ('Ar', 2): {'pos': (4.29215, 0.741938, -1.542184), 'units': 'Ang'},
   ('Ar', 3): {'pos': (0.834362, 4.894828, 5.031917), 'units': 'Ang'},
   ('Ar', 4): {'pos': (-7.896037, -0.914764, -0.186325), 'units': 'Ang'},
   ('Ar', 5): {'pos': (-5.92112, -1.280009, 5.918613), 'units': 'Ang'}},
  'fix_all_cell': True,
  'kpoint_mp_spacing': (1.0, '1/ang')}]