ampsci
c++ program for high-precision atomic structure calculations of single-valence systems
Loading...
Searching...
No Matches
Wigner369j.hpp
1#pragma once
2#include <algorithm> //std::min!
3#include <cmath>
4#include <gsl/gsl_sf_coupling.h>
5#include <optional>
6#include <utility>
7
39namespace Angular {
40
41//==============================================================================
43constexpr int l_k(int ka) { return (ka > 0) ? ka : -ka - 1; }
45constexpr int twoj_k(int ka) { return (ka > 0) ? 2 * ka - 1 : -2 * ka - 1; }
47constexpr int parity_k(int ka) {
48 return (ka % 2 == 0) ? ((ka > 0) ? 1 : -1) : ((ka < 0) ? 1 : -1);
49}
51constexpr int parity_l(int l) { return (l % 2 == 0) ? 1 : -1; }
53constexpr int l_tilde_k(int ka) { return (ka > 0) ? ka - 1 : -ka; }
55constexpr int kappa_twojl(int twoj, int l) {
56 return ((2 * l - twoj) * (twoj + 1)) / 2;
57}
59constexpr int kappa_twojpi(const int twoj, const int pi) {
60 const auto l_minus = (twoj - 1) / 2;
61 const auto l_minus_pi = (l_minus % 2 == 0) ? 1 : -1;
62 const auto l = l_minus_pi == pi ? l_minus : l_minus + 1;
63 return kappa_twojl(twoj, l); // must be simpler way?
64}
66inline constexpr bool zeroQ(double x, double eps = 1.0e-10) {
67 return x >= 0 ? x < eps : x > -eps;
68}
69
70//==============================================================================
71
73
80constexpr int indexFromKappa(int ka) {
81 return (ka < 0) ? -2 * ka - 2 : 2 * ka - 1;
82}
84constexpr int kappaFromIndex(int i) {
85 return (i % 2 == 0) ? -(i + 2) / 2 : (i + 1) / 2;
86}
88constexpr int twojFromIndex(int i) { return (i % 2 == 0) ? i + 1 : i; }
90constexpr int lFromIndex(int i) { return (i % 2 == 0) ? i / 2 : (i + 1) / 2; }
91
92//==============================================================================
94constexpr int states_below_n(int n) { return n * n - 2 * n + 1; }
95
98
104constexpr int nk_to_index(int n, int k) {
106}
107
109inline std::pair<int, int> index_to_nk(int index) {
110 // Better way? isqrt?
111 const auto n = 1 + int(std::sqrt(index + 0.01));
112 // int n = 1 + int_sqrt(index);
113 const auto kappa_index = index - states_below_n(n);
114 return {n, Angular::kappaFromIndex(kappa_index)};
115}
116
118inline int nkindex_to_kappa(int index) {
119 // Better way? isqrt?
120 const auto n = 1 + int(std::sqrt(index + 0.01));
121 // int n = 1 + int_sqrt(index);
122 const auto kappa_index = index - states_below_n(n);
123 return kappaFromIndex(kappa_index);
124}
125
127inline int nkindex_to_twoj(int index) {
128 // Better way? isqrt?
129 const auto n = 1 + int(std::sqrt(index + 0.01));
130 // int n = 1 + int_sqrt(index);
131 const auto kappa_index = index - states_below_n(n);
132 return twojFromIndex(kappa_index);
133}
134
136inline int nkindex_to_l(int index) {
137 // Better way? isqrt?
138 const auto n = 1 + int(std::sqrt(index + 0.01));
139 // int n = 1 + int_sqrt(index);
140 const auto kappa_index = index - states_below_n(n);
141 return l_k(kappaFromIndex(kappa_index));
142}
143
144//==============================================================================
146constexpr bool evenQ(int a) { return (a % 2 == 0); }
148constexpr bool evenQ_2(int two_a) { return (two_a % 4 == 0); }
150constexpr int neg1pow(int a) { return evenQ(a) ? 1 : -1; }
152constexpr int neg1pow_2(int two_a) { return evenQ_2(two_a) ? 1 : -1; }
153
154//==============================================================================
156constexpr int parity(int la, int lb, int k) {
157 return evenQ(la + lb + k) ? 1 : 0;
158}
159
160//==============================================================================
162constexpr int triangle(int j1, int j2, int J) {
163 // nb: can be called with wither j or twoj!
164 return ((j1 + j2 < J) || (std::abs(j1 - j2) > J)) ? 0 : 1;
165}
167constexpr int sumsToZero(int m1, int m2, int m3) {
168 return (m1 + m2 + m3 != 0) ? 0 : 1;
169}
170
171//==============================================================================
174
177inline double threej_2(int two_j1, int two_j2, int two_j3, int two_m1,
178 int two_m2, int two_m3) {
179 if (triangle(two_j1, two_j2, two_j3) * sumsToZero(two_m1, two_m2, two_m3) ==
180 0)
181 return 0;
182 return gsl_sf_coupling_3j(two_j1, two_j2, two_j3, two_m1, two_m2, two_m3);
183}
184
185//==============================================================================
187
190inline double special_threej_2(int two_j1, int two_j2, int two_k) {
191 if (triangle(two_j1, two_j2, two_k) == 0)
192 return 0.0;
193 if (two_k == 0) {
194 auto s = evenQ_2(two_j1 + 1) ? 1.0 : -1.0;
195 return s / std::sqrt(two_j1 + 1);
196 }
197 return gsl_sf_coupling_3j(two_j1, two_j2, two_k, -1, 1, 0);
198}
199
200//==============================================================================
202inline double cg_2(int two_j1, int two_m1, int two_j2, int two_m2, int two_J,
203 int two_M)
204// <j1 m1, j2 m2 | J M> = (-1)^(j1-j2+M) * std::sqrt(2J+1) * (j1 j2 J)
205// . (m1 m2 -M)
206// (Last term is 3j symbol)
207// Note: this function takes INTEGER values, that have already multiplied by 2!
208// Works for l and j (integer and half-integer)
209{
210 if (triangle(two_j1, two_j2, two_J) * sumsToZero(two_m1, two_m2, -two_M) == 0)
211 return 0;
212 int sign = -1;
213 if ((two_j1 - two_j2 + two_M) % 4 == 0)
214 sign = 1; // mod 4 (instead 2), since x2
215 return sign * std::sqrt(two_J + 1.) *
216 gsl_sf_coupling_3j(two_j1, two_j2, two_J, two_m1, two_m2, -two_M);
217}
218
219//==============================================================================
221inline bool sixj_zeroQ(int a, int b, int c, int d, int e, int f) {
222 // nb: works for 2*integers ONLY
223 // check triangle consitions
224 // Note: there are some 6j symbols that pass this test, though are still zero
225 // GSL calculates these to have extremely small (but non-zero) value
226 if (triangle(a, b, c) == 0)
227 return true;
228 if (triangle(c, d, e) == 0)
229 return true;
230 if (triangle(b, d, f) == 0)
231 return true;
232 if (triangle(e, f, a) == 0)
233 return true;
234 if (!evenQ(a + b + c))
235 return true;
236 if (!evenQ(c + d + e))
237 return true;
238 if (!evenQ(b + d + f))
239 return true;
240 if (!evenQ(e + f + a))
241 return true;
242 return false;
243}
244
248inline bool sixjTriads(std::optional<int> a, std::optional<int> b,
249 std::optional<int> c, std::optional<int> d,
250 std::optional<int> e, std::optional<int> f) {
251 // nb: works for 2*integers ONLY
252 // checks triangle consitions, with optional parameters
253 if (a && b && c && triangle(*a, *b, *c) == 0)
254 return false;
255 if (c && d && e && triangle(*c, *d, *e) == 0)
256 return false;
257 if (a && e && f && triangle(*a, *e, *f) == 0)
258 return false;
259 if (b && d && f && triangle(*b, *d, *f) == 0)
260 return false;
261
262 return true;
263}
264
265//==============================================================================
267inline double sixj_2(int two_j1, int two_j2, int two_j3, int two_j4, int two_j5,
268 int two_j6)
269// Calculates wigner 6j symbol:
270// {j1 j2 j3}
271// {j4 j5 j6}
272// Note: this function takes INTEGER values, that have already multiplied by 2!
273// Works for l and j (integer and half-integer)
274{
275 if (sixj_zeroQ(two_j1, two_j2, two_j3, two_j4, two_j5, two_j6))
276 return 0.0;
277 return gsl_sf_coupling_6j(two_j1, two_j2, two_j3, two_j4, two_j5, two_j6);
278}
279
280//------------------------------------------------------------------------------
282inline double ninej_2(int two_j1, int two_j2, int two_j3, int two_j4,
283 int two_j5, int two_j6, int two_j7, int two_j8,
284 int two_j9)
285// Calculates wigner 9j symbol:
286// {j1 j2 j3}
287// {j4 j5 j6}
288// {j7 j8 j9}
289// Note: this function takes INTEGER values, that have already multiplied by 2!
290// Works for l and j (integer and half-integer)
291{
292 return gsl_sf_coupling_9j(two_j1, two_j2, two_j3, two_j4, two_j5, two_j6,
293 two_j7, two_j8, two_j9);
294}
295
296//==============================================================================
298inline double Ck_kk(int k, int ka, int kb)
299// Reduced (relativistic) angular ME:
300// <ka||C^k||kb> = (-1)^(ja+1/2) * srt([ja][jb]) * 3js(ja jb k, -1/2 1/2 0) * Pi
301// Note: takes in kappa! (not j!)
302{
303 if (parity(l_k(ka), l_k(kb), k) == 0) {
304 return 0.0;
305 }
306 auto two_ja = twoj_k(ka);
307 auto two_jb = twoj_k(kb);
308 // auto sign = ((two_ja + 1) / 2 % 2 == 0) ? 1 : -1;
309 auto sign = evenQ_2(two_ja + 1) ? 1 : -1;
310 auto f = std::sqrt((two_ja + 1) * (two_jb + 1));
311 auto g = special_threej_2(two_ja, two_jb, 2 * k);
312 return sign * f * g;
313}
314
316inline double Ck_kk_mmq(int k, int ka, int kb, int twoma, int twomb, int twoq) {
317 const auto tja = twoj_k(ka);
318 const auto tjb = twoj_k(kb);
319 return neg1pow_2(tja - twoma) *
320 threej_2(tja, 2 * k, tjb, -twoma, twoq, twomb) * Ck_kk(k, ka, kb);
321}
322
324inline bool Ck_kk_SR(int k, int ka, int kb) {
325 if (parity(l_k(ka), l_k(kb), k) == 0)
326 return false;
327 if (triangle(twoj_k(ka), twoj_k(kb), 2 * k) == 0)
328 return false;
329 return true;
330}
331
333inline double tildeCk_kk(int k, int ka, int kb) {
334 // tildeCk_kk = (-1)^{ja+1/2}*Ck_kk
335 auto m1tjph = evenQ_2(twoj_k(ka) + 1) ? 1 : -1;
336 return m1tjph * Ck_kk(k, ka, kb);
337}
338
341inline std::pair<int, int> kminmax_Ck(int ka, int kb) {
342 // j = |k|-0.5
343 // kmin = |ja-jb| = | |ka| - |kb| |
344 // kmax = ja+jb = |ka| + |kb| - 1
345 const auto aka = std::abs(ka);
346 const auto akb = std::abs(kb);
347
348 auto kmin = std::abs(aka - akb);
349 auto kmax = aka + akb - 1;
350 // account for parity
351 if (parity(l_k(ka), l_k(kb), kmin) == 0)
352 ++kmin;
353 if (parity(l_k(ka), l_k(kb), kmax) == 0)
354 --kmax;
355
356 return {kmin, kmax};
357}
358
359//==============================================================================
361
371inline double S_kk(int ka, int kb) {
372 auto la = l_k(ka);
373 if (la != l_k(kb))
374 return 0.0;
375 auto tja = twoj_k(ka);
376 auto tjb = twoj_k(kb);
377 if (triangle(tja, 2, tjb) == 0)
378 return 0;
379 auto sign = (((tja + 2 * la + 3) / 2) % 2 == 0) ? 1 : -1;
380 auto f = std::sqrt((tja + 1) * (tjb + 1) * 1.5);
381 // auto sixj = gsl_sf_coupling_6j(tja, 2, tjb, 1, 2 * la, 1);
382 auto sixj_sign = ((ka + kb) % 2 == 0) ? 1.0 : -1.0;
383 auto sixj = 0.5 * sixj_sign *
384 std::sqrt(std::fabs(((ka - 1) * (ka - 1) - kb * kb) /
385 (3.0 * ka * (1.0 + 2.0 * ka))));
386 return sign * f * sixj;
387}
388
389} // namespace Angular
Angular provides functions and classes for calculating and storing angular factors (3,...
Definition CkTable.cpp:7
constexpr bool zeroQ(double x, double eps=1.0e-10)
Checks if a double is within eps of 0.0 [eps=1.0e-10 by default].
Definition Wigner369j.hpp:66
int nkindex_to_kappa(int index)
Returns kappa, given nk_index.
Definition Wigner369j.hpp:118
constexpr int states_below_n(int n)
Returns number of possible states below given n.
Definition Wigner369j.hpp:94
std::pair< int, int > kminmax_Ck(int ka, int kb)
Returns [k_min, k_kmax] for C^k (min/max non-zero k, given kappa_a, kappa_b) Accounts for parity.
Definition Wigner369j.hpp:341
double Ck_kk_mmq(int k, int ka, int kb, int twoma, int twomb, int twoq)
Full C^k_q matrix element - rarely used.
Definition Wigner369j.hpp:316
double threej_2(int two_j1, int two_j2, int two_j3, int two_m1, int two_m2, int two_m3)
Calculates wigner 3j symbol. Takes in 2*j (or 2*l) - intput is integer.
Definition Wigner369j.hpp:177
double cg_2(int two_j1, int two_m1, int two_j2, int two_m2, int two_J, int two_M)
Clebsh-Gordon coeficient <j1 m1, j2 m2 | J M> [takes 2*j, as int].
Definition Wigner369j.hpp:202
double sixj_2(int two_j1, int two_j2, int two_j3, int two_j4, int two_j5, int two_j6)
6j symbol {j1 j2 j3 \ j4 j5 j6} - [takes 2*j as int]
Definition Wigner369j.hpp:267
constexpr int nk_to_index(int n, int k)
return nk_index given {n, kappa}: nk_index(n,k) := n^2 - 2n + 1 + kappa_index
Definition Wigner369j.hpp:104
constexpr int lFromIndex(int i)
returns l, given kappa_index
Definition Wigner369j.hpp:90
constexpr int neg1pow(int a)
Evaluates (-1)^{a} (for integer a)
Definition Wigner369j.hpp:150
int nkindex_to_l(int index)
Returns l, given nk_index.
Definition Wigner369j.hpp:136
constexpr int kappa_twojl(int twoj, int l)
returns kappa, given 2j and l
Definition Wigner369j.hpp:55
double tildeCk_kk(int k, int ka, int kb)
tildeCk_kk = (-1)^{ja+1/2}*Ck_kk
Definition Wigner369j.hpp:333
double S_kk(int ka, int kb)
Reduced spin angular ME: (for spin 1/2): <ka||S||kb>
Definition Wigner369j.hpp:371
constexpr int sumsToZero(int m1, int m2, int m3)
Checks if three ints sum to zero, returns 1 if they do.
Definition Wigner369j.hpp:167
int nkindex_to_twoj(int index)
Returns 2*j, given nk_index.
Definition Wigner369j.hpp:127
constexpr int l_k(int ka)
returns l given kappa
Definition Wigner369j.hpp:43
std::pair< int, int > index_to_nk(int index)
return {n, kappa} given nk_index:
Definition Wigner369j.hpp:109
constexpr int parity_l(int l)
returns parity [(-1)^l] given l
Definition Wigner369j.hpp:51
constexpr int twojFromIndex(int i)
returns 2j, given kappa_index
Definition Wigner369j.hpp:88
constexpr bool evenQ_2(int two_a)
Returns true if a (an int) is even, given 2*a (true if two_a/2 is even)
Definition Wigner369j.hpp:148
constexpr int kappa_twojpi(const int twoj, const int pi)
returns kappa, given 2*j and parity (+/-1),
Definition Wigner369j.hpp:59
double special_threej_2(int two_j1, int two_j2, int two_k)
Special (common) 3js case: (j1 j2 k, -0.5, 0.5, 0)
Definition Wigner369j.hpp:190
bool Ck_kk_SR(int k, int ka, int kb)
Ck selection rule only. Returns false if C^k=0, true if non-zero.
Definition Wigner369j.hpp:324
constexpr int l_tilde_k(int ka)
"Complimentary" l := (2j-l) = l+/-1, for j=l+/-1/2
Definition Wigner369j.hpp:53
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition Wigner369j.hpp:152
constexpr int parity(int la, int lb, int k)
Parity rule. Returns 1 only if la+lb+k is even.
Definition Wigner369j.hpp:156
constexpr int parity_k(int ka)
returns parity [(-1)^l] given kappa
Definition Wigner369j.hpp:47
bool sixj_zeroQ(int a, int b, int c, int d, int e, int f)
Checks triangle conditions for 6j symbols (for 2*j)
Definition Wigner369j.hpp:221
constexpr int twoj(int jindex)
Converts jindex to 2*j [helper function].
Definition CkTable.hpp:12
constexpr int triangle(int j1, int j2, int J)
Returns 1 if triangle rule is satisfied. nb: works with j OR twoj!
Definition Wigner369j.hpp:162
double ninej_2(int two_j1, int two_j2, int two_j3, int two_j4, int two_j5, int two_j6, int two_j7, int two_j8, int two_j9)
9j symbol {j1 j2 j3 \ j4 j5 j6 \ j7 j8 j9} [takes 2*j as int]
Definition Wigner369j.hpp:282
constexpr int indexFromKappa(int ka)
returns kappa_index, given kappa
Definition Wigner369j.hpp:80
bool sixjTriads(std::optional< int > a, std::optional< int > b, std::optional< int > c, std::optional< int > d, std::optional< int > e, std::optional< int > f)
Checks if a 6j symbol is valid - each input is optional.
Definition Wigner369j.hpp:248
constexpr int kappaFromIndex(int i)
Returns kappa, given kappa_index.
Definition Wigner369j.hpp:84
constexpr int twoj_k(int ka)
returns 2j given kappa
Definition Wigner369j.hpp:45
constexpr bool evenQ(int a)
Returns true if a is even - for integer values.
Definition Wigner369j.hpp:146
double Ck_kk(int k, int ka, int kb)
Reduced (relativistic) angular ME: <ka||C^k||kb> [takes k and kappa].
Definition Wigner369j.hpp:298