Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <cstddef>
7 : #include <vector>
8 :
9 : /*!
10 : * \brief Computes Wigner 3J symbols.
11 : *
12 : * Given $l_2$, $m_2$, $l_3$, and $m_3$,
13 : * computes and caches the Wigner 3J symbols
14 : *
15 : * \begin{align}
16 : * \left(
17 : * \begin{array}{ccc}
18 : * l_1 & l_2 & l_3 \\
19 : * -m_3-m_2 & m_2 & m_3
20 : * \end{array}\right)
21 : * \end{align}
22 : *
23 : * for all $l_1$.
24 : *
25 : * Internally, uses recursion relations to efficiently compute many 3J symbols
26 : * at once. This should be more efficient than having a single function
27 : * that takes $l_2$, $m_2$, $l_3$, $m_3$, $l_1$
28 : * and computes a single 3J symbol, assuming
29 : * that the user creates WignerThreeJ objects infrequently, and that the user
30 : * calls each WignerThreeJ object multiple times.
31 : *
32 : * Here we limit the quantum numbers to integer values. It is trivial to
33 : * extend to half-integer values because the underlying routine (the
34 : * drc3jj.f function from the slatec library) supports it.
35 : *
36 : * Uses lazy evaluation: the constructor computes nothing, but the first time
37 : * operator() is called for a nonzero 3J symbol, it computes all the
38 : * nonzero 3J symbols.
39 : *
40 : * Internally, allocates a std::vector. A future optimization may be to
41 : * pass in a buffer of the appropriate size, so that WignerThreeJ would not
42 : * need to do memory allocations.
43 : */
44 1 : class WignerThreeJ {
45 : public:
46 0 : WignerThreeJ(size_t l2, int m2, size_t l3, int m3);
47 : /*!
48 : * Returns the Wigner 3J symbol
49 : * \begin{align}
50 : * \left(
51 : * \begin{array}{ccc}
52 : * l_1 & l_2 & l_3 \\
53 : * -m_3-m_2 & m_2 & m_3
54 : * \end{array}\right).
55 : * \end{align}
56 : *
57 : * Internally, computes and caches all nonzero 3J symbols for all $l_1$.
58 : * Returns zero for $l_1 < l_1^\mathrm{min}$ and
59 : * $l_1 > l_1^\mathrm{max}$.
60 : */
61 1 : double operator()(size_t l1);
62 : /// Returns the smallest value of $l_1$ that gives a nonzero 3J symbol.
63 1 : size_t l1_min() const { return l1_min_; }
64 : /// Returns the largest value of $l_1$ that gives a nonzero 3J symbol.
65 1 : size_t l1_max() const { return l1_max_; }
66 :
67 : private:
68 0 : void recompute();
69 : // The underlying routine takes doubles (to deal with half-integer spins),
70 : // so we need to store doubles here.
71 0 : double l2_, m2_, l3_, m3_;
72 0 : size_t l1_min_, l1_max_;
73 0 : bool up_to_date_;
74 0 : std::vector<double> coefs_;
75 : };
|