-
Notifications
You must be signed in to change notification settings - Fork 921
Description
Python version
Python 3.11.12
Pymatgen version
2025.2.18
Operating system version
MacOS 24.5.0
Current behavior
Hi, thanks for such a great package! I am having some trouble with the Niggli reduction, and unless I am misunderstanding something I think there is a bug in the final mapping.
The issue: Running structure.get_reduced_structure(reduction_algo="niggli")
sometimes returns a reflected version of the original system. The core issue is in the final mapping at the following lines:
pymatgen/src/pymatgen/core/lattice.py
Lines 1274 to 1278 in f9d9fe8
mapped = self.find_mapping(lattice, e, skip_rotation_matrix=True) | |
if mapped is not None: | |
if np.linalg.det(mapped[0].matrix) > 0: | |
return mapped[0] # type:ignore[return-value] | |
return type(self)(-mapped[0].matrix) |
If you switchskip_rotation_matrix=False
and check the determinant of the rotation matrix, you'll get -1, corresponding to a reflection matrix.
Let me know if I'm misunderstanding something and this is an intended behavior!
Below I've provided a snippet that shows the source of the issue: the find_mapping
method returns a reflection mapping, even when the source and target lattices are the same!
Thanks again!
Expected Behavior
I would expect that find_mapping
returns a rotated lattice but not a reflection. While a reflected system has the same energy, it's physical properties can be different.
Minimal example
import ase
import ase.io
import numpy as np
from pymatgen.io.ase import AseAtomsAdaptor
def get_atoms():
atoms = ase.Atoms(
positions=np.array(
[
[0.00000000e00, 0.00000000e00, 0.00000000e00],
[2.32160500e00, -2.55395379e-10, 2.62836300e00],
[-2.55396948e-10, 2.32160500e00, 2.62836300e00],
[2.32160500e00, 2.32160500e00, -6.87146875e-17],
[2.85942205e-10, -2.85941836e-10, 2.94272573e00],
[-4.80245259e-10, 2.32160500e00, 3.14362728e-01],
[2.32160500e00, 2.32160500e00, 2.31400027e00],
[-3.05465285e-11, 2.32160500e00, 4.94236327e00],
]
),
numbers=np.array([11, 11, 65, 65, 8, 8, 8, 8]),
cell=[
[4.643209998612975, 0.0, 0.0],
[-1.0215833248797727e-09, 4.643209998612975, 0.0],
[-2.3216049982849043, -2.3216049998172785, 5.2567259980108245],
],
pbc=True,
)
return atoms
if __name__ == "__main__":
atoms = get_atoms()
ase.io.write("original.png", atoms)
ase.io.write("original.cif", atoms)
structure = AseAtomsAdaptor.get_structure(atoms)
# structure = structure.get_reduced_structure(reduction_algo="niggli")
tol = 1e-5
e = tol * structure._lattice.volume ** (1 / 3)
# Compute a lattice mapping between it and itself
mapping, rotation_matrix, translation_vector = structure._lattice.find_mapping(
structure._lattice, e, skip_rotation_matrix=False
)
print(np.linalg.det(rotation_matrix)). # ----> prints -1, corresponding to a reflection!
structure_mapped = type(structure)(
mapping,
structure.species_and_occu,
structure.cart_coords,
coords_are_cartesian=True,
to_unit_cell=True,
site_properties=structure.site_properties,
labels=structure.labels,
charge=structure._charge,
)
atoms_mapped = AseAtomsAdaptor.get_atoms(structure_mapped)
ase.io.write("mapped.png", atoms_mapped)
ase.io.write("mapped.cif", atoms_mapped)
Relevant files to reproduce this bug
No response