Skip to content

Commit

Permalink
Unit test and fixes for castep lattice_abc format
Browse files Browse the repository at this point in the history
Cell files with lattice_abc vectors were broken, and this was not
causing test failures because it wasn't tested. Here we:

- Create test files with and without explicit dimension units
- fix the treatment of lattice_abc data in "two-rows" format (no unit)
- support recent versions of Pymatgen by migrating to
- Lattice.from_parameters
  from the deprecated Lattice.from_lengths_and_angles
- Note that Lattice.from_parameters does not produce the same cell
  matrices as CASTEP, and raise a warning if the user has
  abs_positions. The positions will very likely be wrong in this case;
  but not many features of Sumo are impacted.
  • Loading branch information
ajjackson committed Aug 29, 2024
1 parent 99f2a4d commit e0189e8
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 4 deletions.
14 changes: 12 additions & 2 deletions sumo/io/castep.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

unsupported_dosplot_args = {"elements", "lm_orbitals", "atoms"}

logger = logging.getLogger("io.castep")


class CastepCell:
"""Structure information and more: CASTEP seedname.cell file
Expand Down Expand Up @@ -76,14 +78,16 @@ def structure(self):
lengths_and_angles = lattice_abc[1:]
else:
unit = "ang"
lengths_and_angles = lattice_abc[1:]
lengths_and_angles = lattice_abc
if len(lengths_and_angles) != 2:
raise ValueError("lattice_abc should have two rows")
lengths_and_angles = [list(map(float, row)) for row in lengths_and_angles]
lengths_and_angles[0] = [
x * to_angstrom[unit] for x in lengths_and_angles[0]
]
lattice = Lattice.from_lengths_and_angles(*lengths_and_angles)
lattice = Lattice.from_parameters(
*lengths_and_angles[0], *lengths_and_angles[1]
)
else:
raise ValueError("Couldn't find a lattice in cell file")

Expand All @@ -95,6 +99,12 @@ def structure(self):
elements, coords = zip(*elements_coords)
return Structure(lattice, elements, coords, coords_are_cartesian=False)
elif "positions_abs" in self.blocks:
if "lattice_abc" in self.blocks:
logger.warning(
"Positions are given in absolute coordinates and lattice "
"in angles/lengths format. Structure may not be consistent."
)

positions_abs = self.blocks["positions_abs"].values
if positions_abs[0][0].lower() in ("ang", "nm", "cm", "m", "bohr", "a0"):
unit = positions_abs[0][0].lower()
Expand Down
20 changes: 20 additions & 0 deletions tests/data/NiO/NiO_abc.cell
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
%BLOCK LATTICE_ABC
2.983077 2.98308198 5.15994087
106.80215981 73.19772637 119.99807072
%ENDBLOCK LATTICE_ABC

%BLOCK POSITIONS_ABS
Ni 0.000000 0.000000 0.000000 SPIN=1
Ni 1.491603 0.861143 2.432002 SPIN=-1
O 0.000069 1.722313 1.215967 SPIN=0
O 2.983138 -0.000026 3.648036 SPIN=0
%ENDBLOCK POSITIONS_ABS

%BLOCK SPECIES_POT
Ni C19
O C19
%ENDBLOCK SPECIES_POT

KPOINTS_MP_GRID 7 7 7
SPECTRAL_KPOINTS_MP_GRID 2 2 2

22 changes: 22 additions & 0 deletions tests/data/NiO/NiO_abc_units.cell
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
%BLOCK LATTICE_ABC
Ang
2.983077 2.98308198 5.15994087
106.80215981 73.19772637 119.99807072
%ENDBLOCK LATTICE_ABC

%BLOCK POSITIONS_ABS
Ang
Ni 0.000000 0.000000 0.000000 SPIN=1
Ni 1.491603 0.861143 2.432002 SPIN=-1
O 0.000069 1.722313 1.215967 SPIN=0
O 2.983138 -0.000026 3.648036 SPIN=0
%ENDBLOCK POSITIONS_ABS

%BLOCK SPECIES_POT
Ni C19
O C19
%ENDBLOCK SPECIES_POT

KPOINTS_MP_GRID 7 7 7
SPECTRAL_KPOINTS_MP_GRID 2 2 2

28 changes: 26 additions & 2 deletions tests/tests_io/test_castep.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CastepCell,
CastepPhonon,
labels_from_cell,
logger,
read_bands_eigenvalues,
read_bands_header,
read_dos,
Expand All @@ -31,12 +32,18 @@ def setUp(self):
self.si_cell_alt = os.path.join(
ilr_files("tests"), "data", "Si", "Si2-alt.cell"
)
si_structure_file = os.path.join(ilr_files("tests"), "data", "Si", "Si8.json")
self.si_structure = Structure.from_file(si_structure_file)
self.zns_band_cell = os.path.join(ilr_files("tests"), "data", "ZnS", "zns.cell")
self.zns_singlepoint_cell = os.path.join(
ilr_files("tests"), "data", "ZnS", "zns-sp.cell"
)
si_structure_file = os.path.join(ilr_files("tests"), "data", "Si", "Si8.json")
self.si_structure = Structure.from_file(si_structure_file)
self.nio_abc_cell = os.path.join(
ilr_files("tests"), "data", "NiO", "NiO_abc.cell"
)
self.nio_abc_units_cell = os.path.join(
ilr_files("tests"), "data", "NiO", "NiO_abc_units.cell"
)

def test_castep_cell_null_init(self):
null_cell = CastepCell()
Expand Down Expand Up @@ -72,6 +79,23 @@ def test_castep_cell_from_singlepoint_file(self):
[[0.0, 2.71, 2.71], [2.71, 0.0, 2.71], [2.71, 2.71, 0.0]],
)

def test_castep_cell_abc(self):
"""Test .cell file using lattice_abc to define unit cell"""
for filename in self.nio_abc_cell, self.nio_abc_units_cell:
with self.assertLogs(logger) as log:
cc = CastepCell.from_file(filename)
structure = cc.structure
self.assertIn("Structure may not be consistent.", log.output[-1])

assert_array_almost_equal(
structure.lattice.matrix,
[
[2.855724, 0.0, 0.862317],
[-1.297582, 2.54391, -0.862313],
[0.0, 0.0, 5.159941],
],
)

def test_castep_cell_from_structure(self):
cell = CastepCell.from_structure(self.si_structure)
self.assertEqual(
Expand Down

0 comments on commit e0189e8

Please sign in to comment.