Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All user facing changes to this project are documented in this file. The format
on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`__, and this project tries
its best to adhere to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`__.

Unreleased
==========

Fixed
-----
- Silenced most warnings from diffpy.structure >= 3.4.


2026-06-10 - version 0.15.0
===========================
Expand Down
1 change: 1 addition & 0 deletions doc/user/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ This is a list of required package dependencies:
* :doc:`matplotlib <matplotlib:index>`: Visualization
* :doc:`numba <numba:index>`: CPU acceleration
* :doc:`numpy <numpy:index>`: Handling of N-dimensional arrays
* :doc:`packaging <packaging:index>`: Version comparison
* :doc:`pooch <pooch:api/index>`: Downloading and caching of datasets
* :doc:`scipy <scipy:index>`: Optimization algorithms, filtering and more
* `tqdm <https://tqdm.github.io/>`__: Progressbars
Expand Down
76 changes: 76 additions & 0 deletions orix/_utils/_diffpy_structure_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#
# Copyright 2018-2026 the orix developers
#
# This file is part of orix.
#
# orix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# orix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with orix. If not, see <http://www.gnu.org/licenses/>.
#

"""Utilities for interfacing with diffpy.structure."""

from typing import Callable

from diffpy.structure import __version__
from diffpy.structure.lattice import Lattice
from diffpy.structure.parsers import p_cif
from diffpy.structure.spacegroups import SpaceGroup
from diffpy.structure.structure import Structure
from packaging.version import Version

DIFFPY_STRUCTURE_VERSION = Version(__version__)

# TODO: Remove these checks (and most likely this whole file) once
# 3.4.0 is the minimal supported version

if DIFFPY_STRUCTURE_VERSION >= Version("3.4.0"):
from diffpy.structure import load_structure
from diffpy.structure.spacegroups import get_space_group
else:
from diffpy.structure import loadStructure as load_structure
from diffpy.structure.spacegroups import GetSpaceGroup as get_space_group


def place_in_lattice(structure: Structure, lattice: Lattice) -> Structure:
if DIFFPY_STRUCTURE_VERSION >= Version("3.4.0"):
return structure.place_in_lattice(lattice)
else:
return structure.placeInLattice(lattice)


def get_cell_parms(lattice: Lattice) -> tuple[float, float, float, float, float, float]:
if DIFFPY_STRUCTURE_VERSION >= Version("3.4.0"):
return lattice.cell_parms()
else:
return lattice.abcABG()


def get_parser_and_structure_from_cif_file(fname: str) -> tuple[p_cif.P_cif, Structure]:
parser = p_cif.P_cif()
if DIFFPY_STRUCTURE_VERSION >= Version("3.4.0"):
structure = parser.parse_file(fname)
else:
structure = parser.parseFile(fname)
return parser, structure


get_space_group: Callable[[str | int], SpaceGroup]
# Simplified signature for our current use case
load_structure: Callable[[str], Structure]

__all__ = [
"get_space_group",
"get_parser_and_structure_from_cif_file",
"load_structure",
"place_in_lattice",
]
26 changes: 16 additions & 10 deletions orix/crystal_map/_phase.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2018-2025 the orix developers
# Copyright 2018-2026 the orix developers
#
# This file is part of orix.
#
Expand All @@ -26,12 +26,17 @@
import warnings

from diffpy.structure import Lattice, Structure
from diffpy.structure.parsers import p_cif
from diffpy.structure.spacegroups import GetSpaceGroup, SpaceGroup
from diffpy.structure.spacegroups import SpaceGroup
from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit
import matplotlib.colors as mcolors
import numpy as np

