Skip to content

compute families of wigner symbols with recurrence relations

License

Notifications You must be signed in to change notification settings

xzackli/WignerFamilies.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WignerFamilies

Dev Build Status Coverage

This package implements methods described in Luscombe and Luban 1998, based on the work of Schulten and Gordon 1961, for generating families of Wigner 3j and 6j symbols by recurrence relation. These exact methods are orders of magnitude more efficient than strategies like prime factorization for problems which require every non-trivial symbol in a family, and really shine for large quantum numbers. WignerFamilies.jl is thread-safe and very fast, beating the standard Fortran routine DRC3JJ from SLATEC by a factor of 2-4 (see notebook).

Installation

using Pkg
Pkg.add("WignerFamilies")

Usage

WignerFamilies.jl currently computes the nontrivial 3j symbols over j with the other quantum numbers fixed, in the family of symbols,

It exposes wigner3j_f(j₂, j₃, m₂, m₃) which returns a simple wrapper around a vector of the typeWignerSymbolVector. This vector contains the computed symbols, indexed by the quantum number j. The type supports half-integer quantum numbers as indices.

using WignerFamilies

# wigner3j for all j fixing j₂=100, j₃=60, m₂=70, m₃=-55, m₁=-m₂-m₃
w3j = wigner3j_f(100, 60, 70, -55) 
js = collect(eachindex(w3j))  # indices are the quantum numbers
plot(js, w3j.symbols)   # you can get the underlying array with w3j.symbols

This generates the symbols in Figure 1 of Luscombe and Luban 1998.

Advanced Use

One can compute symbols in a fully non-allocating way by using the mutating wigner3j_f!. This requires one to initialize a WignerF struct describing the problem, put a wrapper around the piece of memory you want to deposit the symbols using WignerSymbolVector, and then calling the mutating method.

j₂, j₃, m₂, m₃ = 100, 100, -2, 2
w = WignerF(Float64, j₂, j₃, m₂, m₃)
buffer = zeros(Float64, length(w.nₘᵢₙ:w.nₘₐₓ))
w3j = WignerSymbolVector(buffer, w.nₘᵢₙ:w.nₘₐₓ)
WignerFamilies.wigner3j_f!(w, w3j)

This is the best way to get symbols if you're using them in a tight loop, since allocations really hurt in those cases. Typically you would preallocate a buffer and then give this package a view into it.

using BenchmarkTools
@btime WignerFamilies.wigner3j_f!(w, w3j)
1.660 μs (0 allocations: 0 bytes)