/*! \file normalisation.h * \brief Convention-dependent coefficients for VSWFs. * * See also @ref qpms_normalisation_t and @ref vswf_conventions. */ #ifndef NORMALISATION_H #define NORMALISATION_H #include "qpms_types.h" #include "qpms_error.h" #include #include #include "indexing.h" #include "optim.h" /// Returns the (real positive) common norm factor of a given normalisation compared to the reference convention. /** Does NOT perform the inversion if QPMS_NORMALISATION_INVERSE is set. */ static inline double qpms_normalisation_normfactor(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { switch (QPMS_EXPECT(norm & QPMS_NORMALISATION_NORM_BITS, norm & QPMS_NORMALISATION_DEFAULT)) { case QPMS_NORMALISATION_NORM_POWER: return 1; case QPMS_NORMALISATION_NORM_SPHARM: return sqrt(l*(l+1)); case QPMS_NORMALISATION_NORM_NONE: // TODO more precision return sqrt(l*(l+1) * 4*M_PI / (2*l+1)) * exp(0.5*(lgamma(l+m+1) - lgamma(l-m+1))); default: QPMS_WTF; } } /// Returns the factors of a magnetic basis VSWF of a given convention compared to the reference convention. /** * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.) */ static inline complex double qpms_normalisation_factor_M_noCS(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_normfactor(norm, l, m); if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_M_MINUS)) fac *= -1; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_M_I)) fac *= I; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_INVERSE)) fac = 1/fac; return fac; } /// Returns the factors of a magnetic basis VSWF of a given convention compared to the reference convention. /** * This version takes into account the Condon-Shortley phase bit. * Do not use if the C.-S. has already been taken into account e.g. in * a `gsl_sf_legendre_*_e()` call. */ static inline complex double qpms_normalisation_factor_M(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_factor_M_noCS(norm, l, m); return ((norm & QPMS_NORMALISATION_CSPHASE) && (m % 2)) ? -fac : fac; } /// Returns the factors of a electric basis VSWF of a given convention compared to the reference convention. /** * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.) */ static inline complex double qpms_normalisation_factor_N_noCS(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_normfactor(norm, l, m); if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_N_MINUS)) fac *= -1; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_N_I)) fac *= I; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_INVERSE)) fac = 1/fac; return fac; } /// Returns the factors of a electric basis VSWF of a given convention compared to the reference convention. /** * This version takes into account the Condon-Shortley phase bit. * Do not use if the C.-S. has already been taken into account e.g. in * a `gsl_sf_legendre_*_e()` call. */ static inline complex double qpms_normalisation_factor_N(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_factor_N_noCS(norm, l, m); return ((norm & QPMS_NORMALISATION_CSPHASE) && (m % 2)) ? -fac : fac; } /// Returns the factors of a electric basis VSWF divided by the factor of a magnetic VWFS of a given convention, compared to the reference one. static inline complex double qpms_normalisation_factor_N_M(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { return qpms_normalisation_factor_N_noCS(norm, l, m) / qpms_normalisation_factor_M_noCS(norm, l, m); } /// Returns the factors of a longitudinal basis VSWF of a given convention compared to the reference convention. /** * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.) */ static inline complex double qpms_normalisation_factor_L_noCS(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_normfactor(norm, l, m); if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_L_MINUS)) fac *= -1; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_L_I)) fac *= I; if (QPMS_UNLIKELY(norm & QPMS_NORMALISATION_INVERSE)) fac = 1/fac; return fac; } /// Returns the factors of a longitudinal basis VSWF of a given convention compared to the reference convention. /** * This version takes into account the Condon-Shortley phase bit. * Do not use if the C.-S. has already been taken into account e.g. in * a `gsl_sf_legendre_*_e()` call. */ static inline complex double qpms_normalisation_factor_L(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { complex double fac = qpms_normalisation_factor_L_noCS(norm, l, m); return ((norm & QPMS_NORMALISATION_CSPHASE) && (m % 2)) ? -fac : fac; } /// Returns the factors of a basis VSWF of a given convention compared to the reference convention. static inline complex double qpms_normalisation_factor_uvswfi(const qpms_normalisation_t norm, qpms_uvswfi_t ui) { qpms_vswf_type_t t; qpms_m_t m; qpms_l_t l; qpms_uvswfi2tmn(ui, &t, &m, &l); switch(t) { case QPMS_VSWF_MAGNETIC: return qpms_normalisation_factor_M(norm, l, m); case QPMS_VSWF_ELECTRIC: return qpms_normalisation_factor_N(norm, l, m); case QPMS_VSWF_LONGITUDINAL: return qpms_normalisation_factor_L(norm, l, m); default: QPMS_WTF; } } /// Returns normalisation flags corresponding to the dual spherical harmonics / waves. /** * This reverses the normalisation factors returned by qpms_normalisation_factor_* * and conjugates the asimuthal part for complex spherical harmonics, * \f$ e^{\pm im\phi} \leftrightarrow e^{\mp im\phi} \f$. */ static inline qpms_normalisation_t qpms_normalisation_dual(qpms_normalisation_t norm) { norm ^= QPMS_NORMALISATION_INVERSE; if (!(norm & QPMS_NORMALISATION_SPHARM_REAL)) norm ^= QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE; return norm; } /// Returns the asimuthal part of a spherical harmonic. /** Returns \f[ e^{im\phi} \f] for standard complex spherical harmonics, * \f[ e^{-im\phi \f] for complex spherical harmonics * and QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE set. * * For real spherical harmonics, this gives * \f[ * \sqrt{2}\cos{m \phi} \quad \mbox{if } m>0, \\ * \sqrt{2}\sin{m \phi} \quad \mbox{if } m<0, \\ * 0 \quad \mbox{if } m>0. \\ * \f] */ static inline complex double qpms_spharm_azimuthal_part(qpms_normalisation_t norm, qpms_m_t m, double phi) { switch(QPMS_EXPECT(norm, QPMS_NORMALISATION_DEFAULT) & (QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE | QPMS_NORMALISATION_SPHARM_REAL)) { case 0: return cexp(I*m*phi); case QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE: return cexp(-I*m*phi); case QPMS_NORMALISATION_SPHARM_REAL: if (m > 0) return M_SQRT2 * cos(m*phi); else if (m < 0) return M_SQRT2 * sin(m*phi); else return 1.; default: QPMS_WTF; } } /// Returns derivative of the asimuthal part of a spherical harmonic divided by \a m. /** * * This is used to evaluate the VSWFs together with the \a pi member array of the * qpms_pitau_t structure. * * Returns \f[ i e^{im\phi} \f] for standard complex spherical harmonics, * \f[-i e^{-i\phi \f] for complex spherical harmonics * and QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE set. * * For real spherical harmonics, this gives * \f[ * -\sqrt{2}\sin{m \phi} \quad \mbox{if } m>0, \\ * \sqrt{2}\cos{m \phi} \quad \mbox{if } m<0, \\ * -1 \quad \mbox{if } \mbox{if }m=0. \\ * \f] * * (The value returned for \f$ m = 0 \f$ should not actually be used for * anything except for multiplying by zero.) * * */ static inline complex double qpms_spharm_azimuthal_part_derivative_div_m(qpms_normalisation_t norm, qpms_m_t m, double phi) { if(m==0) return 0; switch(QPMS_EXPECT(norm, QPMS_NORMALISATION_DEFAULT) & (QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE | QPMS_NORMALISATION_SPHARM_REAL)) { case 0: return I*cexp(I*m*phi); case QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE: return -I*cexp(-I*m*phi); case QPMS_NORMALISATION_SPHARM_REAL: if (m > 0) return -M_SQRT2 * sin(m*phi); else if (m < 0) return M_SQRT2 * cos(m*phi); else return -1; default: QPMS_WTF; } } #if 0 // legacy code moved from qpms_types.h. TODO cleanup /// Returns the normalisation convention code without the Condon-Shortley phase. static inline int qpms_normalisation_t_normonly(qpms_normalisation_t norm) { return norm & (~QPMS_NORMALISATION_T_CSBIT); } /* Normalisation of the spherical waves is now scattered in at least three different files: * here, we have the norm in terms of radiated power of outgoing wave. * In file legendre.c, function qpms_pitau_get determines the norm used in the vswf.c * spherical vector wave norms. The "dual" waves in vswf.c use the ..._abssquare function below. * In file translations.c, the normalisations are again set by hand using the normfac and lognormfac * functions. */ #include #include // relative to QPMS_NORMALISATION_KRISTENSSON_CS, i.e. // P_l^m[normtype] = P_l^m[Kristensson] static inline double qpms_normalisation_t_factor(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { int csphase = qpms_normalisation_t_csphase(norm); norm = qpms_normalisation_t_normonly(norm); double factor; switch (norm) { case QPMS_NORMALISATION_KRISTENSSON: factor = 1.; break; case QPMS_NORMALISATION_TAYLOR: factor = sqrt(l*(l+1)); break; case QPMS_NORMALISATION_NONE: factor = sqrt(l*(l+1) * 4 * M_PI / (2*l+1) * exp(lgamma(l+m+1)-lgamma(l-m+1))); break; #ifdef USE_XU_ANTINORMALISATION // broken probably in legendre.c case QPMS_NORMALISATION_XU: factor = sqrt(4 * M_PI) / (2*l+1) * exp(lgamma(l+m+1)-lgamma(l-m+1)); break; #endif default: assert(0); } factor *= (m%2)?(-csphase):1; return factor; } // TODO move elsewhere static inline double qpms_normalisation_t_factor_abssquare(qpms_normalisation_t norm, qpms_l_t l, qpms_m_t m) { norm = qpms_normalisation_t_normonly(norm); switch (norm) { case QPMS_NORMALISATION_KRISTENSSON: return 1.; break; case QPMS_NORMALISATION_TAYLOR: return l*(l+1); break; case QPMS_NORMALISATION_NONE: return l*(l+1) * 4 * M_PI / (2*l+1) * exp(lgamma(l+m+1)-lgamma(l-m+1)); break; #ifdef USE_XU_ANTINORMALISATION // broken probably in legendre.c case QPMS_NORMALISATION_XU: { double fac = sqrt(4 * M_PI) / (2*l+1) * exp(lgamma(l+m+1)-lgamma(l-m+1)); return fac * fac; } break; #endif default: assert(0); return NAN; } } #endif #endif //NORMALISATION_H