diff --git a/README.rst b/README.rst index 449c06b..698f8fe 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ Installation The package depends on numpy, scipy, cython and GSL (>= 2.0). The first three can be obtained by pip. If you have a recent enough OS, you can get GSL easily from the repositories; on Debian and derivatives, -just run `apt-get install libgsl-dev` under root. Alternatively, +just run ``apt-get install libgsl-dev`` under root. Alternatively, you can `get the source `_ get the source and compile it yourself. diff --git a/qpms/qpms_c.pyx b/qpms/qpms_c.pyx index 1bfae8c..6e29399 100644 --- a/qpms/qpms_c.pyx +++ b/qpms/qpms_c.pyx @@ -629,7 +629,7 @@ cdef class cquat: Wrapper of the qpms_quat_t object, with the functionality to evaluate Wigner D-matrix elements. ''' - cdef qpms_quat_t q + cdef readonly qpms_quat_t q def __cinit__(self, double w, double x, double y, double z): cdef qpms_quat4d_t p @@ -727,3 +727,54 @@ cdef class cquat: return 0 return qpms_wignerD_elem(self.q, l, mp, m) +cdef class irot3: + ''' + Wrapper over the C type qpms_irot3_t. + ''' + cdef qpms_irot3_t qd + + def __cinit__(self, cquat q, short det): # TODO implement a constructor with no arguments + if (det != 1 and det != -1): + raise ValueError("Improper rotation determinant has to be 1 or -1") + self.qd.rot = q.normalise().q + self.qd.det = det + + property rot: + ''' + The proper rotation part of the irot3 type. + ''' + def __get__(self): + res = cquat(0,0,0,0) + res.q = self.qd.rot + return res + def __set__(self, cquat r): + # TODO check for non-zeroness and throw an exception if norm is zero + self.qd.rot = r.normalise().q + + property det: + ''' + The determinant of the improper rotation. + ''' + def __get__(self): + return self.qd.det + def __set__(self, d): + d = int(d) + if (d != 1 and d != -1): + raise ValueError("Improper rotation determinant has to be 1 or -1") + self.qd.det = d + + def __repr__(self): # TODO make this look like a quaternion with i,j,k + return '(' + repr(self.rot) + ', ' + repr(self.det) + ')' + + def __mul__(irot3 self, irot3 other): + res = irot3(cquat(1,0,0,0), 1) + res.qd = qpms_irot3_mult(self.qd, other.qd) + return res + + def __pow__(irot3 self, int n, _): + res = irot3(cquat(1,0,0,0), 1) + res.qd = qpms_irot3_pow(self.qd, n) + return res + + + diff --git a/qpms/qpms_cdefs.pxd b/qpms/qpms_cdefs.pxd index cdfebd7..bc203ca 100644 --- a/qpms/qpms_cdefs.pxd +++ b/qpms/qpms_cdefs.pxd @@ -97,6 +97,11 @@ cdef extern from "wigner.h": qpms_quat_t qpms_quat_pow(qpms_quat_t q, double exponent) cdouble qpms_wignerD_elem(qpms_quat_t q, qpms_l_t l, qpms_m_t mp, qpms_m_t m) + struct qpms_irot3_t: + qpms_quat_t rot + short det + qpms_irot3_t qpms_irot3_mult(qpms_irot3_t p, qpms_irot3_t q) + qpms_irot3_t qpms_irot3_pow(qpms_irot3_t p, int n) #cdef extern from "numpy/arrayobject.h": diff --git a/qpms/wigner.h b/qpms/wigner.h index 5124e7c..b575e68 100644 --- a/qpms/wigner.h +++ b/qpms/wigner.h @@ -6,6 +6,8 @@ #include "qpms_types.h" +#include "tiny_inlines.h" + /// Quaternion type. /** * Internaly represented as a pair of complex numbers, @@ -20,6 +22,12 @@ typedef struct qpms_quat4d_t { double c1, ci, cj, ck; } qpms_quat4d_t; +/// 3D improper rotations represented as a quaternion and a sign of the determinant. +typedef struct qpms_irot3_t { + qpms_quat_t rot; ///< Quaternion representing the rotation part. + short det; ///< Determinant of the transformation (valid values are 1 (rotation) or -1 (improper rotation) +} qpms_irot3_t; + /// Conversion from the 4*double to the 2*complex quaternion. // TODO is this really correct? // I.e. do the axis from moble's text match this convention? @@ -134,4 +142,29 @@ static inline qpms_quat_t qpms_quat_pow(const qpms_quat_t q, const double expone complex double qpms_wignerD_elem(qpms_quat_t q, qpms_l_t l, qpms_m_t mp, qpms_m_t m); +static inline int qpms_irot3_checkdet(const qpms_irot3_t p) { + if (p.det != 1 && p.det != -1) abort(); + return 0; +} + +/// Improper rotation multiplication. +static inline qpms_irot3_t qpms_irot3_mult(const qpms_irot3_t p, const qpms_irot3_t q) { +#ifndef NDEBUG + qpms_irot3_checkdet(p); + qpms_irot3_checkdet(q); +#endif + const qpms_irot3_t r = {qpms_quat_normalise(qpms_quat_mult(p.rot, q.rot)), p.det*q.det}; + return r; +} + +/// Improper rotation power \f$ p^n \f$. +static inline qpms_irot3_t qpms_irot3_pow(const qpms_irot3_t p, int n) { +#ifndef NDEBUG + qpms_irot3_checkdet(p); +#endif + const qpms_irot3_t r = {qpms_quat_normalise(qpms_quat_pow(p.rot, n)), + p.det == -1 ? min1pow(n) : 1}; + return r; +} + #endif //QPMS_WIGNER_H diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 62f3e7e..e016566 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ setup(name='qpms', install_requires=['cython>=0.21','quaternion','spherical_functions', #'py_gmm' # no longer needed ], + # TODO implement https://stackoverflow.com/questions/17366784/setuptools-unable-to-use-link-from-dependency-links and update README.md accordingly dependency_links=['https://github.com/texnokrates/py_gmm','https://github.com/moble/quaternion','https://github.com/moble/spherical_functions'], ext_modules=cythonize([qpms_c], include_path=['qpms']), cmdclass = {'build_ext': build_ext},