diff --git a/news/deprecate-atom.rst b/news/deprecate-atom.rst new file mode 100644 index 0000000..2ddd8b7 --- /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 6571a81..7558145 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 6c7dd32..2d2b4f5 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,48 @@ def test___init__(self): # """ # return + def test_msdLat(self): + """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) + 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) + + def test_msdCart(self): + """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) + 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) + def test_xyz_cartn(self): """Check Atom.xyz_cartn property.""" hexagonal = Lattice(1, 1, 1, 90, 90, 120) @@ -146,7 +189,117 @@ def test_xyz_cartn(self): # End of class TestAtom + # ---------------------------------------------------------------------------- +@pytest.mark.parametrize( + "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, 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(lattice_vector) + expected = pytest.approx(uiso, rel=0, abs=1e-15) + assert actual == expected + + +@pytest.mark.parametrize( + "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( + [ + [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, 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) + 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, 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, 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(cartesian_vector) + expected = pytest.approx(uiso, rel=0, abs=1e-15) + assert actual == expected + + +@pytest.mark.parametrize( + "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( + [ + [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, 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(cartesian_vector) + expected = pytest.approx(atom.msd_cart(scale * cartesian_vector), rel=1e-13, abs=1e-13) + assert actual == expected + if __name__ == "__main__": unittest.main()