AtomicLevels.jl

AtomicLevels provides a collections of types and methods to facilitate working with atomic states (or, more generally, states with spherical symmetry), both in the relativistic (eigenstates of $J = L + S$) and non-relativistic (eigenstates on $L$ and $S$ separately) frameworks.

The aim is to make sure that the types used to label and store information about atomic states are both efficient and user-friendly at the same time. In addition, it also provides various utility methods, such as generation of a list CSFs corresponding to a given configuration, serialization of orbitals and configurations, methods for introspecting physical quantities etc.

Usage examples

Orbitals

A single orbital can be constructed using string macros

julia> orbitals = o"2s", ro"5f-"
(2s, 5f-)

Various methods are provided to look up the properties of the orbitals

julia> for o in orbitals
           @info "Orbital: $o :: $(typeof(o))" parity(o) degeneracy(o) angular_momenta(o)
       end
┌ Info: Orbital: 2s :: Orbital{Int64}
│   parity(o) = even
│   degeneracy(o) = 2
└   angular_momenta(o) = (0, 1/2)
┌ Info: Orbital: 5f- :: RelativisticOrbital{Int64}
│   parity(o) = odd
│   degeneracy(o) = 6
└   angular_momenta(o) = (5/2,)

You can also create a range of orbitals quickly using the @os_str (or @ros_str) string macros

julia> os"5[d] 6[s-p] k[7-10]"
7-element Vector{Orbital}:
 5d
 6s
 6p
 kk
 kl
 km
 kn

Configurations

The ground state of hydrogen and helium.

julia> c"1s",(c"1s2",c"[He]")
(1s, (1s², [He]ᶜ))

The ground state configuration of xenon, in relativistic notation.

julia> Xe = rc"[Kr] 5s2 5p6"
[Kr]ᶜ 5s² 5p⁴ 5p-²

As we see above, by default, the krypton core is declared “closed”. This is useful for calculations when the core should be frozen. We can “open” it by affixing *.

julia> Xe = c"[Kr]* 5s2 5p6"
1s² 2s² 2p⁶ 3s² 3p⁶ 3d¹⁰ 4s² 4p⁶ 5s² 5p⁶

Note that the 5p shell was broken up into 2 5p- electrons and 4 5p electrons. If we are not filling the shell, occupancy of the spin-up and spin-down electrons has to be given separately.

julia> Xe⁺ = rc"[Kr] 5s2 5p-2 5p3"
[Kr]ᶜ 5s² 5p-² 5p³

It is also possible to work with “continuum orbitals”, where the main quantum number is replaced by a Symbol.

julia> Xe⁺e = rc"[Kr] 5s2 5p-2 5p3 ks"
[Kr]ᶜ 5s² 5p-² 5p³ ks

Excitations

We can easily generate all possible excitations from a reference configuration. If no extra orbitals are specified, only those that are “open” within the reference set will be considered.

julia> excited_configurations(rc"[Kr] 5s2 5p-2 5p3")
2-element Vector{Configuration{RelativisticOrbital{Int64}}}:
 [Kr]ᶜ 5s² 5p-² 5p³
 [Kr]ᶜ 5s² 5p- 5p⁴

By appending virtual orbitals, we can generate excitations to configurations beyond those spanned by the reference set.

julia> excited_configurations(rc"[Kr] 5s2 5p-2 5p3", ros"5[d] 6[s-p]"...)
64-element Vector{Configuration{RelativisticOrbital{Int64}}}:
 [Kr]ᶜ 5s² 5p-² 5p³
 [Kr]ᶜ 5s 5p-² 5p³ 5d-
 [Kr]ᶜ 5s 5p-² 5p³ 5d
 [Kr]ᶜ 5s 5p-² 5p³ 6s
 [Kr]ᶜ 5s² 5p- 5p⁴
 [Kr]ᶜ 5s² 5p- 5p³ 6p-
 [Kr]ᶜ 5s² 5p- 5p³ 6p
 [Kr]ᶜ 5s² 5p-² 5p² 6p-
 [Kr]ᶜ 5s² 5p-² 5p² 6p
 [Kr]ᶜ 5p-² 5p⁴ 6p-
 ⋮
 [Kr]ᶜ 5s² 5p-² 5p 5d-²
 [Kr]ᶜ 5s² 5p-² 5p 5d- 5d
 [Kr]ᶜ 5s² 5p-² 5p 5d- 6s
 [Kr]ᶜ 5s² 5p-² 5p 5d²
 [Kr]ᶜ 5s² 5p-² 5p 5d 6s
 [Kr]ᶜ 5s² 5p-² 5p 6s²
 [Kr]ᶜ 5s² 5p-² 5p 6p-²
 [Kr]ᶜ 5s² 5p-² 5p 6p- 6p
 [Kr]ᶜ 5s² 5p-² 5p 6p²

Again, using the “continuum orbitals”, it is possible to generate the state space accessible via one-photon transitions from the ground state.

julia> Xe⁺e = excited_configurations(rc"[Kr] 5s2 5p6", ros"k[s-d]"...,
                                     max_excitations=:singles,
                                     keep_parity=false)