from orix._utils._diffpy_structure_utils import (
get_cell_parms,
get_parser_and_structure_from_cif_file,
get_space_group,
place_in_lattice,
)
from orix.plot._util.color import get_matplotlib_color
from orix.quaternion.symmetry import (
_EDAX_POINT_GROUP_ALIASES,
Expand Down Expand Up @@ -142,7 +147,7 @@ def structure(self, value: Structure) -> None:
new_value = value.copy()

# Ensure atom positions are expressed in the new basis
new_value.placeInLattice(Lattice(base=new_matrix))
new_value = place_in_lattice(new_value, Lattice(base=new_matrix))

# Store old lattice for expand_asymmetric_unit
self._diffpy_lattice = old_matrix
Expand Down Expand Up @@ -207,7 +212,7 @@ def space_group(self) -> SpaceGroup | None:
def space_group(self, value: int | SpaceGroup | None) -> None:
"""Set the space group."""
if isinstance(value, int):
value = GetSpaceGroup(value)
value = get_space_group(value)
if not isinstance(value, SpaceGroup) and value is not None:
raise ValueError(
f"{value!r} must be of type {SpaceGroup}, an integer 1-230, or None"
Expand Down Expand Up @@ -265,7 +270,7 @@ def is_hexagonal(self) -> bool:
"""Return whether the crystal structure is hexagonal/trigonal or
not.
"""
return np.allclose(self.structure.lattice.abcABG()[3:], [90, 90, 120])
return np.allclose(get_cell_parms(self.structure.lattice)[3:], [90, 90, 120])

@property
def a_axis(self) -> Miller:
Expand Down Expand Up @@ -350,14 +355,13 @@ def from_cif(cls, filename: str | Path) -> Phase:
file format.
"""
path = Path(filename)
parser = p_cif.P_cif()
name = path.stem
structure = parser.parseFile(str(path))
parser, structure = get_parser_and_structure_from_cif_file(str(path))
try:
space_group = parser.spacegroup.number
except AttributeError: # pragma: no cover
space_group = None
warnings.warn(f"Could not read space group from CIF file {path!r}")
name = path.stem
return cls(name, space_group, structure=structure)

def deepcopy(self) -> Phase:
Expand Down Expand Up @@ -401,7 +405,9 @@ def expand_asymmetric_unit(self) -> Phase:

# Ensure atom positions are expressed in diffpy's convention
diffpy_structure = self.structure.copy()
diffpy_structure.placeInLattice(Lattice(base=self._diffpy_lattice))
diffpy_structure = place_in_lattice(
diffpy_structure, Lattice(base=self._diffpy_lattice)
)
xyz = diffpy_structure.xyz
diffpy_structure.clear()

Expand Down
3 changes: 2 additions & 1 deletion orix/io/plugins/ang.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import numpy as np

from orix import __version__
from orix._utils._diffpy_structure_utils import get_cell_parms
from orix.crystal_map._phase_list import PhaseList
from orix.crystal_map.crystal_map import CrystalMap, create_coordinate_arrays
from orix.quaternion.rotation import Rotation
Expand Down Expand Up @@ -618,7 +619,7 @@ def _get_header_from_phases(xmap: CrystalMap) -> str:
# Phase IDs are reversed because EDAX TSL OIM Analysis v7.2.0
# assumes a reversed phase order in the header
for i, (_, phase) in reversed(list(enumerate(pl))):
lattice_constants = phase.structure.lattice.abcABG()
lattice_constants = get_cell_parms(phase.structure.lattice)
lattice_constants = " ".join([f"{float(val):.3f}" for val in lattice_constants])
phase_id = i + 1
phase_name = phase.name
Expand Down
5 changes: 3 additions & 2 deletions orix/io/plugins/orix_hdf5.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2018-2025 the orix developers
# Copyright 2018-2026 the orix developers
#
# This file is part of orix.
#
Expand Down Expand Up @@ -28,6 +28,7 @@
from h5py import File, Group
import numpy as np

from orix._utils._diffpy_structure_utils import get_cell_parms
from orix.crystal_map._phase import Phase
from orix.crystal_map._phase_list import PhaseList
from orix.crystal_map.crystal_map import CrystalMap
Expand Down Expand Up @@ -445,7 +446,7 @@ def lattice2dict(lattice: Lattice, dictionary: dict | None = None) -> dict:
"""
if dictionary is None:
dictionary = {}
dictionary["abcABG"] = np.array(lattice.abcABG())
dictionary["abcABG"] = np.array(get_cell_parms(lattice))
dictionary["baserot"] = lattice.baserot
return dictionary

Expand Down
8 changes: 4 additions & 4 deletions orix/quaternion/symmetry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2018-2025 the orix developers
# Copyright 2018-2026 the orix developers
#
# This file is part of orix.
#
Expand All @@ -21,10 +21,10 @@

from typing import TYPE_CHECKING, Literal

from diffpy.structure.spacegroups import GetSpaceGroup
import matplotlib.figure as mfigure
import numpy as np

from orix._utils._diffpy_structure_utils import get_space_group
from orix.quaternion.rotation import Rotation
from orix.vector.vector3d import Vector3d

Expand Down Expand Up @@ -815,8 +815,8 @@ def get_point_group(space_group_number: int, proper: bool = False) -> Symmetry:
>>> pgO.name
'432'
"""
spg = GetSpaceGroup(space_group_number)
pgn = spg.point_group_name
spg = get_space_group(space_group_number)
pgn: str = spg.point_group_name
if proper:
return spacegroup2pointgroup_dict[pgn]["proper"]
else:
Expand Down
4 changes: 2 additions & 2 deletions orix/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from collections import OrderedDict
from numbers import Number
from typing import Callable
from typing import Callable, Generator

from diffpy.structure import Atom, Lattice, Structure
from h5py import File
Expand Down Expand Up @@ -1088,7 +1088,7 @@ def temp_bruker_h5ebsd_file(tmpdir, request):


@pytest.fixture
def cif_file(tmpdir):
def cif_file(tmpdir) -> Generator[str, None, None]:
"""Actual CIF file of beta double prime phase often seen in Al-Mg-Si
alloys.
"""
Expand Down
29 changes: 18 additions & 11 deletions orix/tests/test_crystal_map/test_phase.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2018-2025 the orix developers
# Copyright 2018-2026 the orix developers
#
# This file is part of orix.
#
Expand All @@ -17,10 +17,15 @@
# along with orix. If not, see <http://www.gnu.org/licenses/>.
#

from diffpy.structure import Atom, Lattice, Structure, loadStructure
from diffpy.structure import Atom, Lattice, Structure
import numpy as np
import pytest

from orix._utils._diffpy_structure_utils import (
get_cell_parms,
load_structure,
place_in_lattice,
)
from orix.crystal_map import Phase
from orix.crystal_map._phase import default_lattice, new_structure_matrix_from_alignment
from orix.quaternion.symmetry import O, Symmetry
Expand Down Expand Up @@ -113,7 +118,9 @@ def test_copy_constructor_phase(self):
assert p1.structure[0].element == p2.structure[0].element
assert tuple(p1.structure[0].xyz) == tuple(p2.structure[0].xyz)
assert p1.structure[0] is not p2.structure[0]
assert p1.structure.lattice.abcABG() == p2.structure.lattice.abcABG()
assert get_cell_parms(p1.structure.lattice) == get_cell_parms(
p2.structure.lattice
)
assert p1.structure.lattice is not p2.structure.lattice

@pytest.mark.parametrize("name", [None, "al", 1, np.arange(2)])
Expand Down Expand Up @@ -272,7 +279,7 @@ def test_structure_matrix(self):
lattice = phase.structure.lattice

# Lattice parameters are unchanged
assert np.allclose(lattice.abcABG(), [1.7, 1.7, 1.4, 90, 90, 120])
assert np.allclose(get_cell_parms(lattice), [1.7, 1.7, 1.4, 90, 90, 120])

# Structure matrix has changed internally, but not the input
# `Lattice` instance
Expand Down Expand Up @@ -412,14 +419,14 @@ def test_from_cif(self, cif_file):
assert phase.point_group.name == "2/m"
assert len(phase.structure) == 22 # Number of atoms
lattice = phase.structure.lattice
assert np.allclose(lattice.abcABG(), [15.5, 4.05, 6.74, 90, 105.3, 90])
assert np.allclose(get_cell_parms(lattice), [15.5, 4.05, 6.74, 90, 105.3, 90])
assert np.allclose(
lattice.base, [[15.5, 0, 0], [0, 4.05, 0], [-1.779, 0, 6.501]], atol=1e-3
)

def test_from_cif_same_structure(self, cif_file):
def test_from_cif_same_structure(self, cif_file: str):
phase1 = Phase.from_cif(cif_file)
structure = loadStructure(cif_file)
structure = load_structure(cif_file)
phase2 = Phase(structure=structure)
assert np.allclose(phase1.structure.lattice.base, phase2.structure.lattice.base)
assert np.allclose(phase1.structure.xyz, phase2.structure.xyz)
Expand Down Expand Up @@ -563,7 +570,7 @@ def test_expand_asymmetric_unit(
# Check atom positions in ORIGINAL lattice alignment
# Doing the check in orix's alignment makes independently computing expected sites difficult
s = exp.structure.copy()
s.placeInLattice(Lattice(base=phase._diffpy_lattice))
s = place_in_lattice(s, Lattice(base=phase._diffpy_lattice))
# Use set to avoid having to ensure the order is the same
assert set(tuple(xyz.round(8).tolist()) for xyz in s.xyz) == set(
expected_atom_positions
Expand All @@ -574,7 +581,7 @@ def test_expand_asymmetric_unit(
assert np.array_equal(base, exp2.structure.lattice.base)
assert len(exp2.structure) == len(expected_atom_positions)
s = exp2.structure.copy()
s.placeInLattice(Lattice(base=phase._diffpy_lattice))
s = place_in_lattice(s, Lattice(base=phase._diffpy_lattice))
assert set(tuple(xyz.round(8).tolist()) for xyz in s.xyz) == set(
expected_atom_positions
)
Expand Down Expand Up @@ -969,12 +976,12 @@ def test_expand_asymmetric_unit_raise_if_no_point_group(self):
def test_default_lattice(self):
for S in ["1", "2", "222", "422", "432"]:
phase = Phase(point_group=S)
lattice_parameters = phase.structure.lattice.abcABG()
lattice_parameters = get_cell_parms(phase.structure.lattice)
assert np.allclose([1, 1, 1, 90, 90, 90], lattice_parameters)

for S in ["3", "622"]:
phase = Phase(point_group=S)
lattice_parameters = phase.structure.lattice.abcABG()
lattice_parameters = get_cell_parms(phase.structure.lattice)
assert np.allclose([1, 1, 1, 90, 90, 120], lattice_parameters)

def test_default_lattice_raises(self):
Expand Down
Loading
Loading