1 /*! \file normalisation.h
2 * \brief Convention-dependent coefficients for VSWFs.
4 * See also @ref qpms_normalisation_t and @ref vswf_conventions.
6 #ifndef NORMALISATION_H
7 #define NORMALISATION_H
9 #include "qpms_types.h"
10 #include "qpms_error.h"
16 /// Returns the (real positive) common norm factor of a given normalisation compared to the reference convention.
17 /** Does NOT perform the inversion if QPMS_NORMALISATION_INVERSE is set. */
18 static inline double qpms_normalisation_normfactor(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
19 switch (QPMS_EXPECT(norm
& QPMS_NORMALISATION_NORM_BITS
, norm
& QPMS_NORMALISATION_DEFAULT
)) {
20 case QPMS_NORMALISATION_NORM_POWER
:
22 case QPMS_NORMALISATION_NORM_SPHARM
:
24 case QPMS_NORMALISATION_NORM_NONE
: // TODO more precision
25 return sqrt(l
*(l
+1) * 4*M_PI
/ (2*l
+1)) *
26 exp(0.5*(lgamma(l
+m
+1) - lgamma(l
-m
+1)));
34 /// Returns the factors of a magnetic basis VSWF of a given convention compared to the reference convention.
36 * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley
37 * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.)
39 static inline complex double qpms_normalisation_factor_M_noCS(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
40 complex double fac
= qpms_normalisation_normfactor(norm
, l
, m
);
41 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_M_MINUS
)) fac
*= -1;
42 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_M_I
)) fac
*= I
;
43 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_INVERSE
)) fac
= 1/fac
;
48 /// Returns the factors of a magnetic basis VSWF of a given convention compared to the reference convention.
50 * This version takes into account the Condon-Shortley phase bit.
51 * Do not use if the C.-S. has already been taken into account e.g. in
52 * a `gsl_sf_legendre_*_e()` call.
54 static inline complex double qpms_normalisation_factor_M(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
55 complex double fac
= qpms_normalisation_factor_M_noCS(norm
, l
, m
);
56 return ((norm
& QPMS_NORMALISATION_CSPHASE
) && (m
% 2)) ? -fac
: fac
;
60 /// Returns the factors of a electric basis VSWF of a given convention compared to the reference convention.
62 * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley
63 * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.)
65 static inline complex double qpms_normalisation_factor_N_noCS(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
66 complex double fac
= qpms_normalisation_normfactor(norm
, l
, m
);
67 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_N_MINUS
)) fac
*= -1;
68 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_N_I
)) fac
*= I
;
69 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_INVERSE
)) fac
= 1/fac
;
74 /// Returns the factors of a electric basis VSWF of a given convention compared to the reference convention.
76 * This version takes into account the Condon-Shortley phase bit.
77 * Do not use if the C.-S. has already been taken into account e.g. in
78 * a `gsl_sf_legendre_*_e()` call.
80 static inline complex double qpms_normalisation_factor_N(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
81 complex double fac
= qpms_normalisation_factor_N_noCS(norm
, l
, m
);
82 return ((norm
& QPMS_NORMALISATION_CSPHASE
) && (m
% 2)) ? -fac
: fac
;
86 /// 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.
87 static inline complex double qpms_normalisation_factor_N_M(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
88 return qpms_normalisation_factor_N_noCS(norm
, l
, m
)
89 / qpms_normalisation_factor_M_noCS(norm
, l
, m
);
93 /// Returns the factors of a longitudinal basis VSWF of a given convention compared to the reference convention.
95 * This version ignores the Condon-Shortley phase bit (perhaps because the Condon-Shortley
96 * phase is already taken into account in a `gsl_sf_legendre_*_e()` call.)
98 static inline complex double qpms_normalisation_factor_L_noCS(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
99 complex double fac
= qpms_normalisation_normfactor(norm
, l
, m
);
100 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_L_MINUS
)) fac
*= -1;
101 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_L_I
)) fac
*= I
;
102 if (QPMS_UNLIKELY(norm
& QPMS_NORMALISATION_INVERSE
)) fac
= 1/fac
;
106 /// Returns the factors of a longitudinal basis VSWF of a given convention compared to the reference convention.
108 * This version takes into account the Condon-Shortley phase bit.
109 * Do not use if the C.-S. has already been taken into account e.g. in
110 * a `gsl_sf_legendre_*_e()` call.
112 static inline complex double qpms_normalisation_factor_L(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
113 complex double fac
= qpms_normalisation_factor_L_noCS(norm
, l
, m
);
114 return ((norm
& QPMS_NORMALISATION_CSPHASE
) && (m
% 2)) ? -fac
: fac
;
117 /// Returns the factors of a basis VSWF of a given convention compared to the reference convention.
118 static inline complex double qpms_normalisation_factor_uvswfi(const qpms_normalisation_t norm
, qpms_uvswfi_t ui
) {
119 qpms_vswf_type_t t
; qpms_m_t m
; qpms_l_t l
;
120 qpms_uvswfi2tmn(ui
, &t
, &m
, &l
);
122 case QPMS_VSWF_MAGNETIC
:
123 return qpms_normalisation_factor_M(norm
, l
, m
);
124 case QPMS_VSWF_ELECTRIC
:
125 return qpms_normalisation_factor_N(norm
, l
, m
);
126 case QPMS_VSWF_LONGITUDINAL
:
127 return qpms_normalisation_factor_L(norm
, l
, m
);
134 /// Returns normalisation flags corresponding to the dual spherical harmonics / waves.
136 * This reverses the normalisation factors returned by qpms_normalisation_factor_*
137 * and conjugates the asimuthal part for complex spherical harmonics,
138 * \f$ e^{\pm im\phi} \leftrightarrow e^{\mp im\phi} \f$.
140 static inline qpms_normalisation_t
qpms_normalisation_dual(qpms_normalisation_t norm
) {
141 norm
^= QPMS_NORMALISATION_INVERSE
;
142 if (!(norm
& QPMS_NORMALISATION_SPHARM_REAL
))
143 norm
^= QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
;
147 /// Returns the asimuthal part of a spherical harmonic.
148 /** Returns \f[ e^{im\phi} \f] for standard complex spherical harmonics,
149 * \f[ e^{-im\phi \f] for complex spherical harmonics
150 * and QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE set.
152 * For real spherical harmonics, this gives
154 * \sqrt{2}\cos{m \phi} \quad \mbox{if } m>0, \\
155 * \sqrt{2}\sin{m \phi} \quad \mbox{if } m<0, \\
156 * 0 \quad \mbox{if } m>0. \\
159 static inline complex double qpms_spharm_azimuthal_part(qpms_normalisation_t norm
, qpms_m_t m
, double phi
) {
160 switch(QPMS_EXPECT(norm
, QPMS_NORMALISATION_DEFAULT
)
161 & (QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
| QPMS_NORMALISATION_SPHARM_REAL
)) {
163 return cexp(I
*m
*phi
);
164 case QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
:
165 return cexp(-I
*m
*phi
);
166 case QPMS_NORMALISATION_SPHARM_REAL
:
167 if (m
> 0) return M_SQRT2
* cos(m
*phi
);
168 else if (m
< 0) return M_SQRT2
* sin(m
*phi
);
175 /// Returns derivative of the asimuthal part of a spherical harmonic divided by \a m.
178 * This is used to evaluate the VSWFs together with the \a pi member array of the
179 * qpms_pitau_t structure.
181 * Returns \f[ i e^{im\phi} \f] for standard complex spherical harmonics,
182 * \f[-i e^{-i\phi \f] for complex spherical harmonics
183 * and QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE set.
185 * For real spherical harmonics, this gives
187 * -\sqrt{2}\sin{m \phi} \quad \mbox{if } m>0, \\
188 * \sqrt{2}\cos{m \phi} \quad \mbox{if } m<0, \\
189 * -1 \quad \mbox{if } \mbox{if }m=0. \\
192 * (The value returned for \f$ m = 0 \f$ should not actually be used for
193 * anything except for multiplying by zero.)
197 static inline complex double qpms_spharm_azimuthal_part_derivative_div_m(qpms_normalisation_t norm
, qpms_m_t m
, double phi
) {
199 switch(QPMS_EXPECT(norm
, QPMS_NORMALISATION_DEFAULT
)
200 & (QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
| QPMS_NORMALISATION_SPHARM_REAL
)) {
202 return I
*cexp(I
*m
*phi
);
203 case QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
:
204 return -I
*cexp(-I
*m
*phi
);
205 case QPMS_NORMALISATION_SPHARM_REAL
:
206 if (m
> 0) return -M_SQRT2
* sin(m
*phi
);
207 else if (m
< 0) return M_SQRT2
* cos(m
*phi
);
214 #if 0 // legacy code moved from qpms_types.h. TODO cleanup
215 /// Returns the normalisation convention code without the Condon-Shortley phase.
216 static inline int qpms_normalisation_t_normonly(qpms_normalisation_t norm
) {
217 return norm
& (~QPMS_NORMALISATION_T_CSBIT
);
220 /* Normalisation of the spherical waves is now scattered in at least three different files:
221 * here, we have the norm in terms of radiated power of outgoing wave.
222 * In file legendre.c, function qpms_pitau_get determines the norm used in the vswf.c
223 * spherical vector wave norms. The "dual" waves in vswf.c use the ..._abssquare function below.
224 * In file translations.c, the normalisations are again set by hand using the normfac and lognormfac
229 // relative to QPMS_NORMALISATION_KRISTENSSON_CS, i.e.
230 // P_l^m[normtype] = P_l^m[Kristensson]
231 static inline double qpms_normalisation_t_factor(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
232 int csphase
= qpms_normalisation_t_csphase(norm
);
233 norm
= qpms_normalisation_t_normonly(norm
);
236 case QPMS_NORMALISATION_KRISTENSSON
:
239 case QPMS_NORMALISATION_TAYLOR
:
240 factor
= sqrt(l
*(l
+1));
242 case QPMS_NORMALISATION_NONE
:
243 factor
= sqrt(l
*(l
+1) * 4 * M_PI
/ (2*l
+1) * exp(lgamma(l
+m
+1)-lgamma(l
-m
+1)));
245 #ifdef USE_XU_ANTINORMALISATION // broken probably in legendre.c
246 case QPMS_NORMALISATION_XU
:
247 factor
= sqrt(4 * M_PI
) / (2*l
+1) * exp(lgamma(l
+m
+1)-lgamma(l
-m
+1));
253 factor
*= (m
%2)?(-csphase
):1;
258 // TODO move elsewhere
259 static inline double qpms_normalisation_t_factor_abssquare(qpms_normalisation_t norm
, qpms_l_t l
, qpms_m_t m
) {
260 norm
= qpms_normalisation_t_normonly(norm
);
262 case QPMS_NORMALISATION_KRISTENSSON
:
265 case QPMS_NORMALISATION_TAYLOR
:
268 case QPMS_NORMALISATION_NONE
:
269 return l
*(l
+1) * 4 * M_PI
/ (2*l
+1) * exp(lgamma(l
+m
+1)-lgamma(l
-m
+1));
271 #ifdef USE_XU_ANTINORMALISATION // broken probably in legendre.c
272 case QPMS_NORMALISATION_XU
:
274 double fac
= sqrt(4 * M_PI
) / (2*l
+1) * exp(lgamma(l
+m
+1)-lgamma(l
-m
+1));
286 #endif //NORMALISATION_H