16-element Vector{Configuration{RelativisticOrbital}}:
 [Kr]ᶜ 5s² 5p⁴ 5p-²
 [Kr]ᶜ 5s 5p⁴ 5p-² ks
 [Kr]ᶜ 5s 5p⁴ 5p-² kp-
 [Kr]ᶜ 5s 5p⁴ 5p-² kp
 [Kr]ᶜ 5s 5p⁴ 5p-² kd-
 [Kr]ᶜ 5s 5p⁴ 5p-² kd
 [Kr]ᶜ 5s² 5p³ 5p-² ks
 [Kr]ᶜ 5s² 5p³ 5p-² kp-
 [Kr]ᶜ 5s² 5p³ 5p-² kp
 [Kr]ᶜ 5s² 5p³ 5p-² kd-
 [Kr]ᶜ 5s² 5p³ 5p-² kd
 [Kr]ᶜ 5s² 5p⁴ 5p- ks
 [Kr]ᶜ 5s² 5p⁴ 5p- kp-
 [Kr]ᶜ 5s² 5p⁴ 5p- kp
 [Kr]ᶜ 5s² 5p⁴ 5p- kd-
 [Kr]ᶜ 5s² 5p⁴ 5p- kd

We can then query for the bound and continuum orbitals thus.

julia> map(Xe⁺e) do c
           b = bound(c)
           num_electrons(b) => b
       end
16-element Vector{Pair{Int64, Configuration{RelativisticOrbital}}}:
 44 => [Kr]ᶜ 5s² 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s 5p⁴ 5p-²
 43 => [Kr]ᶜ 5s² 5p³ 5p-²
 43 => [Kr]ᶜ 5s² 5p³ 5p-²
 43 => [Kr]ᶜ 5s² 5p³ 5p-²
 43 => [Kr]ᶜ 5s² 5p³ 5p-²
 43 => [Kr]ᶜ 5s² 5p³ 5p-²
 43 => [Kr]ᶜ 5s² 5p⁴ 5p-
 43 => [Kr]ᶜ 5s² 5p⁴ 5p-
 43 => [Kr]ᶜ 5s² 5p⁴ 5p-
 43 => [Kr]ᶜ 5s² 5p⁴ 5p-
 43 => [Kr]ᶜ 5s² 5p⁴ 5p-

julia> map(Xe⁺e) do c
           b = continuum(c)
           num_electrons(b) => b
       end
16-element Vector{Pair{Int64, Configuration{RelativisticOrbital}}}:
 0 => ∅
 1 => ks
 1 => kp-
 1 => kp
 1 => kd-
 1 => kd
 1 => ks
 1 => kp-
 1 => kp
 1 => kd-
 1 => kd
 1 => ks
 1 => kp-
 1 => kp
 1 => kd-
 1 => kd

Term symbol calculation

Overview of angular momentum coupling on Wikipedia.

$LS$-coupling. This is done purely non-relativistic, i.e. 2p- is considered equivalent to 2p.

julia> terms(c"1s")
1-element Vector{Term}:
 ²S

julia> terms(c"[Kr] 5s2 5p5")
1-element Vector{Term}:
 ²Pᵒ

julia> terms(c"[Kr] 5s2 5p4 6s 7g")
13-element Vector{Term}:
 ¹D
 ¹F
 ¹G
 ¹H
 ¹I
 ³D
 ³F
 ³G
 ³H
 ³I
 ⁵F
 ⁵G
 ⁵H

$jj$-coupling. $jj$-coupling is implemented slightly differently, it calculates the possible $J$ values resulting from coupling n equivalent electrons in all combinations allowed by the Pauli principle.

julia> intermediate_terms(ro"1s", 1)
1-element Vector{IntermediateTerm{HalfIntegers.Half{Int64}, Seniority}}:
 ₁1/2

julia> intermediate_terms(ro"5p", 2)
2-element Vector{IntermediateTerm{HalfIntegers.Half{Int64}, Seniority}}:
 ₀0
 ₂2

julia> intermediate_terms(ro"7g", 3)
10-element Vector{IntermediateTerm{HalfIntegers.Half{Int64}, Seniority}}:
 ₁9/2
 ₃3/2
 ₃5/2
 ₃7/2
 ₃9/2
 ₃11/2
 ₃13/2
 ₃15/2
 ₃17/2
 ₃21/2

Configuration state functions

CSFs are formed from electronic configurations and their possible term couplings (along with intermediate terms, resulting from unfilled subshells).

julia> sort(vcat(csfs(rc"3s 3p2")..., csfs(rc"3s 3p- 3p")...))
7-element Vector{RelativisticCSF{RelativisticOrbital{Int64}, Seniority}}:
 3s(₁1/2|1/2) 3p²(₀0|1/2)+
 3s(₁1/2|1/2) 3p-(₁1/2|1) 3p(₁3/2|1/2)+
 3s(₁1/2|1/2) 3p²(₂2|3/2)+
 3s(₁1/2|1/2) 3p-(₁1/2|0) 3p(₁3/2|3/2)+
 3s(₁1/2|1/2) 3p-(₁1/2|1) 3p(₁3/2|3/2)+
 3s(₁1/2|1/2) 3p²(₂2|5/2)+
 3s(₁1/2|1/2) 3p-(₁1/2|1) 3p(₁3/2|5/2)+