Skip to content
Merged
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
28 changes: 28 additions & 0 deletions news/deprecate-atom.rst
Original file line number Diff line number Diff line change
@@ -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:**

* <news item>

**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:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
85 changes: 59 additions & 26 deletions src/diffpy/structure/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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),
)

Expand All @@ -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),
)

Expand Down Expand Up @@ -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),
)

Expand Down
153 changes: 153 additions & 0 deletions tests/test_atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import unittest

import numpy
import pytest

from diffpy.structure.atom import Atom
from diffpy.structure.lattice import Lattice
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Loading