SAF
Loading...
Searching...
No Matches
saf_sh_internal.c
Go to the documentation of this file.
1/*
2 * Copyright 2016-2018 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
38#include "saf_sh.h"
39#include "saf_sh_internal.h"
40
41/* ========================================================================== */
42/* Misc. Internal Functions */
43/* ========================================================================== */
44
46(
47 int j1,
48 int j2,
49 int j3,
50 int m1,
51 int m2,
52 int m3
53)
54{
55 int t, N_t;
56 float w, coeff1, coeff2, tri_coeff, sum_s, x_t;
57
58 /* Check selection rules (http://mathworld.wolfram.com/Wigner3j-Symbol.html) */
59 if (abs(m1)>abs(j1) || abs(m2)>abs(j2) || abs(m3)>abs(j3))
60 w = 0.0f;
61 else if (m1+m2+m3 !=0)
62 w = 0.0f;
63 else if ( j3<abs(j1-j2) || j3>(j1+j2) ) /* triangle inequality */
64 w = 0.0f;
65
66 else {
67 /* evaluate the Wigner-3J symbol using the Racah formula (http://mathworld.wolfram.com/Wigner3j-Symbol.html)
68 number of terms for the summation */
69 N_t = -1000000000;
70 N_t = j1+m1 > N_t ? j1+m1 : N_t;
71 N_t = j1-m1 > N_t ? j1-m1 : N_t;
72 N_t = j2+m2 > N_t ? j2+m2 : N_t;
73 N_t = j2-m2 > N_t ? j2-m2 : N_t;
74 N_t = j3+m3 > N_t ? j3+m3 : N_t;
75 N_t = j3-m3 > N_t ? j3-m3 : N_t;
76 N_t = j1+j2-j3 > N_t ? j1+j2-j3 : N_t;
77 N_t = j2+j3-j1 > N_t ? j2+j3-j1 : N_t;
78 N_t = j3+j1-j2 > N_t ? j3+j1-j2 : N_t;
79
80 /* coefficients before the summation */
81 coeff1 = powf(-1.0f,(float)(j1-j2-m3));
82 coeff2 = (float)(factorial(j1+m1)*(float)factorial(j1-m1)*(float)factorial(j2+m2)*(float)factorial(j2-m2)* (float)factorial(j3+m3)*(float)factorial(j3-m3));
83 tri_coeff = (float)(factorial(j1 + j2 - j3)*(float)factorial(j1 - j2 + j3)*(float)factorial(-j1 + j2 + j3)/(float)factorial(j1 + j2 + j3 + 1));
84
85 /* summation over integers that do not result in negative factorials */
86 sum_s = 0.0f;
87 for (t = 0; t<=N_t; t++){
88
89 /* check factorial for negative values, include in sum if not */
90 if (j3-j2+t+m1 >= 0 && j3-j1+t-m2 >=0 && j1+j2-j3-t >= 0 && j1-t-m1 >=0 && j2-t+m2 >= 0){
91 x_t = (float)factorial(t)*(float)factorial(j1+j2-j3-t)*(float)factorial(j3-j2+t+m1)*(float)factorial(j3-j1+t-m2)*(float)factorial(j1-t-m1)*(float)factorial(j2-t+m2);
92 sum_s += powf(-1.0f, (float)t)/x_t;
93 }
94 }
95 w = coeff1*sqrtf(coeff2*tri_coeff)*sum_s;
96 }
97 return w;
98}
99
101(
102 int N1,
103 int N2,
104 int N,
105 float* A
106)
107{
108 int n, m, q, n1, m1, q1, n2, m2, q2, D1, D2, D3;
109 float wigner3jm, wigner3j0;
110
111 D1 = (N1+1)*(N1+1);
112 D2 = (N2+1)*(N2+1);
113 D3 = (N+1)*(N+1);
114 memset(A, 0, D1*D2*D3*sizeof(float));
115 for (n = 0; n<=N; n++){
116 for (m = -n; m<=n; m++){
117 q = n*(n+1)+m;
118
119 for (n1 = 0; n1<=N1; n1++){
120 for (m1 = -n1; m1<=n1; m1++){
121 q1 = n1*(n1+1)+m1;
122
123 for (n2 = 0; n2<=N2; n2++){
124 for (m2 = -n2; m2<=n2; m2++){
125 q2 = n2*(n2+1)+m2;
126
127 if (n<abs(n1-n2) || n>n1+n2)
128 A[q1*D2*D3 + q2*D3 + q] = 0.0f;
129 else{
130 wigner3jm = wigner_3j(n1, n2, n, m1, m2, -m);
131 wigner3j0 = wigner_3j(n1, n2, n, 0, 0, 0);
132 A[q1*D2*D3 + q2*D3 + q] = powf(-1.0f,(float)m) *
133 sqrtf((2.0f*(float)n1+1.0f)*(2.0f*(float)n2+1.0f)*(2.0f*(float)n+1.0f)/(4.0f*SAF_PI)) *
134 wigner3jm * wigner3j0;
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142}
143
144
145/* ========================================================================== */
146/* Internal functions for spherical harmonic rotations */
147/* ========================================================================== */
148
149/* Ivanic, J., Ruedenberg, K. (1998). Rotation Matrices for Real Spherical Harmonics. Direct Determination
150 * by Recursion Page: Additions and Corrections. Journal of Physical Chemistry A, 102(45), 9099?9100. */
151float getP
152(
153 int M,
154 int i,
155 int l,
156 int a,
157 int b,
158 float R_1[3][3],
159 float* R_lm1
160)
161{
162 float ret, ri1, rim1, ri0;
163
164 ri1 = R_1[i+1][2];
165 rim1 = R_1[i+1][0];
166 ri0 = R_1[i+1][1];
167
168 if (b == -l)
169 ret = ri1 * R_lm1[(a+l-1)*M+0] + rim1 * R_lm1[(a+l-1)*M+(2*l-2)];
170 else {
171 if (b == l)
172 ret = ri1*R_lm1[(a+l-1)*M+(2*l-2)] - rim1 * R_lm1[(a+l-1)*M];
173 else
174 ret = ri0 * R_lm1[(a+l-1)*M+(b+l-1)];
175 }
176
177 return ret;
178}
179
180/* Ivanic, J., Ruedenberg, K. (1998). Rotation Matrices for Real Spherical Harmonics. Direct Determination
181 * by Recursion Page: Additions and Corrections. Journal of Physical Chemistry A, 102(45), 9099?9100. */
182float getU
183(
184 int M,
185 int l,
186 int m,
187 int n,
188 float R_1[3][3],
189 float* R_lm1
190)
191{
192 return getP(M, 0, l, m, n, R_1, R_lm1);
193}
194
195/* Ivanic, J., Ruedenberg, K. (1998). Rotation Matrices for Real Spherical Harmonics. Direct Determination
196 * by Recursion Page: Additions and Corrections. Journal of Physical Chemistry A, 102(45), 9099?9100. */
197float getV
198(
199 int M,
200 int l,
201 int m,
202 int n,
203 float R_1[3][3],
204 float* R_lm1
205)
206{
207 int d;
208 float ret, p0, p1;
209
210 if (m == 0) {
211 p0 = getP(M, 1, l, 1, n, R_1, R_lm1);
212 p1 = getP(M, -1, l, -1, n, R_1, R_lm1);
213 ret = p0 + p1;
214 }
215 else {
216 if (m>0) {
217 d = m == 1 ? 1 : 0;
218 p0 = getP(M, 1, l, m - 1, n, R_1, R_lm1);
219 p1 = getP(M, -1, l, -m + 1, n, R_1, R_lm1);
220 ret = p0*sqrtf(1.0f + d) - p1*(1.0f - d);
221 }
222 else {
223 d = m == -1 ? 1 : 0;
224 p0 = getP(M, 1, l, m + 1, n, R_1, R_lm1);
225 p1 = getP(M, -1, l, -m - 1, n, R_1, R_lm1);
226 ret = p0*(1.0f - (float)d) + p1*sqrtf(1.0f + (float)d);
227 }
228 }
229
230 return ret;
231}
232
233/* Ivanic, J., Ruedenberg, K. (1998). Rotation Matrices for Real Spherical Harmonics. Direct Determination
234 * by Recursion Page: Additions and Corrections. Journal of Physical Chemistry A, 102(45), 9099?9100. */
235float getW
236(
237 int M,
238 int l,
239 int m,
240 int n,
241 float R_1[3][3],
242 float* R_lm1
243)
244{
245 float ret, p0, p1;
246 ret = 0.0f;
247
248 if (m != 0) {
249 if (m>0) {
250 p0 = getP(M, 1, l, m + 1, n, R_1, R_lm1);
251 p1 = getP(M, -1, l, -m - 1, n, R_1, R_lm1);
252 ret = p0 + p1;
253 }
254 else {
255 p0 = getP(M, 1, l, m - 1, n, R_1, R_lm1);
256 p1 = getP(M, -1, l, -m + 1, n, R_1, R_lm1);
257 ret = p0 - p1;
258 }
259 }
260 return ret;
261}
262
263
264/* ========================================================================== */
265/* Internal functions for sphESPRIT */
266/* ========================================================================== */
267
269(
270 int order,
271 int mm,
272 int ni,
273 int mu,
274 double* Wnimu
275)
276{
277 int i, n, n2_1, ind, len;
278 double* nm, *nimu, *w_nimu;
279
280 len = order*order;
281 nm = malloc1d(len*2*sizeof(double));
282 nimu = malloc1d(len*2*sizeof(double));
283 w_nimu = malloc1d(len*sizeof(double));
284 ind = 0;
285 for(n=0; n<=order-1; n++){
286 n2_1 = 2*n+1;
287 for(i=0; i<n2_1; i++, ind++){
288 nm[ind*2] = (double)n;
289 nm[ind*2+1] = -(double)n+(double)i;
290 }
291 }
292 if(mm==1){
293 for(i=0; i<len; i++){
294 nimu[i*2] = nm[i*2]+(double)ni;
295 nimu[i*2+1] = nm[i*2+1]+(double)mu;
296 }
297 }
298 else /* mm==-1 */{
299 for(i=0; i<len; i++){
300 nimu[i*2] = nm[i*2]+(double)ni;
301 nimu[i*2+1] = -nm[i*2+1]+(double)mu;
302 }
303 }
304 for(i=0; i<len; i++)
305 w_nimu[i] = sqrt( (nimu[i*2]-nimu[i*2+1]-1.0)*(nimu[i*2]-nimu[i*2+1])/((2.0*nimu[i*2]-1.0)*(2.0*nimu[i*2]+1.0)) );
306 memset(Wnimu, 0, len*len*sizeof(double));
307 for(i=0; i<len; i++)
308 Wnimu[i*len+i] = w_nimu[i];
309
310 free(nm);
311 free(nimu);
312 free(w_nimu);
313}
314
316(
317 int order,
318 int ni,
319 int mu,
320 double* Vnimu
321)
322{
323 int i, n, n2_1, ind, len;
324 double* nm, *nimu, *v_nimu;
325
326 len = order*order;
327 nm = malloc1d(len*2*sizeof(double));
328 nimu = malloc1d(len*2*sizeof(double));
329 v_nimu = malloc1d(len*sizeof(double));
330 ind = 0;
331 for(n=0; n<=order-1; n++){
332 n2_1 = 2*n+1;
333 for(i=0; i<n2_1; i++, ind++){
334 nm[ind*2] = (double)n;
335 nm[ind*2+1] = -(double)n+(double)i;
336 }
337 }
338 for(i=0; i<len; i++){
339 nimu[i*2] = nm[i*2]+(double)ni;
340 nimu[i*2+1] = nm[i*2+1]+(double)mu;
341 }
342 for(i=0; i<len; i++)
343 v_nimu[i] = sqrt( (nimu[i*2]-nimu[i*2+1])*(nimu[i*2]+nimu[i*2+1])/((2.0*nimu[i*2]-1.0)*(2.0*nimu[i*2]+1.0)) );
344 memset(Vnimu, 0, len*len*sizeof(double));
345 for(i=0; i<len; i++)
346 Vnimu[i*len+i] = v_nimu[i];
347
348 free(nm);
349 free(nimu);
350 free(v_nimu);
351}
352
354(
355 int order,
356 int ni,
357 int mu,
358 int* idx_nimu,
359 int* idx_nm
360)
361{
362 int i, n, n2_1, ind, len;
363 int* nm, *nimu, *qnm, *qnimu;
364
365 len = order*order;
366 nm = malloc1d(len*2*sizeof(int));
367 nimu = malloc1d(len*2*sizeof(int));
368 qnm = malloc1d(len*sizeof(int));
369 qnimu = malloc1d(len*sizeof(int));
370 ind = 0;
371 for(n=0; n<=order-1; n++){
372 n2_1 = 2*n+1;
373 for(i=0; i<n2_1; i++, ind++){
374 nm[ind*2] = n;
375 nm[ind*2+1] =-n+i;
376 }
377 }
378 for(i=0; i<len; i++){
379 nimu[i*2] = nm[i*2]+ni;
380 nimu[i*2+1] = nm[i*2+1]+mu;
381 qnm[i] = nm[i*2]*nm[i*2]+nm[i*2]+nm[i*2+1];
382 qnimu[i] = nimu[i*2]*nimu[i*2]+nimu[i*2]+nimu[i*2+1];
383 }
384 for(i=0, ind=0; i<len; i++){
385 if(abs(nimu[i*2+1])<=nimu[i*2]){
386 idx_nm[ind] = qnimu[i];
387 idx_nimu[ind] = qnm[i];
388 ind++;
389 }
390 }
391
392 free(nm);
393 free(nimu);
394 free(qnm);
395 free(qnimu);
396}
#define SAF_PI
pi constant (single precision)
long double factorial(int n)
Factorial, accurate up to n<=25.
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
Main header for the Spherical Harmonic Transform and Spherical Array Processing module (SAF_SH_MODULE...
void getVnimu(int order, int ni, int mu, double *Vnimu)
Helper function for sphESPRIT_create()
float getV(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
void gaunt_mtx(int N1, int N2, int N, float *A)
Constructs a matrix of Guant coefficients.
float getU(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
float getP(int M, int i, int l, int a, int b, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
float getW(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
void getWnimu(int order, int mm, int ni, int mu, double *Wnimu)
Helper function for sphESPRIT_create()
void muni2q(int order, int ni, int mu, int *idx_nimu, int *idx_nm)
Helper function for sphESPRIT_create()
float wigner_3j(int j1, int j2, int j3, int m1, int m2, int m3)
Computes the Wigner 3j symbol through the Racah formula found in http://mathworld....
Internal header for the Spherical Harmonic Transform and Spherical Array Processing module (SAF_SH_MO...