diff --git a/.travis.yml b/.travis.yml index 264a58f..2945643 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,10 @@ os: - linux - osx julia: - - 1.0 - - 1.1 - 1.2 - 1.3 + - 1.4 + - 1.5 - nightly notifications: email: false @@ -44,7 +44,7 @@ after_success: jobs: include: - stage: "Documentation" - julia: 1.0 + julia: 1.4 os: linux script: - julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); diff --git a/Manifest.toml b/Manifest.toml index 20ba52e..01ab420 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,49 +1,42 @@ # This file is machine-generated - editing it directly is not advised +[[ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra"] +git-tree-sha1 = "89182776a99b69964e995cc2f1e37b5fc3476d56" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "0.3.4" + [[BandedMatrices]] -deps = ["FillArrays", "LazyArrays", "LinearAlgebra", "MatrixFactorizations", "Random", "SparseArrays"] -git-tree-sha1 = "5a9e887a119842c033622d5fab67968516e2746f" +deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra", "Random", "SparseArrays"] +git-tree-sha1 = "ab0e98974a74abf7b2b3a89946d28695133a8ae7" uuid = "aae01518-5342-5314-be14-df237901396f" -version = "0.12.3" +version = "0.15.11" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[BlockArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "87b72cb71d7306b767d175ece832bf8a7f3b6dc8" +deps = ["ArrayLayouts", "Compat", "LinearAlgebra"] +git-tree-sha1 = "5e157c81f1321f2338e1e2b71389c7783d3f59e6" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.10.0" +version = "0.12.8" [[BlockBandedMatrices]] -deps = ["BandedMatrices", "BlockArrays", "Distributed", "FillArrays", "LazyArrays", "LinearAlgebra", "MatrixFactorizations", "Pkg", "Profile", "Random", "SharedArrays", "SparseArrays", "Statistics", "Test"] -git-tree-sha1 = "160fc541f01304ab823d1aff1959e84f28a63faf" +deps = ["ArrayLayouts", "BandedMatrices", "BlockArrays", "Distributed", "FillArrays", "LinearAlgebra", "MatrixFactorizations", "Pkg", "Random", "SharedArrays", "SparseArrays", "Statistics", "Test"] +git-tree-sha1 = "1caed9949c3077a8af9fa45739c094fefd8b33b6" uuid = "ffab5731-97b5-5995-9138-79e8c1846df0" -version = "0.5.1" - -[[CSTParser]] -deps = ["Tokenize"] -git-tree-sha1 = "c69698c3d4a7255bc1b4bc2afc09f59db910243b" -uuid = "00ebfdb7-1f24-5e51-bd34-a7502290713f" -version = "0.6.2" +version = "0.8.5" [[Combinatorics]] -deps = ["LinearAlgebra", "Polynomials", "Test"] -git-tree-sha1 = "50b3ae4d643dc27eaff69fb6be06ee094d5500c9" +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "0.7.0" +version = "1.0.2" [[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "ed2c4abadf84c53d9e58510b5fc48912c2336fbb" +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "054993b6611376ddb40203e973e954fd9d1d1902" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "2.2.0" - -[[DataStructures]] -deps = ["InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "f94423c68f2e47db0d6f626a26d4872266e0ec3d" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.17.2" +version = "3.12.0" [[Dates]] deps = ["Printf"] @@ -59,9 +52,9 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[FillArrays]] deps = ["LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "16974065d5bfa867446d3228bc63f05a440e910b" +git-tree-sha1 = "44f561e293987ffc84272cd3d2b14b0b93123d63" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.7.2" +version = "0.8.10" [[Formatting]] deps = ["Printf"] @@ -70,21 +63,16 @@ uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" version = "0.4.1" [[HalfIntegers]] -git-tree-sha1 = "46026f5138d0458abbeaa60ddd700580f009e291" +git-tree-sha1 = "4c2671229adff0a42b3999f4255904959472de8f" uuid = "f0d1745a-41c9-11e9-1dd9-e5d34d218721" -version = "0.1.3" +version = "1.1.1" [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[LazyArrays]] -deps = ["FillArrays", "LinearAlgebra", "MacroTools", "StaticArrays"] -git-tree-sha1 = "576081d32ef488a03a8cebcc63fdb23d52b466e5" -uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "0.12.2" - [[LibGit2]] +deps = ["Printf"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" [[Libdl]] @@ -97,47 +85,34 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -[[MacroTools]] -deps = ["CSTParser", "Compat", "DataStructures", "Test", "Tokenize"] -git-tree-sha1 = "d6e9dedb8c92c3465575442da456aec15a89ff76" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.1" - [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[MatrixFactorizations]] -deps = ["LinearAlgebra", "Random", "Test"] -git-tree-sha1 = "42f7931fac4068709a8412d3766e9a2bfc91e451" +deps = ["ArrayLayouts", "LinearAlgebra", "Random"] +git-tree-sha1 = "69454f168628adeff74264837fc4c9d258dcc444" uuid = "a3b82374-2e81-5b9e-98ce-41277c0e4c87" -version = "0.2.0" +version = "0.4.1" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[OrderedCollections]] -deps = ["Random", "Serialization", "Test"] -git-tree-sha1 = "c4c13474d23c60d20a67b217f1d7f22a40edf8f1" +git-tree-sha1 = "12ce190210d278e12644bcadf5b21cbdcf225cd3" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.1.0" +version = "1.2.0" [[Parameters]] -deps = ["OrderedCollections"] -git-tree-sha1 = "b62b2558efb1eef1fa44e4be5ff58a515c287e38" +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "38b2e970043613c187bd56a995fe2e551821eb4a" uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.0" +version = "0.12.1" [[Pkg]] -deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -[[Polynomials]] -deps = ["LinearAlgebra", "RecipesBase"] -git-tree-sha1 = "f7c0c07e82798aef542d60a6e6e85e39f4590750" -uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -version = "0.5.3" - [[Primes]] deps = ["Test"] git-tree-sha1 = "ff1a2323cb468ec5f201838fcbe3c232266b1f95" @@ -148,10 +123,6 @@ version = "0.4.0" deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -[[Profile]] -deps = ["Printf"] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" - [[REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -160,10 +131,10 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[RecipesBase]] -git-tree-sha1 = "7bdce29bc9b2f5660a6e5e64d64d91ec941f6aa2" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "0.7.0" +[[RationalRoots]] +git-tree-sha1 = "4165f49c451e6fc4bc0a2eeb1250abdf8c6e550e" +uuid = "308eb6b3-cc68-5ff3-9e97-c3c4da4fa681" +version = "0.1.0" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -182,12 +153,6 @@ uuid = "6462fe0b-24de-5631-8697-dd941f90decc" deps = ["LinearAlgebra", "Random"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -[[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "db23bbf50064c582b6f2b9b043c8e7e98ea8c0c6" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "0.11.0" - [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" @@ -196,26 +161,26 @@ uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[Tokenize]] -git-tree-sha1 = "dfcdbbfb2d0370716c815cbd6f8a364efb6f42cf" -uuid = "0796e94c-ce3b-5d07-9a54-7f471281c624" -version = "0.5.6" - [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +[[UnPack]] +git-tree-sha1 = "d4bfa022cd30df012700cf380af2141961bb3bfb" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.1" + [[Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[UnicodeFun]] -deps = ["Test"] -git-tree-sha1 = "63cbbd00217fc9aafedf055b60459c1ae7e01ecc" +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.0" +version = "0.4.1" [[WignerSymbols]] -deps = ["HalfIntegers", "Primes"] -git-tree-sha1 = "d89c807c3affcf48e31ca36378238f98429278a6" +deps = ["HalfIntegers", "Primes", "RationalRoots"] +git-tree-sha1 = "dfb49a51c4c8e205d1d7ddc7e6a1a29da871ef52" uuid = "9f57e263-0b3d-5e2e-b1be-24f2bb48858b" -version = "1.0.0" +version = "1.1.0" diff --git a/Project.toml b/Project.toml index a2e36a7..f89c0f5 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,9 @@ Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" UnicodeFun = "1cfade01-22cf-5700-b092-accc4b62d6e1" WignerSymbols = "9f57e263-0b3d-5e2e-b1be-24f2bb48858b" +[compat] +julia = "1.2" + [extras] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/appveyor.yml b/appveyor.yml index a9cab82..8c208e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,9 @@ environment: matrix: - - julia_version: 1.0 - - julia_version: 1.1 - julia_version: 1.2 - julia_version: 1.3 + - julia_version: 1.4 + - julia_version: 1.5 - julia_version: nightly platform: diff --git a/src/AtomicLevels.jl b/src/AtomicLevels.jl index 9fcb608..38a5d80 100644 --- a/src/AtomicLevels.jl +++ b/src/AtomicLevels.jl @@ -20,6 +20,7 @@ include("excited_configurations.jl") include("terms.jl") include("allchoices.jl") include("jj_terms.jl") +include("intermediate_terms.jl") include("couple_terms.jl") include("csfs.jl") include("jj2lsj.jl") diff --git a/src/configurations.jl b/src/configurations.jl index edc7058..36150b7 100644 --- a/src/configurations.jl +++ b/src/configurations.jl @@ -111,7 +111,7 @@ Base.copy(cfg::Configuration) = Configuration(copy(cfg.orbitals), copy(cfg.occupancy), copy(cfg.states), sorted=cfg.sorted) -const RelativisticConfiguration{N} = Configuration{RelativisticOrbital{N}} +const RelativisticConfiguration = Configuration{<:RelativisticOrbital} """ issorted(cfg::Configuration) diff --git a/src/couple_terms.jl b/src/couple_terms.jl index d9c427e..142d6dc 100644 --- a/src/couple_terms.jl +++ b/src/couple_terms.jl @@ -86,9 +86,6 @@ couple_terms(J1::T, J2::T) where {T <: Union{Integer,HalfInteger}} = couple_terms(J1::Real, J2::Real) = couple_terms(convert(HalfInteger, J1), convert(HalfInteger, J2)) -term_type(::Type{IntermediateTerm}) = Term -term_type(::Type{T}) where {T<:Real} = T - function intermediate_couplings(its::Vector{T}, t₀::T=zero(T)) where {T<:Union{Term,Integer,HalfInteger}} ts = Vector{Vector{T}}() for t in couple_terms(t₀, its[1]) @@ -118,7 +115,7 @@ julia> intermediate_couplings([IntermediateTerm(T"2S", 1), IntermediateTerm(T"2D [¹S, ²S, ³D] ``` """ -intermediate_couplings(its::Vector{IntermediateTerm}, t₀::Term=zero(Term)) = +intermediate_couplings(its::Vector{<:IntermediateTerm{T}}, t₀::T=zero(T)) where T = intermediate_couplings(map(t -> t.term, its), t₀) """ diff --git a/src/csfs.jl b/src/csfs.jl index bb341ff..a21be59 100644 --- a/src/csfs.jl +++ b/src/csfs.jl @@ -1,33 +1,32 @@ -struct CSF{O<:AbstractOrbital, IT<:Union{IntermediateTerm,HalfInteger}, T<:Union{Term,HalfInteger}} +struct CSF{O<:AbstractOrbital, T<:Union{Term,HalfInteger}, S} config::Configuration{<:O} - subshell_terms::Vector{IT} + subshell_terms::Vector{IntermediateTerm{T,S}} terms::Vector{T} - function CSF(config::Configuration{<:Orbital}, - subshell_terms::Vector{<:IntermediateTerm}, - terms::Vector{<:Term}) - length(subshell_terms) == length(peel(config)) || - throw(ArgumentError("Need to provide $(length(peel(config))) subshell terms for $(config)")) - length(terms) == length(peel(config)) || - throw(ArgumentError("Need to provide $(length(peel(config))) terms for $(config)")) - new{Orbital,IntermediateTerm,Term}(config, subshell_terms, terms) - end - function CSF(config::Configuration{O}, - subshell_terms::Vector{R}, - terms::Vector{R}) where {O <: RelativisticOrbital, R <: Real} + subshell_terms::Vector{IntermediateTerm{T,S}}, + terms::Vector{T}) where {O<:Union{<:Orbital,<:RelativisticOrbital}, + T<:Union{Term,HalfInt}, S} length(subshell_terms) == length(peel(config)) || throw(ArgumentError("Need to provide $(length(peel(config))) subshell terms for $(config)")) length(terms) == length(peel(config)) || throw(ArgumentError("Need to provide $(length(peel(config))) terms for $(config)")) - new{O,HalfInt,HalfInt}(config, - convert.(HalfInt, subshell_terms), - convert.(HalfInt, terms)) + + for (i,(orb,occ,_)) in enumerate(peel(config)) + st = subshell_terms[i] + assert_unique_classification(orb, occ, st) || + throw(ArgumentError("$(st) is not a unique classification of $(orb)$(to_superscript(occ))")) + end + + new{O,T,S}(config, subshell_terms, terms) end + + CSF(config, subshell_terms::Vector{<:IntermediateTerm}, terms::Vector{<:Real}) = + CSF(config, subshell_terms, convert.(HalfInt, terms)) end -const NonRelativisticCSF = CSF{<:Orbital,IntermediateTerm,Term} -const RelativisticCSF = CSF{<:RelativisticOrbital,HalfInt,HalfInt} +const NonRelativisticCSF = CSF{<:Orbital,Term} +const RelativisticCSF = CSF{<:RelativisticOrbital,HalfInt} Base.:(==)(a::CSF{O,T}, b::CSF{O,T}) where {O,T} = (a.config == b.config) && (a.subshell_terms == b.subshell_terms) && (a.terms == b.terms) @@ -92,7 +91,8 @@ function Base.show(io::IO, ::MIME"text/plain", csf::RelativisticCSF) println(io) nc > 0 && printfmt(io, cfmt, "") - for j in csf.subshell_terms + for it in csf.subshell_terms + j = it.term if denominator(j) == 1 printfmt(io, ifmt, numerator(j)) else diff --git a/src/intermediate_terms.jl b/src/intermediate_terms.jl new file mode 100644 index 0000000..85ae1f2 --- /dev/null +++ b/src/intermediate_terms.jl @@ -0,0 +1,183 @@ +# * Seniority + +""" + Seniority(ν) + +Seniority is an extra quantum number introduced by Giulio Racah (1943) +to disambiguate between terms belonging to a subshell with a specific +occupancy, that are assigned the same term symbols. For partially +filled f-shells (in ``LS`` coupling) or partially filled ``9/2`` +shells (in ``jj`` coupling), seniority alone is not enough to +disambiguate all the arising terms. + +The seniority number is defined as the minimum occupancy number `ν ∈ +n:-2:0` for which the term first appears, e.g. the ²D term first +occurs in the d¹ configuration, then twice in the d³ configuration +(which will then have the terms ₁²D and ₃²D). +""" +struct Seniority + ν::Int +end + +Base.isless(a::Seniority, b::Seniority) = a.ν < b.ν +Base.iseven(s::Seniority) = iseven(s.ν) +Base.isodd(s::Seniority) = isodd(s.ν) + +# This is the notation by Giulio Racah, p.377: +# - Racah, G. (1943). Theory of complex spectra. III. Physical Review, +# 63(9-10), 367–382. http://dx.doi.org/10.1103/physrev.63.367 +Base.show(io::IO, s::Seniority) = write(io, to_subscript(s.ν)) + +istermvalid(term, s::Seniority) = + iseven(multiplicity(term)) ⊻ iseven(s) + +# This is due to the statement "For partially filled f-shells, +# seniority alone is not sufficient to distinguish all possible +# states." on page 24 of +# +# - Froese Fischer, C., Brage, T., & Jönsson, P. (1997). Computational +# Atomic Structure : An Mchf Approach. Bristol, UK Philadelphia, Penn: +# Institute of Physics Publ. +# +# This is too strict, i.e. there are partially filled (ℓ ≥ f)-shells +# for which seniority /is/ enough, but I don't know which, so better +# play it safe. +assert_unique_classification(orb::Orbital, occupation, term::Term, s::Seniority) = + istermvalid(term, s) && + (orb.ℓ < 3 || occupation == degeneracy(orb)) + +function assert_unique_classification(orb::RelativisticOrbital, occupation, J::HalfInteger, s::Seniority) + ν = s.ν + # This is deduced by looking at Table A.10 of + # + # - Grant, I. P. (2007). Relativistic Quantum Theory of Atoms and + # Molecules : Theory and Computation. New York: Springer. + istermvalid(J, s) && + !((orb.j == half(9) && (occupation == 4 || occupation == 6) && + (J == 4 || J == 6) && ν == 4) || + orb.j > half(9)) # Again, too strict. +end + + +# * Intermediate terms, seniority +""" + IntermediateTerm(term, ν) + +Represents a `term` together with its extra disambiguating quantum +numbers, labelled by `ν`, which should be sortable (i.e. comparable by +`isless`). The most common implementation of this is a single quantum +number, [`Seniority`](@ref). +""" +struct IntermediateTerm{T,S} + term::T + ν::S + IntermediateTerm(term::T, ν::S) where {T<:Union{Term,<:HalfInteger}, S} = + new{T,S}(term, ν) + IntermediateTerm(term::Real, ν) = + IntermediateTerm(convert(HalfInt, term), ν) +end + +function Base.show(io::IO, iterm::IntermediateTerm{<:Any,<:Integer}) + write(io, "₍") + write(io, to_subscript(iterm.ν)) + write(io, "₎") + show(io, iterm.term) +end + +function Base.show(io::IO, iterm::IntermediateTerm) + show(io, iterm.ν) + show(io, iterm.term) +end + +Base.isless(a::IntermediateTerm, b::IntermediateTerm) = + a.ν < b.ν || + a.ν == b.ν && a.term < b.term + +""" + intermediate_terms(orb::Orbital, w::Int=one(Int)) + +Generates all [`IntermediateTerm`](@ref) for a given non-relativstic +orbital `orb` and occupation `w`. + +# Examples + +```jldoctest +julia> intermediate_terms(o"2p", 2) +3-element Array{IntermediateTerm,1}: + ₀¹S + ₂¹D + ₂³P +``` + +The preceding subscript is the seniority number, which indicates at +which occupancy a certain term is first seen, cf. + +```jldoctest +julia> intermediate_terms(o"3d", 1) +1-element Array{IntermediateTerm,1}: + ₁²D + +julia> intermediate_terms(o"3d", 3) +8-element Array{IntermediateTerm,1}: + ₁²D + ₃²P + ₃²D + ₃²F + ₃²G + ₃²H + ₃⁴P + ₃⁴F +``` + +In the second case, we see both `₁²D` and `₃²D`, since there are two +ways of coupling 3 `d` electrons to a `²D` symmetry. +""" +function intermediate_terms(orb::Union{<:Orbital,<:RelativisticOrbital}, w::Int=one(Int)) + g = degeneracy(orb) + w > g÷2 && (w = g - w) + ts = terms(orb, w) + its = map(unique(ts)) do t + its = IntermediateTerm{typeof(t),Seniority}[] + previously_seen = 0 + + # We have to loop in reverse, since odd occupation numbers + # should go from 1 and even from 0. + for ν ∈ reverse(w:-2:0) + nn = count_terms(orb, ν, t) - previously_seen + previously_seen += nn + append!(its, repeat([IntermediateTerm(t, Seniority(ν))], nn)) + end + its + end + sort(vcat(its...)) +end + +""" + intermediate_terms(config) + +Generate the intermediate terms for each subshell of `config`. + +# Examples + +```jldoctest +julia> intermediate_terms(c"1s 2p3") +2-element Array{Array{IntermediateTerm,1},1}: + [₁²S] + [₁²Pᵒ, ₃²Dᵒ, ₃⁴Sᵒ] + +julia> intermediate_terms(rc"3d2 5g3") +2-element Array{Array{HalfIntegers.Half{Int64},1},1}: + [0, 2, 4] + [3/2, 5/2, 7/2, 9/2, 9/2, 11/2, 13/2, 15/2, 17/2, 21/2] +``` +""" +function intermediate_terms(config::Configuration) + map(config) do (orb,occupation,state) + intermediate_terms(orb,occupation) + end +end + +assert_unique_classification(orb, occupation, it::IntermediateTerm) = + assert_unique_classification(orb, occupation, it.term, it.ν) + +export IntermediateTerm, intermediate_terms, Seniority diff --git a/src/jj2lsj.jl b/src/jj2lsj.jl index 33f6b7f..a0eaed5 100644 --- a/src/jj2lsj.jl +++ b/src/jj2lsj.jl @@ -66,7 +66,7 @@ function rotate!(blocks::Vector{M}, orbs::RelativisticOrbital...) where {T,M<:Ab o = 2*(bi-1) for (b,(j,mⱼ)) in enumerate(jmⱼ) b-o ∉ 1:2 && continue - blocks[bi][a-o,b-o] = ClebschGordanℓs(ℓ,m,half(1),s,j,mⱼ) + blocks[bi][a-o,b-o] = convert(T, ClebschGordanℓs(ℓ,m,half(1),s,j,mⱼ)) end end blocks @@ -112,8 +112,8 @@ function jj2lsj(::Type{T}, orbs::RelativisticOrbital...) where T jₘₐₓ = maximum([kappa_to_j(o.κ) for o in subspace]) pure = [Matrix{T}(undef,1,1),Matrix{T}(undef,1,1)] - pure[1][1]=ClebschGordanℓs(ℓ,-ℓ,half(1),-half(1),jₘₐₓ,-jₘₐₓ) - pure[2][1]=ClebschGordanℓs(ℓ,ℓ,half(1),half(1),jₘₐₓ,jₘₐₓ) + pure[1][1] = convert(T, ClebschGordanℓs(ℓ,-ℓ,half(1),-half(1),jₘₐₓ,-jₘₐₓ)) + pure[2][1] = convert(T, ClebschGordanℓs(ℓ,ℓ,half(1),half(1),jₘₐₓ,jₘₐₓ)) n = sum(length.(mⱼ))-2 if n > 0 @@ -127,7 +127,7 @@ function jj2lsj(::Type{T}, orbs::RelativisticOrbital...) where T end |> b -> vcat(b...) rows = size.(blocks,1) N = sum(rows) - R = BlockBandedMatrix(Zeros{T}(N,N), (rows,rows), (0,0)) + R = BlockBandedMatrix(Zeros{T}(N,N), rows,rows, (0,0)) for (i,b) in enumerate(blocks) R[Block(i,i)] .= b end diff --git a/src/jj_terms.jl b/src/jj_terms.jl index 72fe193..5c3a9f6 100644 --- a/src/jj_terms.jl +++ b/src/jj_terms.jl @@ -45,18 +45,7 @@ function terms(orb::RelativisticOrbital, w::Int=one(Int)) reverse!(_terms_jw(j, w)) end -function _terms_jw(j::HalfInteger, w::Integer) - j >= 0 || throw(DomainError(j, "j must be positive")) - 1 <= w <= 2*j+1 || throw(DomainError(w, "w must be 1 <= w <= 2j+1 (=$(2j+1)) for j=$j")) - # This works by considering all possible n-particle fermionic product states (Slater - # determinants; i.e. each orbital can only appear once) of the orbitals with different - # m quantum numbers -- they are just different n-element combinations of the possible - # m quantum numbers (m ∈ -j:j). - # - # Each of the product states is still a J_z eigenstate and the eigenvalue is just a sum - # of the J_z eigenvalues of the orbitals. As for every coupled J, we also get M ∈ -J:J, - # we can just look at the histogram of all the M quantum numbers to figure out which - # J states and how many of them we have. +function _terms_jw_histogram(j, w) Jmax = j*w NJ = convert(Int, 2*Jmax + 1) hist = zeros(Int, NJ) @@ -65,6 +54,25 @@ function _terms_jw(j::HalfInteger, w::Integer) i = convert(Int, M + Jmax) + 1 hist[i] += 1 end + hist +end + +function _terms_jw(j::HalfInteger, w::Integer) + j >= 0 || throw(DomainError(j, "j must be positive")) + 1 <= w <= 2*j+1 || throw(DomainError(w, "w must be 1 <= w <= 2j+1 (=$(2j+1)) for j=$j")) + # This works by considering all possible n-particle fermionic product states (Slater + # determinants; i.e. each orbital can only appear once) of the orbitals with different + # m quantum numbers -- they are just different n-element combinations of the possible + # m quantum numbers (m ∈ -j:j). + Jmax = j*w + hist = _terms_jw_histogram(j, w) + NJ = length(hist) + # Each of the product states is still a J_z eigenstate and the + # eigenvalue is just a sum of the J_z eigenvalues of the + # orbitals. As for every coupled J, we also get M ∈ -J:J, we can + # just look at the histogram of all the M quantum numbers to + # figure out which J states and how many of them we have. + # Go through the histogram to figure out the J terms. jvalues = HalfInt[] Jmid = div(NJ, 2) + (isodd(NJ) ? 1 : 0) @@ -80,9 +88,31 @@ function _terms_jw(j::HalfInteger, w::Integer) return jvalues end -# This is a workaround until seniority number are implemented for -# jj-coupled subshells. -intermediate_terms(orb::RelativisticOrbital, w::Int=one(Int)) = - terms(orb, w) +function count_terms(orb::RelativisticOrbital, w::Integer, J::HalfInteger) + j = kappa_to_j(orb.κ) + 0 <= w <= 2*j+1 || throw(DomainError(w, "w must be 0 <= w <= 2j+1 (=$(2j+1)) for j=$j")) + # We can equivalently calculate the JJ terms for holes, so we'll do that when we have + # fewer holes than particles on this shell + 2w ≥ 2j+1 && (w = convert(Int, 2j) + 1 - w) + # Zero and one particle cases are simple special cases + if w == 0 + iszero(J) ? 1 : 0 + elseif w == 1 + J == j ? 1 : 0 + else + Jmax = j*w + J > Jmax && return 0 + hist = _terms_jw_histogram(j, w) + NJ = length(hist) + i = convert(Int, Jmax - J + 1) + hist[i] - ((i > 1) ? hist[i-1] : 0) + end +end + +count_terms(orb::RelativisticOrbital, w, J::Real) = + count_terms(orb, w, convert(HalfInt, J)) + +multiplicity(J::HalfInteger) = convert(Int, 2J + 1) +weight(J::HalfInteger) = multiplicity(J) export terms, intermediate_terms diff --git a/src/terms.jl b/src/terms.jl index ae01443..87ba49f 100644 --- a/src/terms.jl +++ b/src/terms.jl @@ -242,119 +242,4 @@ function Base.show(io::IO, term::Term) write(io, to_superscript(term.parity)) end -# * Intermediate terms, seniority -""" - IntermediateTerm(term, seniority) - -Represents a term together with its seniority quantum number. -""" -struct IntermediateTerm - term::Term - seniority::Int - function IntermediateTerm(term::Term, seniority::Int) - iseven(multiplicity(term)) ⊻ iseven(seniority) || - throw(ArgumentError("Invalid seniority $(seniority) for term $(term)")) - new(term, seniority) - end -end - -function Base.show(io::IO, iterm::IntermediateTerm) - # This is the notation by Giulio Racah, p.377: - # - Racah, G. (1943). Theory of complex spectra. iii. Physical Review, - # 63(9-10), 367–382. http://dx.doi.org/10.1103/physrev.63.367 - write(io, to_subscript(iterm.seniority)) - show(io, iterm.term) -end - -Base.isless(a::IntermediateTerm, b::IntermediateTerm) = - a.seniority < b.seniority || - a.seniority == b.seniority && a.term < b.term - -""" - intermediate_terms(orb::Orbital, w::Int=one(Int)) - -Generates all [`IntermediateTerm`](@ref) for a given non-relativstic -orbital `orb` and occupation `w`. - -# Examples - -```jldoctest -julia> intermediate_terms(o"2p", 2) -3-element Array{IntermediateTerm,1}: - ₀¹S - ₂¹D - ₂³P -``` - -The preceding subscript is the seniority number, which indicates at -which occupancy a certain term is first seen, cf. - -```jldoctest -julia> intermediate_terms(o"3d", 1) -1-element Array{IntermediateTerm,1}: - ₁²D - -julia> intermediate_terms(o"3d", 3) -8-element Array{IntermediateTerm,1}: - ₁²D - ₃²P - ₃²D - ₃²F - ₃²G - ₃²H - ₃⁴P - ₃⁴F -``` - -In the second case, we see both `₁²D` and `₃²D`, since there are two -ways of coupling 3 `d` electrons to a `²D` symmetry. -""" -function intermediate_terms(orb::Orbital, w::Int=one(Int)) - ts = terms(orb, w) - its = map(unique(ts)) do t - its = IntermediateTerm[] - previously_seen = 0 - # The seniority number is defined as the minimum occupancy - # number ν ∈ n:-2:0 for which the term first appears, e.g. the - # ²D term first occurs in the d¹ configuration, then twice in - # the d³ configuration (which will then have the terms ₁²D and - # ₃²D). - # - # We have to loop in reverse, since odd occupation numbers - # should go from 1 and even from 0. - for ν ∈ reverse(w:-2:0) - nn = count_terms(orb, ν, t) - previously_seen - previously_seen += nn - append!(its, repeat([IntermediateTerm(t, ν)], nn)) - end - its - end - sort(vcat(its...)) -end - -""" - intermediate_terms(config) - -Generate the intermediate terms for each subshell of `config`. - -# Examples - -```jldoctest -julia> intermediate_terms(c"1s 2p3") -2-element Array{Array{IntermediateTerm,1},1}: - [₁²S] - [₁²Pᵒ, ₃²Dᵒ, ₃⁴Sᵒ] - -julia> intermediate_terms(rc"3d2 5g3") -2-element Array{Array{HalfIntegers.Half{Int64},1},1}: - [0, 2, 4] - [3/2, 5/2, 7/2, 9/2, 9/2, 11/2, 13/2, 15/2, 17/2, 21/2] -``` -""" -function intermediate_terms(config::Configuration) - map(config) do (orb,occ,state) - intermediate_terms(orb,occ) - end -end - -export Term, @T_str, multiplicity, weight, terms, count_terms, IntermediateTerm, intermediate_terms +export Term, @T_str, multiplicity, weight, terms, count_terms diff --git a/test/atsp/csfparser.jl b/test/atsp/csfparser.jl index 52f489a..ce32213 100644 --- a/test/atsp/csfparser.jl +++ b/test/atsp/csfparser.jl @@ -21,7 +21,7 @@ function parse_csf(filename) np = length(peel_cfg) its = map(enumerate(ts[1:np])) do (i,t) ip = p_suffix(parity(Configuration(peel_cfg[i]...))) - IntermediateTerm(parse(Term, "$(t[1:2])$(ip)"), parse(Int, t[end])) + IntermediateTerm(parse(Term, "$(t[1:2])$(ip)"), Seniority(parse(Int, t[end]))) end coupled_terms = map(enumerate(ts[vcat(1,np+1:end)])) do (i,t) parse(Term, "$(t[1:2])$(p_suffix(peel_cfg[1:i]))") diff --git a/test/csfs.jl b/test/csfs.jl index db83f69..7753146 100644 --- a/test/csfs.jl +++ b/test/csfs.jl @@ -24,27 +24,27 @@ using .ATSPParser @testset "CSFs" begin @testset "Construction" begin - csf = CSF(rc"1s2 2p- 2p", [0, 1//2, 3//2], [0, 1//2, 2]) - @test csf isa CSF{RelativisticOrbital{Int},HalfInt,HalfInt} + csf = CSF(rc"1s2 2p- 2p", [IntermediateTerm(0,Seniority(0)), IntermediateTerm(1//2,Seniority(1)), IntermediateTerm(3//2,Seniority(1))], [0, 1//2, 2]) + @test csf isa CSF{RelativisticOrbital{Int},HalfInt} @test csf isa RelativisticCSF @test csf == csf - @test csf != CSF(rc"1s2 2p- 2p", [0, 1//2, 3//2], [0, 1//2, 1]) + @test csf != CSF(rc"1s2 2p- 2p", [IntermediateTerm(0,Seniority(0)), IntermediateTerm(1//2,Seniority(1)), IntermediateTerm(3//2,Seniority(1))], [0, 1//2, 1]) @test num_electrons(csf) == 4 - @test CSF(c"1s2 2p", [IntermediateTerm(T"1S",0), IntermediateTerm(T"2Po", 1)], [T"1S", T"2Po"]) isa NonRelativisticCSF + @test CSF(c"1s2 2p", [IntermediateTerm(T"1S",Seniority(0)), IntermediateTerm(T"2Po", Seniority(1))], [T"1S", T"2Po"]) isa NonRelativisticCSF end @testset "CSF list generation" begin @testset "ls coupling" begin - csf_1s2 = CSF(c"1s2", [IntermediateTerm(T"1S",0)],[T"1S"]) + csf_1s2 = CSF(c"1s2", [IntermediateTerm(T"1S",Seniority(0))],[T"1S"]) @test csfs(c"1s2") == [csf_1s2] @test string(csf_1s2) == "1s²(₀¹S|¹S)+" csfs_1s_kp = [CSF(c"1s kp", - [IntermediateTerm(T"2S",1),IntermediateTerm(T"2Po",1)], + [IntermediateTerm(T"2S",Seniority(1)),IntermediateTerm(T"2Po",Seniority(1))], [T"2S", T"1Po"]), CSF(c"1s kp", - [IntermediateTerm(T"2S",1),IntermediateTerm(T"2Po",1)], + [IntermediateTerm(T"2S",Seniority(1)),IntermediateTerm(T"2Po",Seniority(1))], [T"2S", T"3Po"])] @test csfs(c"1s kp") == csfs_1s_kp @@ -67,8 +67,10 @@ using .ATSPParser csfs(rc"[Kr] 5s2 5p-2 5p3 6s") csfs(rc"1s kp") - @test string(CSF(rc"[Kr] 5s2 5p- 5p4 kd", [0//1, 1//2, 0//1, 5//2], [0//1, 1//2, 1//2, 3//1])) == - "[Kr]ᶜ 5s²(0|0) 5p-(1/2|1/2) 5p⁴(0|1/2) kd(5/2|3)-" + @test string(CSF(rc"[Kr] 5s2 5p- 5p4 kd", + [IntermediateTerm(0//1, Seniority(0)), IntermediateTerm(1//2, Seniority(1)), IntermediateTerm(0//1, Seniority(0)), IntermediateTerm(5//2, Seniority(1))], + [0//1, 1//2, 1//2, 3//1])) == + "[Kr]ᶜ 5s²(₀0|0) 5p-(₁1/2|1/2) 5p⁴(₀0|1/2) kd(₁5/2|3)-" end end end diff --git a/test/excited_configurations.jl b/test/excited_configurations.jl index 2bd107c..3d92bab 100644 --- a/test/excited_configurations.jl +++ b/test/excited_configurations.jl @@ -1,12 +1,12 @@ using Test using AtomicLevels +using HalfIntegers: HalfInt, half # ^^^ -- to make it possible to run the test file separately module GRASPParser using Test using AtomicLevels import AtomicLevels: CSF, csfs -using WignerSymbols include("grasp/rcsfparser.jl") compare_with_grasp(f, rcsfout) = @testset "GRASP CSFs: $(rcsfout)" begin diff --git a/test/grasp/rcsfparser.jl b/test/grasp/rcsfparser.jl index a346760..32a65ae 100644 --- a/test/grasp/rcsfparser.jl +++ b/test/grasp/rcsfparser.jl @@ -20,7 +20,7 @@ function parse_rcsf(filename) core_occupations = map(degeneracy, core_orbitals) blockid, csfid = 1, 1 - csfs = CSF{RelativisticOrbital{Int},HalfInt}[] + csfs = CSF{RelativisticOrbital{Int},HalfInt,Seniority}[] while ! eof(io) line1 = readline(io) if startswith(line1, " *") @@ -85,8 +85,16 @@ function parse_rcsf(filename) end config = Configuration(vcat(core_orbitals, orbitals), vcat(core_occupations, noccupations), sorted=true) - subshell_terms = map(x -> convert(Rational{Int}, x), - vcat(core_couplings, Vector{HalfInt}(orbcouplings))) + # I have no idea how to generate correct seniority numbers, even in the case where + # it does not matter (no degeneracy). So instead, I'll try to match the couplings + # to the intermediate terms generated by AtomicLevels. + subshell_terms = let its = intermediate_terms.(vcat(core_orbitals, orbitals), vcat(core_occupations, noccupations)) + map(its, vcat(core_couplings, Vector{HalfInt}(orbcouplings))) do its, grasp_term + idxs = findall(it -> it.term == grasp_term, its) + @assert length(idxs) == 1 # make sure there is only one intermediate term with the coupling we're interested in + its[first(idxs)] + end + end terms = map(x -> convert(Rational{Int}, x), vcat(core_couplings, Vector{HalfInt}(csfcouplings))) csf = CSF(config, subshell_terms, terms) diff --git a/test/intermediate_terms.jl b/test/intermediate_terms.jl new file mode 100644 index 0000000..53272c7 --- /dev/null +++ b/test/intermediate_terms.jl @@ -0,0 +1,87 @@ +@testset "Intermediate terms" begin + @testset "LS coupling" begin + @test !AtomicLevels.istermvalid(T"2S", Seniority(2)) + + @test string(IntermediateTerm(T"2D", Seniority(3))) == "₃²D" + @test string(IntermediateTerm(T"2D", 3)) == "₍₃₎²D" + + @testset "Seniority" begin + # Table taken from p. 25 of + # + # - Froese Fischer, C., Brage, T., & Jönsson, P. (1997). Computational + # Atomic Structure : An MCHF Approach. Bristol, UK Philadelphia, Penn: + # Institute of Physics Publ. + subshell_terms = [ + o"1s" => (1 => "2S1", + 2 => "1S0"), + o"2p" => (1 => "2P1", + 2 => ("1S0", "1D2", "3P2"), + 3 => ("2P1", "2D3", "4S3")), + o"3d" => (1 => "2D1", + 2 => ("1S0", "1D2", "1G2", "3P2", "3F2"), + 3 => ("2D1", "2P3", "2D3", "2F3", "2G3", "2H3", "4P3", "4F3"), + 4 => ("1S0", "1D2", "1G2", "3P2", "3F2", "1S4", "1D4", "1F4", "1G4", "1I4", "3P4", "3D4", "3F4", "3G4", "3H4", "5D4"), + 5 => ("2D1", "2P3", "2D3", "2F3", "2G3", "2H3", "4P3", "4F3", "2S5", "2D5", "2F5", "2G5", "2I5", "4D5", "4G5", "6S5")), + o"4f" => (1 => "2F1", + 2 => ("1S0", "1D2", "1G2", "1I2", "3P2", "3F2", "3H2")) + ] + + foreach(subshell_terms) do (orb, data) + foreach(data) do (occ, expected_its) + p = parity(orb)^occ + expected_its = map(expected_its isa String ? (expected_its,) : expected_its) do ei + t_str = "$(ei[1:2])$(isodd(p) ? "o" : "")" + IntermediateTerm(parse(Term, t_str), Seniority(parse(Int, ei[end]))) + end + its = intermediate_terms(orb, occ) + @test its == [expected_its...] + end + end + end + end + + @testset "jj coupling" begin + # Taken from Table A.10 of + # + # - Grant, I. P. (2007). Relativistic Quantum Theory of Atoms and + # Molecules : Theory and Computation. New York: Springer. + # + # except for the last row, which seems to have a typo since J=19/2 is + # missing. Cf. Table 4-5 of + # + # - Cowan, R. (1981). The Theory of Atomic Structure and + # Spectra. Berkeley: University of California Press. + ref_table = [[rc"1s2", rc"2p-2"] => [0 => [0]], + [rc"1s", rc"2p-"] => [1 => [1/2]], + [rc"2p4", rc"3d-4"] => [0 => [0]], + [rc"2p", rc"2p3", rc"3d-", rc"3d-3"] => [1 => [3/2]], + [rc"2p2", rc"3d-2"] => [0 => [0], 2 => [2]], + [rc"3d6", rc"4f-6"] => [0 => [0]], + [rc"3d", rc"3d5", rc"4f-", rc"4f-5"] => [1 => [5/2]], + [rc"3d2", rc"3d4", rc"4f-2", rc"4f-4"] => [0 => [0], 2 => [2,4]], + [rc"3d3", rc"4f-3"] => [1 => [5/2], 3 => [3/2, 9/2]], + [rc"4f8", rc"5g-8"] => [0 => [0]], + [rc"4f", rc"4f7", rc"5g-", rc"5g-7"] => [1 => [7/2]], + [rc"4f2", rc"4f6", rc"5g-2", rc"5g-6"] => [0 => [0], 2 => [2,4,6]], + [rc"4f3", rc"4f5", rc"5g-3", rc"5g-5"] => [1 => [7/2], 3 => [3/2, 5/2, 9/2, 11/2, 15/2]], + [rc"4f4", rc"5g-4"] => [0 => [0], 2 => [2,4,6], 4 => [2,4,5,8]], + [rc"5g10", rc"6h-10"] => [0 => [0]], + [rc"5g", rc"5g9", rc"6h-", rc"6h-9"] => [1 => [9/2]], + [rc"5g2", rc"5g8", rc"6h-2", rc"6h-8"] => [0 => [0], 2 => [2,4,6,8]], + [rc"5g3", rc"5g7", rc"6h-3", rc"6h-7"] => [1 => [9/2], + 3 => vcat((3:2:17), 21)./2], + [rc"5g4", rc"5g6", rc"6h-4", rc"6h-6"] => [0 => 0, 2 => [2,4,6,8], + 4 => [0,2,3,4,4,5,6,6,7,8,9,10,12]], + [rc"5g5", rc"6h-5"] => [1 => [9/2], 3 => vcat((3:2:17), 21)./2, + 5 => vcat(1, (5:2:19), 25)./2]] + + for (cfgs,cases) in ref_table + ref = [reduce(vcat, [[IntermediateTerm(convert(HalfInt, J), Seniority(ν)) + for J in Js] + for (ν,Js) in cases])] + for cfg in cfgs + @test intermediate_terms(cfg) == ref + end + end + end +end diff --git a/test/jj_terms.jl b/test/jj_terms.jl index 452c965..374ad80 100644 --- a/test/jj_terms.jl +++ b/test/jj_terms.jl @@ -58,23 +58,22 @@ # Table 4.5, Cowan 1981 foreach([ (ro"1s",ro"2p-") => [(0,2) => 0, 1 => 1//2], - (ro"2p",ro"3d-") => [(0,4) => 0, (1,3) => 3//2, 2 => (0,2)], - (ro"3d",ro"4f-") => [(0,6) => 0, (1,5) => 5//2, (2,4) => (0,2,4), - 3 => (3//2,5//2,9//2)], - (ro"4f",ro"5g-") => [(0,8) => 0, (1,7) => 7//2, (2,6) => (0,2,4,6), - (3,5) => (3//2,5//2,7//2,9//2,11/2,15//2), - 4 => (0,2,2,4,4,5,6,8)], - (ro"5g",ro"6h-") => [(0,10) => 0, (1,9) => 9//2, (2,8) => (0,2,4,6,8), - (3,7) => (3//2,5//2,7//2,9//2,9//2,11//2,13//2,15//2,17//2,21//2), - (4,6) => (0,0,2,2,3,4,4,4,5,6,6,6,7,8,8,9,10,12), - 5 => (1//2,3//2,5//2,5//2,7//2,7//2,9//2,9//2,9//2,11//2,11//2,13//2,13//2,15//2,15//2,17//2,17//2,19//2,21//2,25//2)] + (ro"2p",ro"3d-") => [(0,4) => 0, (1,3) => 3//2, 2 => [0,2]], + (ro"3d",ro"4f-") => [(0,6) => 0, (1,5) => 5//2, (2,4) => [0,2,4], + 3 => [3//2,5//2,9//2]], + (ro"4f",ro"5g-") => [(0,8) => 0, (1,7) => 7//2, (2,6) => [0,2,4,6], + (3,5) => [3//2,5//2,7//2,9//2,11/2,15//2], + 4 => [0,2,2,4,4,5,6,8]], + (ro"5g",ro"6h-") => [(0,10) => 0, (1,9) => 9//2, (2,8) => [0,2,4,6,8], + (3,7) => [3//2,5//2,7//2,9//2,9//2,11//2,13//2,15//2,17//2,21//2], + (4,6) => [0,0,2,2,3,4,4,4,5,6,6,6,7,8,8,9,10,12], + 5 => [1//2,3//2,5//2,5//2,7//2,7//2,9//2,9//2,9//2,11//2,11//2,13//2,13//2,15//2,15//2,17//2,17//2,19//2,21//2,25//2]] ]) do (orbs,wsj) foreach(orbs) do orb foreach(wsj) do (ws,j) js = map(HalfInteger, isa(j, Number) ? [j] : j) foreach(ws) do w - # Should actually test without unique - @test unique(intermediate_terms(orb,w)) == unique(js) + @test terms(orb,w) == js end end end diff --git a/test/runtests.jl b/test/runtests.jl index bf02714..d14f22d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,7 @@ include("configurations.jl") include("excited_configurations.jl") include("terms.jl") include("jj_terms.jl") +include("intermediate_terms.jl") include("couple_terms.jl") include("csfs.jl") include("levels.jl") diff --git a/test/terms.jl b/test/terms.jl index 568b091..af8533e 100644 --- a/test/terms.jl +++ b/test/terms.jl @@ -239,44 +239,4 @@ using Test @test count_terms(o"3d", 3, T"2D") == 2 @test count_terms(o"3d", 5, T"2D") == 3 end - - @testset "Intermediate terms" begin - @test_throws ArgumentError IntermediateTerm(T"2S", 2) - - @test string(IntermediateTerm(T"2D", 3)) == "₃²D" - - @testset "Seniority" begin - # Table taken from p. 25 of - # - # - Froese Fischer, C., Brage, T., & Jönsson, P. (1997). Computational - # Atomic Structure : An MCHF Approach. Bristol, UK Philadelphia, Penn: - # Institute of Physics Publ. - subshell_terms = [ - o"1s" => (1 => "2S1", - 2 => "1S0"), - o"2p" => (1 => "2P1", - 2 => ("1S0", "1D2", "3P2"), - 3 => ("2P1", "2D3", "4S3")), - o"3d" => (1 => "2D1", - 2 => ("1S0", "1D2", "1G2", "3P2", "3F2"), - 3 => ("2D1", "2P3", "2D3", "2F3", "2G3", "2H3", "4P3", "4F3"), - 4 => ("1S0", "1D2", "1G2", "3P2", "3F2", "1S4", "1D4", "1F4", "1G4", "1I4", "3P4", "3D4", "3F4", "3G4", "3H4", "5D4"), - 5 => ("2D1", "2P3", "2D3", "2F3", "2G3", "2H3", "4P3", "4F3", "2S5", "2D5", "2F5", "2G5", "2I5", "4D5", "4G5", "6S5")), - o"4f" => (1 => "2F1", - 2 => ("1S0", "1D2", "1G2", "1I2", "3P2", "3F2", "3H2")) - ] - - foreach(subshell_terms) do (orb, data) - foreach(data) do (occ, expected_its) - p = parity(orb)^occ - expected_its = map(expected_its isa String ? (expected_its,) : expected_its) do ei - t_str = "$(ei[1:2])$(isodd(p) ? "o" : "")" - IntermediateTerm(parse(Term, t_str), parse(Int, ei[end])) - end - its = intermediate_terms(orb, occ) - @test its == [expected_its...] - end - end - end - end end