wulfric.niggli#

wulfric.niggli(a=1, b=1, c=1, alpha=90, beta=90, gamma=90, eps_rel=0.0001, verbose=False, return_cell=False, max_iter=10000)[source]#

Computes Niggli matrix form.

Parameters:
afloat, default 1

Length of the \(\boldsymbol{a_1}\) vector.

bfloat, default 1

Length of the \(\boldsymbol{a_2}\) vector.

cfloat, default 1

Length of the \(\boldsymbol{a_3}\) vector.

alphafloat, default 90

Angle between vectors \(\boldsymbol{a_2}\) and \(\boldsymbol{a_3}\). In degrees.

betafloat, default 90

Angle between vectors \(\boldsymbol{a_1}\) and \(\boldsymbol{a_3}\). In degrees.

gammafloat, default 90

Angle between vectors \(\boldsymbol{a_1}\) and \(\boldsymbol{a_2}\). In degrees.

eps_relfloat, default 1e-5

Relative epsilon as defined in [2].

verbosebool, default False

Whether to print the steps of an algorithm.

return_cellbool, default False

Whether to return cell parameters instead of Niggli matrix form.

max_iterint, default 100000

Maximum number of iterations.

Returns:
result(3,2) numpy.ndarray or (6,) tuple of floats

Niggli matrix form as defined in [1]:

\[\begin{split}\begin{pmatrix} A & B & C \\ \xi/2 & \eta/2 & \zeta/2 \end{pmatrix}\end{split}\]

If return_cell == True, then returns Niggli cell parameters: (a, b, c, alpha, beta, gamma).

Raises:
ValueError

If the niggli cell is not found in max_iter iterations.

ValueError

If the provided cell`s volume is zero.

Notes

The parameters are defined as follows:

\[\begin{split}A & = a^2 \\ B & = b^2 \\ C & = c^2 \\ \xi & = 2bc \cos(\alpha) \\ \eta & = 2ac \cos(\beta) \\ \zeta & = 2ab \cos(\gamma)\end{split}\]

Steps of an algorithm from original paper [1]:

  1. \(A > B\) or (\(A = B\) and \(|\xi| > |\eta|\)), then swap \((A, \xi) \leftrightarrow (B,\eta)\).

  2. \(B > C\) or (\(B = C\) and \(|\eta| > |\zeta|\)), then swap \((B, \eta) \leftrightarrow (C,\zeta)\) and go to 1.

  3. If \(\xi \eta \zeta > 0\), then put \((|\xi|, |\eta|, |\zeta|) \rightarrow (\xi, \eta, \zeta)\).

  4. If \(\xi \eta \zeta \leq 0\), then put \((-|\xi|, -|\eta|, -|\zeta|) \rightarrow (\xi, \eta, \zeta)\).

  5. If \(|\xi| > B\) or (\(\xi = B\) and \(2\eta < \zeta\)) or (\(\xi = -B\) and \(\zeta < 0\)), then apply the following transformation:

    \[\begin{split}C & = B + C - \xi \,\text{sign}(\xi) \\ \eta & = \eta - \zeta \,\text{sign}(\xi) \\ \xi & = \xi - 2B \,\text{sign}(\xi)\end{split}\]

    and go to 1.

  6. If \(|\eta| > A\) or (\(\eta = A\) and \(2\xi < \zeta\)) or (\(\eta = -A\) and \(\zeta < 0\)), then apply the following transformation:

    \[\begin{split}C & = A + C - \eta \,\text{sign}(\eta) \\ \xi & = \xi - \zeta \,\text{sign}(\eta) \\ \eta & = \eta - 2A \,\text{sign}(\eta)\end{split}\]

    and go to 1.

  7. If \(|\zeta| > A\) or (\(\zeta = A\) and \(2\xi < \eta\)) or (\(\zeta = -A\) and \(\eta < 0\)), then apply the following transformation:

    \[\begin{split}B & = A + B - \zeta \,\text{sign}(\zeta) \\ \xi & = \xi - \eta \,\text{sign}(\zeta) \\ \zeta & = \zeta - 2A \,\text{sign}(\zeta)\end{split}\]

    and go to 1.

  8. If \(\xi + \eta + \zeta + A + B < 0\) or (\(\xi + \eta + \zeta + A + B = 0\) and \(2(A + \eta) + \zeta > 0\)), then apply the following transformation:

    \[\begin{split}C & = A + B + C + \xi + \eta + \zeta \\ \xi & = 2B + \xi + \zeta \\ \eta & = 2A + \eta + \zeta\end{split}\]

    and go to 1.

References

[1] (1,2,3)

Křivý, I. and Gruber, B., 1976. A unified algorithm for determining the reduced (Niggli) cell. Acta Crystallographica Section A: Crystal Physics, Diffraction, Theoretical and General Crystallography, 32(2), pp.297-298.

[2]

Grosse-Kunstleve, R.W., Sauter, N.K. and Adams, P.D., 2004. Numerically stable algorithms for the computation of reduced unit cells. Acta Crystallographica Section A: Foundations of Crystallography, 60(1), pp.1-6.

Examples

Example from [1] (parameters are reproducing \(A=9\), \(B=27\), \(C=4\), \(\xi\) = -5, \(\eta\) = -4, \(\zeta = -22\)):

>>> import wulfric as wulf
>>> from wulfric.constants import TODEGREES
>>> from math import acos, sqrt
>>> a = 3
>>> b = sqrt(27)
>>> c = 2
>>> print(f"{a} {b:.3f} {c}")
3 5.196 2
>>> alpha = acos(-5 / 2 / b / c) * TODEGREES
>>> beta = acos(-4 / 2 / a / c) * TODEGREES
>>> gamma = acos(-22 / 2 / a / b) * TODEGREES
>>> print(f"{alpha:.2f} {beta:.2f} {gamma:.2f}")
103.92 109.47 134.88
>>> niggli_matrix_form = wulf.niggli(a, b, c, alpha, beta, gamma, verbose=True) 
               A         B         C        xi        eta      zeta
start:       9.0000  27.0000   4.0000  -5.0000  -4.0000 -22.0000
2 appl. to   9.0000  27.0000   4.0000  -5.0000  -4.0000 -22.0000
1 appl. to   9.0000   4.0000  27.0000  -5.0000 -22.0000  -4.0000
4 appl. to   4.0000   9.0000  27.0000 -22.0000  -5.0000  -4.0000
5 appl. to   4.0000   9.0000  27.0000 -22.0000  -5.0000  -4.0000
4 appl. to   4.0000   9.0000  14.0000  -4.0000  -9.0000  -4.0000
6 appl. to   4.0000   9.0000  14.0000  -4.0000  -9.0000  -4.0000
4 appl. to   4.0000   9.0000   9.0000  -8.0000  -1.0000  -4.0000
7 appl. to   4.0000   9.0000   9.0000  -8.0000  -1.0000  -4.0000
3 appl. to   4.0000   9.0000   9.0000  -9.0000  -1.0000   4.0000
5 appl. to   4.0000   9.0000   9.0000   9.0000   1.0000   4.0000
3 appl. to   4.0000   9.0000   9.0000  -9.0000  -3.0000   4.0000
result:      4.0000   9.0000   9.0000   9.0000   3.0000   4.0000
>>> niggli_matrix_form
array([[4. , 9. , 9. ],
       [4.5, 1.5, 2. ]])