From e19e6287e940f79ee7e4f54af708dd3038a15e22 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Sat, 7 Mar 2026 00:35:36 -0500 Subject: [PATCH 1/3] chore: deprecate Atom class method --- news/deprecate-atom.rst | 28 +++++++ src/diffpy/structure/atom.py | 85 +++++++++++++------ tests/test_atom.py | 155 +++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 news/deprecate-atom.rst diff --git a/news/deprecate-atom.rst b/news/deprecate-atom.rst new file mode 100644 index 00000000..2ddd8b79 --- /dev/null +++ b/news/deprecate-atom.rst @@ -0,0 +1,28 @@ +**Added:** + +* Added ``msd_latt`` method in ``atom.py`` +* Added ``msd_cart`` method in ``atom.py`` +* Added ``_get_uij`` method in ``atom.py`` +* Added ``_set_uij`` method in ``atom.py`` + + +**Changed:** + +* + +**Deprecated:** + +* Deprecated ``msdLat`` method in ``atom.py`` for removal in version 4.0.0 +* Deprecated ``msdCart`` method in ``atom.py`` for removal in version 4.0.0 + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/structure/atom.py b/src/diffpy/structure/atom.py index 6571a815..75581454 100644 --- a/src/diffpy/structure/atom.py +++ b/src/diffpy/structure/atom.py @@ -18,12 +18,27 @@ import numpy from diffpy.structure.lattice import cartesian as cartesian_lattice +from diffpy.utils._deprecator import build_deprecation_message, deprecated # conversion constants _BtoU = 1.0 / (8 * numpy.pi**2) _UtoB = 1.0 / _BtoU # ---------------------------------------------------------------------------- +base = "diffpy.structure.Atom" +removal_version = "4.0.0" +msdLat_deprecation_msg = build_deprecation_message( + base, + "msdLat", + "msd_latt", + removal_version, +) +msdCart_deprecation_msg = build_deprecation_message( + base, + "msdCart", + "msd_cart", + removal_version, +) class Atom(object): @@ -149,7 +164,16 @@ def __init__( self.anisotropy = bool(anisotropy) return + @deprecated(msdLat_deprecation_msg) def msdLat(self, vl): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use diffpy.structure.Atom.msd_latt instead. + """ + return self.msd_latt(vl) + + def msd_latt(self, vl): """Calculate mean square displacement along the lattice vector. Parameters @@ -173,7 +197,16 @@ def msdLat(self, vl): msd = numpy.dot(rhs, numpy.dot(self.U, rhs)) return msd + @deprecated(msdLat_deprecation_msg) def msdCart(self, vc): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use diffpy.structure.Atom.msd_cart instead. + """ + return self.msd_cart(vc) + + def msd_cart(self, vc): """Calculate mean square displacement along the Cartesian vector. @@ -336,14 +369,14 @@ def U(self, value): # Uij elements - def _get_Uij(self, i, j): + def _get_uij(self, i, j): """The getter function for the `U11`, `U22`, ..., properties.""" if self.anisotropy: return self._U[i, j] lat = self.lattice or cartesian_lattice return self._U[0, 0] * lat.isotropicunit[i, j] - def _set_Uij(self, i, j, value): + def _set_uij(self, i, j, value): """The setter function for the `U11`, `U22`, ..., properties.""" self._U[i, j] = value self._U[j, i] = value @@ -361,18 +394,18 @@ def _set_Uij(self, i, j, value): """ U11 = property( - lambda self: self._get_Uij(0, 0), - lambda self, value: self._set_Uij(0, 0, value), + lambda self: self._get_uij(0, 0), + lambda self, value: self._set_uij(0, 0, value), doc=_doc_uii.format(0), ) U22 = property( - lambda self: self._get_Uij(1, 1), - lambda self, value: self._set_Uij(1, 1, value), + lambda self: self._get_uij(1, 1), + lambda self, value: self._set_uij(1, 1, value), doc=_doc_uii.format(1), ) U33 = property( - lambda self: self._get_Uij(2, 2), - lambda self, value: self._set_Uij(2, 2, value), + lambda self: self._get_uij(2, 2), + lambda self, value: self._set_uij(2, 2, value), doc=_doc_uii.format(2), ) @@ -384,18 +417,18 @@ def _set_Uij(self, i, j, value): """ U12 = property( - lambda self: self._get_Uij(0, 1), - lambda self, value: self._set_Uij(0, 1, value), + lambda self: self._get_uij(0, 1), + lambda self, value: self._set_uij(0, 1, value), doc=_doc_uij.format(0, 1), ) U13 = property( - lambda self: self._get_Uij(0, 2), - lambda self, value: self._set_Uij(0, 2, value), + lambda self: self._get_uij(0, 2), + lambda self, value: self._set_uij(0, 2, value), doc=_doc_uij.format(0, 2), ) U23 = property( - lambda self: self._get_Uij(1, 2), - lambda self, value: self._set_Uij(1, 2, value), + lambda self: self._get_uij(1, 2), + lambda self, value: self._set_uij(1, 2, value), doc=_doc_uij.format(1, 2), ) @@ -463,33 +496,33 @@ def Uisoequiv(self, value): """ B11 = property( - lambda self: _UtoB * self._get_Uij(0, 0), - lambda self, value: self._set_Uij(0, 0, _BtoU * value), + lambda self: _UtoB * self._get_uij(0, 0), + lambda self, value: self._set_uij(0, 0, _BtoU * value), doc=_doc_bii.format(1), ) B22 = property( - lambda self: _UtoB * self._get_Uij(1, 1), - lambda self, value: self._set_Uij(1, 1, _BtoU * value), + lambda self: _UtoB * self._get_uij(1, 1), + lambda self, value: self._set_uij(1, 1, _BtoU * value), doc=_doc_bii.format(2), ) B33 = property( - lambda self: _UtoB * self._get_Uij(2, 2), - lambda self, value: self._set_Uij(2, 2, _BtoU * value), + lambda self: _UtoB * self._get_uij(2, 2), + lambda self, value: self._set_uij(2, 2, _BtoU * value), doc=_doc_bii.format(3), ) B12 = property( - lambda self: _UtoB * self._get_Uij(0, 1), - lambda self, value: self._set_Uij(0, 1, _BtoU * value), + lambda self: _UtoB * self._get_uij(0, 1), + lambda self, value: self._set_uij(0, 1, _BtoU * value), doc=_doc_bij.format(1, 2), ) B13 = property( - lambda self: _UtoB * self._get_Uij(0, 2), - lambda self, value: self._set_Uij(0, 2, _BtoU * value), + lambda self: _UtoB * self._get_uij(0, 2), + lambda self, value: self._set_uij(0, 2, _BtoU * value), doc=_doc_bij.format(1, 3), ) B23 = property( - lambda self: _UtoB * self._get_Uij(1, 2), - lambda self, value: self._set_Uij(1, 2, _BtoU * value), + lambda self: _UtoB * self._get_uij(1, 2), + lambda self, value: self._set_uij(1, 2, _BtoU * value), doc=_doc_bij.format(2, 3), ) diff --git a/tests/test_atom.py b/tests/test_atom.py index 6c7dd32d..4097603f 100644 --- a/tests/test_atom.py +++ b/tests/test_atom.py @@ -18,6 +18,7 @@ import unittest import numpy +import pytest from diffpy.structure.atom import Atom from diffpy.structure.lattice import Lattice @@ -74,6 +75,50 @@ def test___init__(self): # """ # return + def test_msdLat(self): + """Check Atom.msd_latt (and deprecated Atom.msdLat alias).""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom_1 = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=0.0123) + assert atom_1.msdLat([1, 2, 3]) == pytest.approx(0.0123, rel=0, abs=1e-15) + assert atom_1.msdLat([9, 0, -4]) == pytest.approx(0.0123, rel=0, abs=1e-15) + U = numpy.array( + [ + [0.010, 0.002, 0.001], + [0.002, 0.020, 0.003], + [0.001, 0.003, 0.030], + ], + dtype=float, + ) + atom_2 = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) + + vc = numpy.array([1.2, -0.3, 0.7], dtype=float) + vl = hexagonal.fractional(vc) + + assert atom_2.msdLat(vl) == pytest.approx(atom_2.msd_cart(vc), rel=1e-13, abs=1e-13) + return + + def test_msdCart(self): + """Check Atom.msd_cart (and deprecated Atom.msdCart alias).""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom_1 = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=0.0456) + assert atom_1.msdCart([1, 0, 0]) == pytest.approx(0.0456, rel=0, abs=1e-15) + assert atom_1.msdCart([0, 5, -2]) == pytest.approx(0.0456, rel=0, abs=1e-15) + assert atom_1.msdCart([0, 5, -2]) == pytest.approx(0.0456, rel=0, abs=1e-15) + + U = numpy.array( + [ + [0.011, 0.001, 0.000], + [0.001, 0.019, 0.002], + [0.000, 0.002, 0.027], + ], + dtype=float, + ) + atom_2 = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) + + vc = numpy.array([0.4, 1.1, -0.6], dtype=float) + assert atom_2.msdCart(vc) == pytest.approx(atom_2.msdCart(3.7 * vc), rel=1e-13, abs=1e-13) + return + def test_xyz_cartn(self): """Check Atom.xyz_cartn property.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) @@ -146,7 +191,117 @@ def test_xyz_cartn(self): # End of class TestAtom + # ---------------------------------------------------------------------------- +@pytest.mark.parametrize( + "uiso, vl", + [ + (0.0123, [1, 2, 3]), + ], +) +def test_msd_latt_isotropic(uiso, vl): + """Check Atom.msd_latt().""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=uiso) + + actual = atom.msd_latt(vl) + expected = pytest.approx(uiso, rel=0, abs=1e-15) + assert actual == expected + + +@pytest.mark.parametrize( + "U, vc", + [ + ( + numpy.array( + [ + [0.010, 0.002, 0.001], + [0.002, 0.020, 0.003], + [0.001, 0.003, 0.030], + ], + dtype=float, + ), + numpy.array([1.2, -0.3, 0.7], dtype=float), + ), + ( + numpy.array( + [ + [0.018, -0.001, 0.002], + [-0.001, 0.012, 0.004], + [0.002, 0.004, 0.025], + ], + dtype=float, + ), + numpy.array([-0.8, 0.9, 0.1], dtype=float), + ), + ], +) +def test_msd_latt_anisotropic(U, vc): + """Check Atom.msd_latt() anisotropic coordinate-invariance.""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) + + vl = hexagonal.fractional(vc) + + actual = atom.msd_latt(vl) + expected = pytest.approx(atom.msd_cart(vc), rel=1e-13, abs=1e-13) + assert actual == expected + + +@pytest.mark.parametrize( + "uiso, vc", + [ + (0.0456, [0, 5, -2]), + ], +) +def test_msd_cart_isotropic(uiso, vc): + """Check Atom.msd_cart().""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=uiso) + + actual = atom.msd_cart(vc) + expected = pytest.approx(uiso, rel=0, abs=1e-15) + assert actual == expected + + +@pytest.mark.parametrize( + "U, vc, scale", + [ + ( + numpy.array( + [ + [0.011, 0.001, 0.000], + [0.001, 0.019, 0.002], + [0.000, 0.002, 0.027], + ], + dtype=float, + ), + numpy.array([0.4, 1.1, -0.6], dtype=float), + 3.7, + ), + ( + numpy.array( + [ + [0.020, 0.003, -0.001], + [0.003, 0.014, 0.002], + [-0.001, 0.002, 0.009], + ], + dtype=float, + ), + numpy.array([2.0, -1.0, 0.5], dtype=float), + 0.25, + ), + ], +) +def test_msd_cart_anisotropic(U, vc, scale): + """Check Atom.msd_cart() anisotropic normalization invariance.""" + hexagonal = Lattice(1, 1, 1, 90, 90, 120) + atom = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) + + actual = atom.msd_cart(vc) + expected = pytest.approx(atom.msd_cart(scale * vc), rel=1e-13, abs=1e-13) + assert actual == expected + if __name__ == "__main__": unittest.main() From 9751d1e5918457a0ee422cf063352b06516310be Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Sat, 7 Mar 2026 00:38:56 -0500 Subject: [PATCH 2/3] chore: change docstring --- tests/test_atom.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_atom.py b/tests/test_atom.py index 4097603f..66a7f283 100644 --- a/tests/test_atom.py +++ b/tests/test_atom.py @@ -76,7 +76,7 @@ def test___init__(self): # return def test_msdLat(self): - """Check Atom.msd_latt (and deprecated Atom.msdLat alias).""" + """Check Atom.msdLat.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom_1 = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=0.0123) assert atom_1.msdLat([1, 2, 3]) == pytest.approx(0.0123, rel=0, abs=1e-15) @@ -95,10 +95,9 @@ def test_msdLat(self): vl = hexagonal.fractional(vc) assert atom_2.msdLat(vl) == pytest.approx(atom_2.msd_cart(vc), rel=1e-13, abs=1e-13) - return def test_msdCart(self): - """Check Atom.msd_cart (and deprecated Atom.msdCart alias).""" + """Check Atom.msdCart.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom_1 = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=0.0456) assert atom_1.msdCart([1, 0, 0]) == pytest.approx(0.0456, rel=0, abs=1e-15) @@ -117,7 +116,6 @@ def test_msdCart(self): vc = numpy.array([0.4, 1.1, -0.6], dtype=float) assert atom_2.msdCart(vc) == pytest.approx(atom_2.msdCart(3.7 * vc), rel=1e-13, abs=1e-13) - return def test_xyz_cartn(self): """Check Atom.xyz_cartn property.""" From d6d852514d5131230618d0301ed2980428059652 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Sat, 7 Mar 2026 11:58:57 -0500 Subject: [PATCH 3/3] chore: rename variables to more explicit one, add test case descriptions. --- tests/test_atom.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/test_atom.py b/tests/test_atom.py index 66a7f283..2d2b4f5e 100644 --- a/tests/test_atom.py +++ b/tests/test_atom.py @@ -192,24 +192,25 @@ def test_xyz_cartn(self): # ---------------------------------------------------------------------------- @pytest.mark.parametrize( - "uiso, vl", - [ + "uiso, lattice_vector", + [ # C1: isotropic displacement, msd is direction-independent in lattice coordinates. + # Expected the msd_latt equals Uisoequiv for any direction. (0.0123, [1, 2, 3]), ], ) -def test_msd_latt_isotropic(uiso, vl): +def test_msd_latt_isotropic(uiso, lattice_vector): """Check Atom.msd_latt().""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=uiso) - - actual = atom.msd_latt(vl) + actual = atom.msd_latt(lattice_vector) expected = pytest.approx(uiso, rel=0, abs=1e-15) assert actual == expected @pytest.mark.parametrize( - "U, vc", - [ + "U, cartesian_vector", + [ # C2: anisotropic displacement with same physical direction expressed in lattice vs cartesian coords + # Expected msd_latt(fractional(cartesian_vector)) == msd_cart(cartesian_vector) ( numpy.array( [ @@ -234,37 +235,37 @@ def test_msd_latt_isotropic(uiso, vl): ), ], ) -def test_msd_latt_anisotropic(U, vc): +def test_msd_latt_anisotropic(U, cartesian_vector): """Check Atom.msd_latt() anisotropic coordinate-invariance.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) - - vl = hexagonal.fractional(vc) - - actual = atom.msd_latt(vl) - expected = pytest.approx(atom.msd_cart(vc), rel=1e-13, abs=1e-13) + lattice_vector = hexagonal.fractional(cartesian_vector) + actual = atom.msd_latt(lattice_vector) + expected = pytest.approx(atom.msd_cart(cartesian_vector), rel=1e-13, abs=1e-13) assert actual == expected @pytest.mark.parametrize( - "uiso, vc", - [ + "uiso, cartesian_vector", + [ # C1: isotropic displacement with msd is direction-independent in cartesian coordinates + # Expected msd_cart equals Uisoequiv for any direction (0.0456, [0, 5, -2]), ], ) -def test_msd_cart_isotropic(uiso, vc): +def test_msd_cart_isotropic(uiso, cartesian_vector): """Check Atom.msd_cart().""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom = Atom("C", [0, 0, 0], lattice=hexagonal, Uisoequiv=uiso) - actual = atom.msd_cart(vc) + actual = atom.msd_cart(cartesian_vector) expected = pytest.approx(uiso, rel=0, abs=1e-15) assert actual == expected @pytest.mark.parametrize( - "U, vc, scale", - [ + "U, cartesian_vector, scale", + [ # C2: anisotropic displacement with msd_cart normalizes direction vector internally + # Expected msd_cart(cartesian_vector) == msd_cart(scale * cartesian_vector) ( numpy.array( [ @@ -291,13 +292,12 @@ def test_msd_cart_isotropic(uiso, vc): ), ], ) -def test_msd_cart_anisotropic(U, vc, scale): +def test_msd_cart_anisotropic(U, cartesian_vector, scale): """Check Atom.msd_cart() anisotropic normalization invariance.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) atom = Atom("C", [0, 0, 0], lattice=hexagonal, U=U) - - actual = atom.msd_cart(vc) - expected = pytest.approx(atom.msd_cart(scale * vc), rel=1e-13, abs=1e-13) + actual = atom.msd_cart(cartesian_vector) + expected = pytest.approx(atom.msd_cart(scale * cartesian_vector), rel=1e-13, abs=1e-13) assert actual == expected