From ec51827250fad628571008493772dec3b257c168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:43:49 +0100 Subject: [PATCH 01/15] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b5b778b..a5cb15d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "AtomicLevels" uuid = "10933b4c-d60f-11e8-1fc6-bd9035a249a1" authors = ["Stefanos Carlström "] -version = "0.1.8" +version = "0.1.9" [deps] BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" From b08b87a3bbf7208346190e87c77def2abda72166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:43:58 +0100 Subject: [PATCH 02/15] Updated CI config --- .github/workflows/CI.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d56b2fe..49f852f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,9 +11,8 @@ jobs: matrix: version: - '1.2' - - '1.6' - - '~1.7.0-0' - - 'nightly' + - '1.7' + - '1' os: - ubuntu-latest - macOS-latest From 4fcd41f05395593ed60cd70b828215d0f5f273a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:44:47 +0100 Subject: [PATCH 03/15] Comparison helper --- src/common.jl | 14 ++++++++++++++ src/intermediate_terms.jl | 7 ++++--- src/orbitals.jl | 5 ++--- src/relativistic_orbitals.jl | 18 +++++++++++++----- src/terms.jl | 15 +++++++-------- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/common.jl b/src/common.jl index 136a7df..9896936 100644 --- a/src/common.jl +++ b/src/common.jl @@ -3,3 +3,17 @@ function spectroscopic_label(ℓ) ℓ >= 0 || throw(DomainError(ℓ, "Negative ℓ values not allowed")) ℓ + 1 ≤ length(spectroscopic) ? spectroscopic[ℓ+1] : "[$(ℓ)]" end + +""" + @< a b [lt] + +Helper macro that returns `true` if `a < b`, `false` if `a > b`, and +does nothing if `a == b`. The intended use-case is to simplify the +implementation of `isless` methods. Optionally accepts a custom +implementation of `lt`. +""" +macro <(a, b, lt=:(<)) + islt = :($(esc(lt))($(esc(a)), $(esc(b))) && return true) + isgt = :($(esc(lt))($(esc(b)), $(esc(a))) && return false) + Expr(:block, islt, isgt, :(false)) +end diff --git a/src/intermediate_terms.jl b/src/intermediate_terms.jl index ee2105b..c3eddc1 100644 --- a/src/intermediate_terms.jl +++ b/src/intermediate_terms.jl @@ -125,9 +125,10 @@ function Base.show(io::IO, iterm::IntermediateTerm) show(io, iterm.term) end -Base.isless(a::IntermediateTerm, b::IntermediateTerm) = - a.ν < b.ν || - a.ν == b.ν && a.term < b.term +function Base.isless(a::IntermediateTerm, b::IntermediateTerm) + @< a.ν b.ν + @< a.term b.term +end """ intermediate_terms(orb::Orbital, w::Int=one(Int)) diff --git a/src/orbitals.jl b/src/orbitals.jl index ef26fe6..440c0b2 100644 --- a/src/orbitals.jl +++ b/src/orbitals.jl @@ -133,9 +133,8 @@ false ``` """ function Base.isless(a::Orbital, b::Orbital) - nisless(a.n, b.n) && return true - a.n == b.n && a.ℓ < b.ℓ && return true - false + @< a.n b.n nisless + @< a.ℓ b.ℓ end """ diff --git a/src/relativistic_orbitals.jl b/src/relativistic_orbitals.jl index e28ec6e..5b08aa0 100644 --- a/src/relativistic_orbitals.jl +++ b/src/relativistic_orbitals.jl @@ -158,11 +158,19 @@ end degeneracy(orb::RelativisticOrbital{N}) where N = 2*abs(orb.κ) # 2j + 1 = 2|κ| function Base.isless(a::RelativisticOrbital, b::RelativisticOrbital) - nisless(a.n, b.n) && return true - aℓ, bℓ = κ2ℓ(a.κ), κ2ℓ(b.κ) - a.n == b.n && aℓ < bℓ && return true - a.n == b.n && aℓ == bℓ && abs(a.κ) < abs(b.κ) && return true - false + @< a.n b.n nisless + @< κ2ℓ(a.κ) κ2ℓ(b.κ) + @< abs(a.κ) abs(b.κ) +end + +function Base.isless(a::RelativisticOrbital, b::Orbital) + @< a.n b.n nisless + @< κ2ℓ(a.κ) b.ℓ +end + +function Base.isless(a::Orbital, b::RelativisticOrbital) + @< a.n b.n nisless + @< a.ℓ κ2ℓ(b.κ) end parity(orb::RelativisticOrbital) = p"odd"^κ2ℓ(orb.κ) diff --git a/src/terms.jl b/src/terms.jl index 6b9b376..1ceb66a 100644 --- a/src/terms.jl +++ b/src/terms.jl @@ -161,14 +161,13 @@ julia> weight(T"³P") """ weight(t::Term) = (2t.L + 1) * multiplicity(t) -import Base.== -==(t1::Term, t2::Term) = ((t1.L == t2.L) && (t1.S == t2.S) && (t1.parity == t2.parity)) - -import Base.< -<(t1::Term, t2::Term) = ((t1.S < t2.S) || (t1.S == t2.S) && (t1.L < t2.L) - || (t1.S == t2.S) && (t1.L == t2.L) && (t1.parity < t2.parity)) -import Base.isless -isless(t1::Term, t2::Term) = (t1 < t2) +Base.:(==)(t1::Term, t2::Term) = ((t1.L == t2.L) && (t1.S == t2.S) && (t1.parity == t2.parity)) + +function Base.isless(t1::Term, t2::Term) + @< t1.S t2.S + @< t1.L t2.L + @< t1.parity t2.parity +end Base.hash(t::Term) = hash((t.L,t.S,t.parity)) From e806ca8be636b3fb4f70d1beb10f4f77bd7fb3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:45:32 +0100 Subject: [PATCH 04/15] Properties and accessors --- src/csfs.jl | 67 +++++++++++++++++++++++++++++++++++- src/levels.jl | 8 +++++ src/orbitals.jl | 5 ++- src/relativistic_orbitals.jl | 2 ++ src/spin_orbitals.jl | 1 + src/terms.jl | 18 ++++++++++ src/unicode.jl | 3 ++ 7 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/csfs.jl b/src/csfs.jl index af5e03a..0b13bbf 100644 --- a/src/csfs.jl +++ b/src/csfs.jl @@ -74,6 +74,9 @@ Base.isless(a::CSF, b::CSF) = last(a.terms) < last(b.terms) num_electrons(csf::CSF) = num_electrons(csf.config) +isrelativistic(::NonRelativisticCSF) = false +isrelativistic(::RelativisticCSF) = true + """ orbitals(csf::CSF{O}) -> Vector @@ -93,6 +96,68 @@ julia> orbitals(csf) """ orbitals(csf::CSF) = orbitals(csf.config) +""" + term(csf::CSF) + +Return the final term of `csf`. + +```jldoctest +julia> c = first(csfs(c"1s 2p")) +1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)- + +julia> term(c) +¹Pᵒ + +julia> c = first(csfs(rc"1s2 2p-2")) +1s² 2p-² + 0 0 + 0 0+ + +julia> term(c) +0 + +julia> c = first(csfs(rc"1s2 2p-")) +1s² 2p- + 0 1/2 + 0 1/2- + +julia> term(c) +1/2 +``` +""" +term(csf::CSF) = last(csf.terms) + +""" + parity(csf::CSF) -> Parity + +Return the parity of `csf`. + +```jldoctest +julia> c = first(csfs(c"1s 2p")) +1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)- + +julia> parity(c) +odd + +julia> c = first(csfs(rc"1s2 2p-2")) +1s² 2p-² + 0 0 + 0 0+ + +julia> parity(c) +even + +julia> c = first(csfs(rc"1s2 2p-")) +1s² 2p- + 0 1/2 + 0 1/2- + +julia> parity(c) +odd +``` +""" +parity(csf::CSF) = parity(csf.config) + """ csfs(::Configuration) -> Vector{CSF} csfs(::Vector{Configuration}) -> Vector{CSF} @@ -184,4 +249,4 @@ function Base.show(io::IO, ::MIME"text/plain", csf::RelativisticCSF) print(io, iseven(parity(csf.config)) ? "+" : "-") end -export CSF, NonRelativisticCSF, RelativisticCSF, csfs +export CSF, NonRelativisticCSF, RelativisticCSF, csfs, term diff --git a/src/levels.jl b/src/levels.jl index b11e52a..454ffd8 100644 --- a/src/levels.jl +++ b/src/levels.jl @@ -109,6 +109,10 @@ julia> levels.(csfs(rc"1s 2p-")) """ levels(csf::CSF) = sort([Level(csf,J) for J in J_range(last(csf.terms))]) +parity(l::Level) = parity(l.csf) +orbitals(l::Level) = orbitals(l.csf) +isrelativistic(l::Level) = isrelativistic(l.csf) + # * State @doc raw""" @@ -178,4 +182,8 @@ julia> states(c) """ states(csf::CSF) = states.(levels(csf)) +parity(s::State) = parity(s.level) +orbitals(s::State) = orbitals(s.level) +isrelativistic(s::State) = isrelativistic(s.level) + export Level, weight, J_range, levels, State, states diff --git a/src/orbitals.jl b/src/orbitals.jl index 440c0b2..cdd006a 100644 --- a/src/orbitals.jl +++ b/src/orbitals.jl @@ -180,6 +180,8 @@ function isbound end isbound(::Orbital{Int}) = true isbound(::Orbital{Symbol}) = false +isrelativistic(::Orbital) = false + """ angular_momenta(orbital) @@ -319,4 +321,5 @@ end export AbstractOrbital, Orbital, @o_str, @os_str, - degeneracy, symmetry, isbound, angular_momenta, angular_momentum_ranges + degeneracy, symmetry, isbound, isrelativistic, + angular_momenta, angular_momentum_ranges diff --git a/src/relativistic_orbitals.jl b/src/relativistic_orbitals.jl index 5b08aa0..a318b58 100644 --- a/src/relativistic_orbitals.jl +++ b/src/relativistic_orbitals.jl @@ -179,6 +179,8 @@ symmetry(orb::RelativisticOrbital) = orb.κ isbound(::RelativisticOrbital{Int}) = true isbound(::RelativisticOrbital{Symbol}) = false +isrelativistic(::RelativisticOrbital) = true + """ angular_momenta(orbital) diff --git a/src/spin_orbitals.jl b/src/spin_orbitals.jl index 56723c7..15d883b 100644 --- a/src/spin_orbitals.jl +++ b/src/spin_orbitals.jl @@ -98,6 +98,7 @@ parity(so::SpinOrbital) = parity(so.orb) symmetry(so::SpinOrbital) = (symmetry(so.orb), so.m...) isbound(so::SpinOrbital) = isbound(so.orb) +isrelativistic(so::SpinOrbital) = isrelativistic(so.orb) Base.promote_type(::Type{SO}, ::Type{SO}) where {SO<:SpinOrbital} = SO diff --git a/src/terms.jl b/src/terms.jl index 1ceb66a..087d1c9 100644 --- a/src/terms.jl +++ b/src/terms.jl @@ -119,6 +119,24 @@ end Base.zero(::Type{Term}) = T"1S" +""" + parity(t::Term) -> Parity + +Return the parity of `t`. + +```jldoctest +julia> parity(T"1S") +even + +julia> parity(T"1So") +odd + +julia> parity(T"2Do") +odd +``` +""" +parity(t::Term) = t.parity + """ multiplicity(t::Term) diff --git a/src/unicode.jl b/src/unicode.jl index d956f84..d3bc716 100644 --- a/src/unicode.jl +++ b/src/unicode.jl @@ -15,3 +15,6 @@ function from_superscript(s) '⁰' => '0') map(Base.Fix1(getindex, inverse_superscript_map), s) end + +UnicodeFun.to_subscript(j::Half) = + isinteger(j) ? to_subscript(j.twice÷2) : to_subscript(j.twice)*","*to_subscript(2) From e94e5d720b10561a2e9befee01a90616f66c86fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:46:34 +0100 Subject: [PATCH 05/15] Better term disambiguation by dummy quantum number in addition to seniority --- src/csfs.jl | 9 +-- src/intermediate_terms.jl | 119 +++++++++++++++++++++++++++++++++---- test/csfs.jl | 8 +++ test/grasp/rcsfparser.jl | 2 +- test/intermediate_terms.jl | 10 ++++ 5 files changed, 133 insertions(+), 15 deletions(-) diff --git a/src/csfs.jl b/src/csfs.jl index 0b13bbf..64d6098 100644 --- a/src/csfs.jl +++ b/src/csfs.jl @@ -167,15 +167,16 @@ configurations. """ function csfs end -function csfs(config::Configuration) - map(allchoices(intermediate_terms(peel(config)))) do subshell_terms +function csfs(config::Configuration, DQN::Type=SeniorityEnumeration) + map(allchoices(intermediate_terms(DQN, peel(config)))) do subshell_terms map(intermediate_couplings(subshell_terms)) do coupled_terms CSF(config, subshell_terms, coupled_terms[2:end]) end - end |> c -> vcat(c...) |> sort + end |> c -> reduce(vcat, c) |> sort end -csfs(configs::Vector{Configuration{O}}) where O = vcat(map(csfs, configs)...) +csfs(configs::Vector{Configuration{O}}, DQN::Type=SeniorityEnumeration) where O = + reduce(vcat, map(Base.Fix2(csfs, DQN), configs)) Base.length(csf::CSF) = length(peel(csf.config)) Base.getindex(csf::CSF, i::Integer) = diff --git a/src/intermediate_terms.jl b/src/intermediate_terms.jl index c3eddc1..119c574 100644 --- a/src/intermediate_terms.jl +++ b/src/intermediate_terms.jl @@ -44,7 +44,7 @@ istermvalid(term, s::Seniority) = # play it safe. assert_unique_classification(orb::Orbital, occupation, term::Term, s::Seniority) = istermvalid(term, s) && - (orb.ℓ < 3 || occupation == degeneracy(orb)) + (orb.ℓ < 3 || occupation == degeneracy(orb) || occupation ≤ 2) function assert_unique_classification(orb::RelativisticOrbital, occupation, J::HalfInteger, s::Seniority) ν = s.ν @@ -58,8 +58,70 @@ function assert_unique_classification(orb::RelativisticOrbital, occupation, J::H orb.j > half(9)) # Again, too strict. end +# ** Seniority + enumeration -# * Intermediate terms, seniority +""" + SeniorityEnumeration(ν, α) + +In addition to [`Seniority`](@ref), an extra quantum number `α` is +used to enumerate equivalent terms. +""" +struct SeniorityEnumeration + ν::Int + α::Int +end + +function Base.isless(a::SeniorityEnumeration, b::SeniorityEnumeration) + @< a.ν b.ν + @< a.α b.α +end +Base.iseven(s::SeniorityEnumeration) = iseven(s.ν) +Base.isodd(s::SeniorityEnumeration) = isodd(s.ν) + +Base.:(==)(a::Seniority, b::SeniorityEnumeration) = + a.ν == b.ν && b.α == 0 + +Base.:(==)(a::SeniorityEnumeration, b::Seniority) = + a.ν == b.ν && a.α == 0 + +function Base.show(io::IO, s::SeniorityEnumeration) + write(io, to_subscript(s.ν)) + iszero(s.α) || write(io, ";", to_subscript(s.α)) +end + +istermvalid(term, s::SeniorityEnumeration) = + iseven(multiplicity(term)) ⊻ iseven(s) + +assert_unique_classification(orb::Orbital, occupation, term::Term, s::SeniorityEnumeration) = + istermvalid(term, s) + +assert_unique_classification(orb::RelativisticOrbital, occupation, J::HalfInteger, s::SeniorityEnumeration) = + istermvalid(J, s) + +# * Term enumeration + +""" + TermEnumeration(ν) + +`TermEnumeration` is an extra quantum number that disambiguates +between terms belonging to a subshell with a specific occupancy, that +are assigned the same term symbols. In contrast with +[`Seniority`](@ref), `TermEnumeration` is not an eigenvalue of any +operator, and cannot be used to fix e.g. phases of matrix elements. On +the other hand, no limitation on the occupancy of a subshell exists. +""" +struct TermEnumeration + ν::Int +end + +Base.isless(a::TermEnumeration, b::TermEnumeration) = a.ν < b.ν + +Base.show(io::IO, s::TermEnumeration) = write(io, "₍", to_subscript(s.ν), "₎") + +assert_unique_classification(orb, occupation, term, s::TermEnumeration) = + s.ν ∈ 1:count_terms(orb, occupation, term) + +# * Intermediate terms """ struct IntermediateTerm{T,S} @@ -130,8 +192,24 @@ function Base.isless(a::IntermediateTerm, b::IntermediateTerm) @< a.term b.term end +Base.:(==)(a::IntermediateTerm, b::IntermediateTerm) = + a.ν == b.ν && a.term == b.term + +# ** Seniority[,Enumeration] + +_new_intermediate_terms(t,ν,nn,::Type{Seniority}) = repeat([IntermediateTerm(t, Seniority(ν))], nn) + +function _new_intermediate_terms(t,ν,nn,::Type{SeniorityEnumeration}) + if nn == 1 + [IntermediateTerm(t, SeniorityEnumeration(ν, 0))] + else + [IntermediateTerm(t, SeniorityEnumeration(ν, α)) + for α = 1:nn] + end +end + """ - intermediate_terms(orb::Orbital, w::Int=one(Int)) + intermediate_terms([DisambiguatingQN=SeniorityEnumeration], orb::Orbital, w::Int=one(Int)) Generates all [`IntermediateTerm`](@ref) for a given non-relativstic orbital `orb` and occupation `w`. @@ -169,12 +247,12 @@ julia> intermediate_terms(o"3d", 3) 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)) +function intermediate_terms(::Type{S}, orb::Union{<:Orbital,<:RelativisticOrbital}, w::Int=one(Int)) where {S<:Union{Seniority,SeniorityEnumeration}} g = degeneracy(orb) w > g÷2 && (w = g - w) ts = terms(orb, w) its = map(unique(ts)) do t - its = IntermediateTerm{typeof(t),Seniority}[] + its = IntermediateTerm{typeof(t),S}[] previously_seen = 0 # We have to loop in reverse, since odd occupation numbers @@ -182,15 +260,32 @@ function intermediate_terms(orb::Union{<:Orbital,<:RelativisticOrbital}, w::Int= for ν ∈ reverse(w:-2:0) nn = count_terms(orb, ν, t) - previously_seen previously_seen += nn - append!(its, repeat([IntermediateTerm(t, Seniority(ν))], nn)) + append!(its, _new_intermediate_terms(t, ν, nn, S)) end its end sort(vcat(its...)) end +# ** TermEnumeration + +function intermediate_terms(::Type{TermEnumeration}, orb::Union{<:Orbital,<:RelativisticOrbital}, w::Int=one(Int)) + g = degeneracy(orb) + w > g÷2 && (w = g - w) + ts = unique(terms(orb, w)) + + sort(reduce(vcat, [[IntermediateTerm(t, TermEnumeration(ν)) + for ν = 1:count_terms(orb, w, t)] + for t in ts])) +end + +# ** Interface + +intermediate_terms(orb::Union{<:Orbital,<:RelativisticOrbital}, args...) = + intermediate_terms(SeniorityEnumeration, orb, args...) + """ - intermediate_terms(config) + intermediate_terms([DisambiguatingQN=Seniority], config) Generate the intermediate terms for each subshell of `config`. @@ -208,13 +303,17 @@ julia> intermediate_terms(rc"3d2 5g3") [₁9/2, ₃3/2, ₃5/2, ₃7/2, ₃9/2, ₃11/2, ₃13/2, ₃15/2, ₃17/2, ₃21/2] ``` """ -function intermediate_terms(config::Configuration) +function intermediate_terms(DQN::Type, config::Configuration) map(config) do (orb,occupation,state) - intermediate_terms(orb,occupation) + intermediate_terms(DQN, orb,occupation) end end +intermediate_terms(config::Configuration) = + intermediate_terms(SeniorityEnumeration, config) + + assert_unique_classification(orb, occupation, it::IntermediateTerm) = assert_unique_classification(orb, occupation, it.term, it.ν) -export IntermediateTerm, intermediate_terms, Seniority +export IntermediateTerm, intermediate_terms, Seniority, SeniorityEnumeration, TermEnumeration diff --git a/test/csfs.jl b/test/csfs.jl index 4065ffa..8f3267b 100644 --- a/test/csfs.jl +++ b/test/csfs.jl @@ -73,5 +73,13 @@ using .ATSPParser [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 + + @testset "Term disambiguation" begin + @test_throws ArgumentError csfs(c"2p2 4f11") + @test allunique(csfs(c"2p2 4f11", TermEnumeration)) + + @test_throws ArgumentError csfs(rc"5g4") + @test allunique(csfs(rc"5g4", TermEnumeration)) + end end end diff --git a/test/grasp/rcsfparser.jl b/test/grasp/rcsfparser.jl index a07952c..fd4fc6d 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,Seniority}[] + csfs = CSF{RelativisticOrbital{Int},HalfInt,SeniorityEnumeration}[] while ! eof(io) line1 = readline(io) if startswith(line1, " *") diff --git a/test/intermediate_terms.jl b/test/intermediate_terms.jl index faeb1b4..402edb2 100644 --- a/test/intermediate_terms.jl +++ b/test/intermediate_terms.jl @@ -57,6 +57,16 @@ using Test end end + @testset "Term enumeration" begin + @test !allunique(intermediate_terms(c"2p2 4f11")[2]) + @test allunique(intermediate_terms(SeniorityEnumeration, c"2p2 4f11")[2]) + @test allunique(intermediate_terms(TermEnumeration, c"2p2 4f11")[2]) + + @test !allunique(intermediate_terms(rc"5g4")[1]) + @test allunique(intermediate_terms(SeniorityEnumeration, rc"5g4")[1]) + @test allunique(intermediate_terms(TermEnumeration, rc"5g4")[1]) + end + @testset "jj coupling" begin # Taken from Table A.10 of # From 7244cfff97a2b52a83d02cab6ca150700d7aa11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 14:56:36 +0100 Subject: [PATCH 06/15] Better hash implementation for Term --- src/terms.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terms.jl b/src/terms.jl index 087d1c9..71a2f0b 100644 --- a/src/terms.jl +++ b/src/terms.jl @@ -187,7 +187,7 @@ function Base.isless(t1::Term, t2::Term) @< t1.parity t2.parity end -Base.hash(t::Term) = hash((t.L,t.S,t.parity)) +Base.hash(t::Term, h::UInt) = hash((t.L,t.S,t.parity), h) include("xu2006.jl") From aa39aa8976e9a2f960274ff5ae66824b281f5203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 15:07:23 +0100 Subject: [PATCH 07/15] Fix tests --- test/intermediate_terms.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/intermediate_terms.jl b/test/intermediate_terms.jl index 402edb2..71ebfa9 100644 --- a/test/intermediate_terms.jl +++ b/test/intermediate_terms.jl @@ -58,11 +58,11 @@ using Test end @testset "Term enumeration" begin - @test !allunique(intermediate_terms(c"2p2 4f11")[2]) + @test !allunique(intermediate_terms(Seniority, c"2p2 4f11")[2]) @test allunique(intermediate_terms(SeniorityEnumeration, c"2p2 4f11")[2]) @test allunique(intermediate_terms(TermEnumeration, c"2p2 4f11")[2]) - @test !allunique(intermediate_terms(rc"5g4")[1]) + @test !allunique(intermediate_terms(Seniority, rc"5g4")[1]) @test allunique(intermediate_terms(SeniorityEnumeration, rc"5g4")[1]) @test allunique(intermediate_terms(TermEnumeration, rc"5g4")[1]) end @@ -103,7 +103,7 @@ using Test 5 => vcat(1, (5:2:19), 25)./2]] for (cfgs,cases) in ref_table - ref = [reduce(vcat, [[IntermediateTerm(convert(HalfInt, J), Seniority(ν)) + ref = [reduce(vcat, [[IntermediateTerm(convert(HalfInt, J), SeniorityEnumeration(ν)) for J in Js] for (ν,Js) in cases])] for cfg in cfgs From 6dcf6691728f34f9b50f443f25297b93caf7e22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 15:15:06 +0100 Subject: [PATCH 08/15] Fix test --- test/csfs.jl | 6 ++++-- test/intermediate_terms.jl | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/csfs.jl b/test/csfs.jl index 8f3267b..049d956 100644 --- a/test/csfs.jl +++ b/test/csfs.jl @@ -75,10 +75,12 @@ using .ATSPParser end @testset "Term disambiguation" begin - @test_throws ArgumentError csfs(c"2p2 4f11") + @test_throws ArgumentError csfs(c"2p2 4f11", Seniority) + @test allunique(csfs(c"2p2 4f11", SeniorityEnumeration)) @test allunique(csfs(c"2p2 4f11", TermEnumeration)) - @test_throws ArgumentError csfs(rc"5g4") + @test_throws ArgumentError csfs(rc"5g4", Seniority) + @test allunique(csfs(rc"5g4", SeniorityEnumeration)) @test allunique(csfs(rc"5g4", TermEnumeration)) end end diff --git a/test/intermediate_terms.jl b/test/intermediate_terms.jl index 71ebfa9..379c2e9 100644 --- a/test/intermediate_terms.jl +++ b/test/intermediate_terms.jl @@ -102,10 +102,15 @@ using Test [rc"5g5", rc"6h-5"] => [1 => [9/2], 3 => vcat((3:2:17), 21)./2, 5 => vcat(1, (5:2:19), 25)./2]] + # On-the-fly generation of extra disambiguating quantum number + # α, in case there is more than one J in Js for the same + # seniority ν. + getα(Js,J,i) = count(==(J), Js) == 1 ? 0 : i-findfirst(==(J), Js)+1 + for (cfgs,cases) in ref_table - ref = [reduce(vcat, [[IntermediateTerm(convert(HalfInt, J), SeniorityEnumeration(ν)) - for J in Js] - for (ν,Js) in cases])] + ref = [sort(reduce(vcat, [[IntermediateTerm(convert(HalfInt, J), SeniorityEnumeration(ν,getα(Js,J,i))) + for (i,J) in enumerate(Js)] + for (ν,Js) in cases]))] for cfg in cfgs @test intermediate_terms(cfg) == ref end From ed95fb6d67fbfe6fa835e425dae6f45f181dd9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 15:59:02 +0100 Subject: [PATCH 09/15] Added missing docstrings --- docs/src/csfs.md | 1 + docs/src/terms.md | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/src/csfs.md b/docs/src/csfs.md index e80e152..091515e 100644 --- a/docs/src/csfs.md +++ b/docs/src/csfs.md @@ -100,6 +100,7 @@ NonRelativisticCSF RelativisticCSF csfs orbitals(::CSF) +term(::CSF) ``` ## Index diff --git a/docs/src/terms.md b/docs/src/terms.md index e3806cf..1ecd533 100644 --- a/docs/src/terms.md +++ b/docs/src/terms.md @@ -77,7 +77,7 @@ intermediate_terms ### Disambiguating quantum numbers The [`IntermediateTerm`](@ref) type does not specify how to interpret the disambiguating -quantum number(s) ``ν``, or even what the type of it should be. In AtomicLevels, we use two +quantum number(s) ``ν``, or even what the type of it should be. In AtomicLevels, we use four different types, depending on the situation: * **A simple `Integer`.** In this case, the quantum number ``\nu`` must be in the range @@ -87,14 +87,24 @@ different types, depending on the situation: AtomicLevels does not prescribe any further interpretation for the quantum number. It can be used as a simple counter to distinguish the different terms, or the user can - define their own mapping from the set of integers to physical states. + define their own mapping from the set of integers to physical + states. See also [`TermEnumeration`](@ref) which can be used to wrap + this integer. * **`Seniority`.** In this case the number is interpreted to be _Racah's seniority number_. This gives the intermediate term a specific physical interpretation, but only works for certain subshells. See the [`Seniority`](@ref) type for more information. +* **`SeniorityEnumeration`.** To further disambiguate those terms + which are degenerate for a given seniority ``\nu``, an additional, + ad-hoc, quantum number ``\alpha`` is added to the classification, + which simply enumerates the multiplet. See the + [`SeniorityEnumeration`](@ref) type for more information. + ```@docs Seniority +SeniorityEnumeration +TermEnumeration ``` ## Term couplings From a4fb5fb438c23331f16746b107c4ae9c780cc07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 16:00:36 +0100 Subject: [PATCH 10/15] Deploy documentation previews --- docs/make.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index b65dee0..311a546 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -49,4 +49,7 @@ makedocs( doctest = false ) -deploydocs(repo = "github.com/JuliaAtoms/AtomicLevels.jl.git") +deploydocs(; + repo = "github.com/JuliaAtoms/AtomicLevels.jl.git", + push_preview = true, +) From 7e9aff6aff7c2726a27849bf3a12ecfd2e10c33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 16:06:46 +0100 Subject: [PATCH 11/15] Fixed docstrings --- docs/src/terms.md | 16 ++++++++-------- src/configurations.jl | 8 ++++---- src/intermediate_terms.jl | 10 +++++----- src/levels.jl | 10 +++++----- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/src/terms.md b/docs/src/terms.md index 1ecd533..e3b6825 100644 --- a/docs/src/terms.md +++ b/docs/src/terms.md @@ -140,7 +140,7 @@ individual subshells: ```jldoctest intermediate_term_examples julia> its = intermediate_terms(c"3p2 4s 5p2") -3-element Vector{Vector{IntermediateTerm{Term, Seniority}}}: +3-element Vector{Vector{IntermediateTerm{Term, SeniorityEnumeration}}}: [₀¹S, ₂¹D, ₂³P] [₁²S] [₀¹S, ₂¹D, ₂³P] @@ -232,7 +232,7 @@ each subshell of `3p² 4s 5p²` ```jldoctest intermediate_term_examples julia> last.(its) -3-element Vector{IntermediateTerm{Term, Seniority}}: +3-element Vector{IntermediateTerm{Term, SeniorityEnumeration}}: ₂³P ₁²S ₂³P @@ -278,12 +278,12 @@ schemes has the following [`CSF`](@ref)s: ```jldoctest levels_and_states julia> csls = csfs(c"1s 2p") -2-element Vector{NonRelativisticCSF{Orbital{Int64}, Seniority}}: +2-element Vector{NonRelativisticCSF{Orbital{Int64}, SeniorityEnumeration}}: 1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)- 1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)- julia> csjj = vcat(csfs(rc"1s 2p"), csfs(rc"1s 2p-")) -4-element Vector{RelativisticCSF{RelativisticOrbital{Int64}, Seniority}}: +4-element Vector{RelativisticCSF{RelativisticOrbital{Int64}, SeniorityEnumeration}}: 1s(₁1/2|1/2) 2p(₁3/2|1)- 1s(₁1/2|1/2) 2p(₁3/2|2)- 1s(₁1/2|1/2) 2p-(₁1/2|0)- @@ -295,12 +295,12 @@ values of ``J``, i.e. ``0``, ``2\times 1``, and ``2``: ```jldoctest levels_and_states julia> levels.(csls) -2-element Vector{Vector{Level{Orbital{Int64}, Term, Seniority}}}: +2-element Vector{Vector{Level{Orbital{Int64}, Term, SeniorityEnumeration}}}: [|1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1⟩] [|1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 0⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 1⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 2⟩] julia> levels.(csjj) -4-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, Seniority}}}: +4-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, SeniorityEnumeration}}}: [|1s(₁1/2|1/2) 2p(₁3/2|1)-, J = 1⟩] [|1s(₁1/2|1/2) 2p(₁3/2|2)-, J = 2⟩] [|1s(₁1/2|1/2) 2p-(₁1/2|0)-, J = 0⟩] @@ -321,7 +321,7 @@ schemes, sorting by ``M_J`` for clarity: ```jldoctest levels_and_states julia> sort(reduce(vcat, reduce(vcat, states.(csls))), by=s->s.M_J) -12-element Vector{State{Orbital{Int64}, Term, Seniority}}: +12-element Vector{State{Orbital{Int64}, Term, SeniorityEnumeration}}: |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 2, M_J = -2⟩ |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = -1⟩ |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 1, M_J = -1⟩ @@ -336,7 +336,7 @@ julia> sort(reduce(vcat, reduce(vcat, states.(csls))), by=s->s.M_J) |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 2, M_J = 2⟩ julia> sort(reduce(vcat, reduce(vcat, states.(csjj))), by=s->s.M_J) -12-element Vector{State{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, Seniority}}: +12-element Vector{State{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, SeniorityEnumeration}}: |1s(₁1/2|1/2) 2p(₁3/2|2)-, J = 2, M_J = -2⟩ |1s(₁1/2|1/2) 2p(₁3/2|1)-, J = 1, M_J = -1⟩ |1s(₁1/2|1/2) 2p(₁3/2|2)-, J = 2, M_J = -1⟩ diff --git a/src/configurations.jl b/src/configurations.jl index a6ae405..84da3a2 100644 --- a/src/configurations.jl +++ b/src/configurations.jl @@ -937,7 +937,7 @@ non-relativistic orbital with `n` and `ℓ` quantum numbers, with given occupanc ```jldoctest; filter = r"#s[0-9]+" julia> AtomicLevels.rconfigurations_from_orbital(3, 1, 2) -3-element Vector{Configuration{var"#s00"} where var"#s00"<:RelativisticOrbital}: +3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² 3p- 3p 3p² @@ -979,7 +979,7 @@ non-relativistic version of the `orbital` with a given occupancy. ```jldoctest; filter = r"#s[0-9]+" julia> AtomicLevels.rconfigurations_from_orbital(o"3p", 2) -3-element Vector{Configuration{var"#s00"} where var"#s00"<:RelativisticOrbital}: +3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² 3p- 3p 3p² @@ -1037,7 +1037,7 @@ and `occupancy` are integers, and `ℓ` is in spectroscopic notation. ```jldoctest; filter = r"#s[0-9]+" julia> rcs"3p2" -3-element Vector{Configuration{var"#s00"} where var"#s00"<:RelativisticOrbital}: +3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² 3p- 3p 3p² @@ -1064,7 +1064,7 @@ julia> spin_configurations(c"1s2"s) 1s₀α 1s₀β julia> spin_configurations(c"1s ks") -4-element Vector{SpinConfiguration{SpinOrbital{var"#s00", Tuple{Int64, HalfIntegers.Half{Int64}}} where var"#s00"<:Orbital}}: +4-element Vector{SpinConfiguration{SpinOrbital{<:Orbital, Tuple{Int64, HalfIntegers.Half{Int64}}}}}: 1s₀α ks₀α 1s₀β ks₀α 1s₀α ks₀β diff --git a/src/intermediate_terms.jl b/src/intermediate_terms.jl index 119c574..92648c7 100644 --- a/src/intermediate_terms.jl +++ b/src/intermediate_terms.jl @@ -218,7 +218,7 @@ orbital `orb` and occupation `w`. ```jldoctest julia> intermediate_terms(o"2p", 2) -3-element Vector{IntermediateTerm{Term, Seniority}}: +3-element Vector{IntermediateTerm{Term, SeniorityEnumeration}}: ₀¹S ₂¹D ₂³P @@ -229,11 +229,11 @@ which occupancy a certain term is first seen, cf. ```jldoctest julia> intermediate_terms(o"3d", 1) -1-element Vector{IntermediateTerm{Term, Seniority}}: +1-element Vector{IntermediateTerm{Term, SeniorityEnumeration}}: ₁²D julia> intermediate_terms(o"3d", 3) -8-element Vector{IntermediateTerm{Term, Seniority}}: +8-element Vector{IntermediateTerm{Term, SeniorityEnumeration}}: ₁²D ₃²P ₃²D @@ -293,12 +293,12 @@ Generate the intermediate terms for each subshell of `config`. ```jldoctest julia> intermediate_terms(c"1s 2p3") -2-element Vector{Vector{IntermediateTerm{Term, Seniority}}}: +2-element Vector{Vector{IntermediateTerm{Term, SeniorityEnumeration}}}: [₁²S] [₁²Pᵒ, ₃²Dᵒ, ₃⁴Sᵒ] julia> intermediate_terms(rc"3d2 5g3") -2-element Vector{Vector{IntermediateTerm{HalfIntegers.Half{Int64}, Seniority}}}: +2-element Vector{Vector{IntermediateTerm{HalfIntegers.Half{Int64}, SeniorityEnumeration}}}: [₀0, ₂2, ₂4] [₁9/2, ₃3/2, ₃5/2, ₃7/2, ₃9/2, ₃11/2, ₃13/2, ₃15/2, ₃17/2, ₃21/2] ``` diff --git a/src/levels.jl b/src/levels.jl index 454ffd8..2aa31b1 100644 --- a/src/levels.jl +++ b/src/levels.jl @@ -92,17 +92,17 @@ Generate all permissible [`Level`](@ref)s given `csf`. ```jldoctest julia> levels.(csfs(c"1s 2p")) -2-element Vector{Vector{Level{Orbital{Int64}, Term, Seniority}}}: +2-element Vector{Vector{Level{Orbital{Int64}, Term, SeniorityEnumeration}}}: [|1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1⟩] [|1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 0⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 1⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|³Pᵒ)-, J = 2⟩] julia> levels.(csfs(rc"1s 2p")) -2-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, Seniority}}}: +2-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, SeniorityEnumeration}}}: [|1s(₁1/2|1/2) 2p(₁3/2|1)-, J = 1⟩] [|1s(₁1/2|1/2) 2p(₁3/2|2)-, J = 2⟩] julia> levels.(csfs(rc"1s 2p-")) -2-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, Seniority}}}: +2-element Vector{Vector{Level{RelativisticOrbital{Int64}, HalfIntegers.Half{Int64}, SeniorityEnumeration}}}: [|1s(₁1/2|1/2) 2p-(₁1/2|0)-, J = 0⟩] [|1s(₁1/2|1/2) 2p-(₁1/2|1)-, J = 1⟩] ``` @@ -153,7 +153,7 @@ julia> l = Level(first(csfs(c"1s 2p")), 1) |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1⟩ julia> states(l) -3-element Vector{State{Orbital{Int64}, Term, Seniority}}: +3-element Vector{State{Orbital{Int64}, Term, SeniorityEnumeration}}: |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = -1⟩ |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = 0⟩ |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = 1⟩ @@ -176,7 +176,7 @@ julia> c = first(csfs(c"1s 2p")) 1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)- julia> states(c) -1-element Vector{Vector{State{Orbital{Int64}, Term, Seniority}}}: +1-element Vector{Vector{State{Orbital{Int64}, Term, SeniorityEnumeration}}}: [|1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = -1⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = 0⟩, |1s(₁²S|²S) 2p(₁²Pᵒ|¹Pᵒ)-, J = 1, M_J = 1⟩] ``` """ From f85aba7de147014fa951699966df90cf94957a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 16:13:44 +0100 Subject: [PATCH 12/15] En-dash --- docs/src/csfs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/csfs.md b/docs/src/csfs.md index 091515e..8dade4c 100644 --- a/docs/src/csfs.md +++ b/docs/src/csfs.md @@ -44,7 +44,7 @@ In the multi-electron case, the states that you multiply together are the atomic 2. Electrons are fermionic particles adhering to the [Pauli principle](https://en.wikipedia.org/wiki/Pauli_exclusion_principle). - This means that not all direct products of single-particle states are valid (the same single-particle state can not be repeated) or unique (anti-symmetry means that the order in the product does not matter). This, in turn, means that not all the coupled eigenstates predicted by the Clebsch-Gordan relation actually exist and you can not use the Clebsch–Gordan relation directly to determine their coefficients. + This means that not all direct products of single-particle states are valid (the same single-particle state can not be repeated) or unique (anti-symmetry means that the order in the product does not matter). This, in turn, means that not all the coupled eigenstates predicted by the Clebsch–Gordan relation actually exist and you can not use the Clebsch–Gordan relation directly to determine their coefficients. To work within those constraints, AtomicLevels specifies a _coupling scheme_. That is, the CSFs contain additional data that allows the states to be identified uniquely. From 76afeb1411e14dc1bbb839da4cd6eb42e3cdaa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Tue, 10 Jan 2023 16:15:11 +0100 Subject: [PATCH 13/15] Build documentation and run doctests on Julia 1.8 --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 49f852f..ca1b6cd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -48,7 +48,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 with: - version: '1.6' + version: '1.8' - run: | julia --project=docs -e ' using Pkg @@ -65,7 +65,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 with: - version: '1.6' + version: '1.8' - run: | julia --project=docs -e ' using Pkg From a02a7eed91d70f7f1663d19c39d09a85f9feb583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Wed, 11 Jan 2023 08:57:11 +0100 Subject: [PATCH 14/15] Some more doctest fixes --- .github/workflows/CI.yml | 2 +- docs/Project.toml | 2 +- src/configurations.jl | 8 ++++---- src/excited_configurations.jl | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ca1b6cd..b104818 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -72,7 +72,7 @@ jobs: Pkg.develop(PackageSpec(path=pwd())) Pkg.instantiate()' - run: | - julia --project=docs -e ' + julia --project=docs --color=yes -e ' using Documenter: DocMeta, doctest using AtomicLevels DocMeta.setdocmeta!(AtomicLevels, :DocTestSetup, :(using AtomicLevels); recursive=true) diff --git a/docs/Project.toml b/docs/Project.toml index 823df0b..ad828f1 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -3,4 +3,4 @@ AtomicLevels = "10933b4c-d60f-11e8-1fc6-bd9035a249a1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" [compat] -Documenter = "0.25" +Documenter = "0.27" diff --git a/src/configurations.jl b/src/configurations.jl index 84da3a2..dd3e893 100644 --- a/src/configurations.jl +++ b/src/configurations.jl @@ -935,7 +935,7 @@ non-relativistic orbital with `n` and `ℓ` quantum numbers, with given occupanc # Examples -```jldoctest; filter = r"#s[0-9]+" +```jldoctest julia> AtomicLevels.rconfigurations_from_orbital(3, 1, 2) 3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² @@ -977,7 +977,7 @@ non-relativistic version of the `orbital` with a given occupancy. # Examples -```jldoctest; filter = r"#s[0-9]+" +```jldoctest julia> AtomicLevels.rconfigurations_from_orbital(o"3p", 2) 3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² @@ -1035,7 +1035,7 @@ and `occupancy` are integers, and `ℓ` is in spectroscopic notation. # Examples -```jldoctest; filter = r"#s[0-9]+" +```jldoctest julia> rcs"3p2" 3-element Vector{Configuration{<:RelativisticOrbital}}: 3p-² @@ -1054,7 +1054,7 @@ end Generate all possible configurations of spin-orbitals from `configuration`, i.e. all permissible values for the quantum numbers `n`, `ℓ`, `mℓ`, `ms` for each electron. Example: -```jldoctest; filter = r"#s[0-9]+" +```jldoctest julia> spin_configurations(c"1s2") 1-element Vector{SpinConfiguration{SpinOrbital{Orbital{Int64}, Tuple{Int64, HalfIntegers.Half{Int64}}}}}: 1s₀α 1s₀β diff --git a/src/excited_configurations.jl b/src/excited_configurations.jl index 3136e03..a75f7f1 100644 --- a/src/excited_configurations.jl +++ b/src/excited_configurations.jl @@ -184,7 +184,7 @@ substitution will be rejected. # Examples -```jldoctest; filter = r"#s[0-9]+" +```jldoctest julia> excited_configurations(c"1s2", o"2s", o"2p") 4-element Vector{Configuration{Orbital{Int64}}}: 1s² @@ -210,7 +210,7 @@ julia> excited_configurations(first(scs"1s2"), sos"k[s]"...) do dst,src dst end end -9-element Vector{SpinConfiguration{SpinOrbital{var"#s00", Tuple{Int64, HalfIntegers.Half{Int64}}} where var"#s00"<:Orbital}}: +9-element Vector{SpinConfiguration{SpinOrbital{<:Orbital, Tuple{Int64, HalfIntegers.Half{Int64}}}}}: 1s₀α 1s₀β [1s₀α]s₀α 1s₀β [1s₀α]s₀β 1s₀β @@ -223,7 +223,7 @@ julia> excited_configurations(first(scs"1s2"), sos"k[s]"...) do dst,src julia> excited_configurations((a,b) -> a.m == b.m ? a : nothing, spin_configurations(c"1s"), sos"k[s-d]"..., keep_parity=false) -8-element Vector{SpinConfiguration{SpinOrbital{var"#s00", Tuple{Int64, HalfIntegers.Half{Int64}}} where var"#s00"<:Orbital}}: +8-element Vector{SpinConfiguration{SpinOrbital{<:Orbital, Tuple{Int64, HalfIntegers.Half{Int64}}}}}: 1s₀α ks₀α kp₀α From 14e631f65ae50573da8292a476b246e220f1be31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefanos=20Carlstr=C3=B6m?= Date: Fri, 13 Jan 2023 11:13:10 +0100 Subject: [PATCH 15/15] Introduced SpatialOrbital abstract type to distinguish from SpinOrbital This is potentially a breaking change, so we bump the version to 0.2.0. --- Project.toml | 2 +- docs/src/orbitals.md | 1 + src/csfs.jl | 2 +- src/orbitals.jl | 17 +++++++++++++---- src/relativistic_orbitals.jl | 4 ++-- src/spin_orbitals.jl | 17 +++++++---------- src/terms.jl | 2 +- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Project.toml b/Project.toml index a5cb15d..f696480 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "AtomicLevels" uuid = "10933b4c-d60f-11e8-1fc6-bd9035a249a1" authors = ["Stefanos Carlström "] -version = "0.1.9" +version = "0.2" [deps] BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" diff --git a/docs/src/orbitals.md b/docs/src/orbitals.md index 7fb32d5..a93a458 100644 --- a/docs/src/orbitals.md +++ b/docs/src/orbitals.md @@ -24,6 +24,7 @@ generic orbitals can dispatch on that. Orbital RelativisticOrbital AbstractOrbital +SpatialOrbital ``` The [`SpinOrbital`](@ref) type can be used to fully qualify all the quantum numbers (that diff --git a/src/csfs.jl b/src/csfs.jl index 64d6098..7c23f97 100644 --- a/src/csfs.jl +++ b/src/csfs.jl @@ -29,7 +29,7 @@ or non-relativistic orbitals. * If it is a configuration of [`RelativisticOrbital`](@ref)s, both `subshell_terms` and `terms` should both be a list of half-integer values. """ -struct CSF{O<:AbstractOrbital, T<:Union{Term,HalfInteger}, S} +struct CSF{O<:SpatialOrbital, T<:Union{Term,HalfInteger}, S} config::Configuration{<:O} subshell_terms::Vector{IntermediateTerm{T,S}} terms::Vector{T} diff --git a/src/orbitals.jl b/src/orbitals.jl index cdd006a..b66f06b 100644 --- a/src/orbitals.jl +++ b/src/orbitals.jl @@ -10,6 +10,15 @@ Abstract supertype of all orbital types. abstract type AbstractOrbital end Base.Broadcast.broadcastable(x::AbstractOrbital) = Ref(x) +""" + abstract type SpatialOrbital + +Abstract supertype of all spatial orbital types, i.e. those where +projection quantum numbers are _not_ specified, as opposed to +[`SpinOrbital`](@ref)s. +""" +abstract type SpatialOrbital <: AbstractOrbital end + """ const MQ = Union{Int,Symbol} @@ -34,7 +43,7 @@ end # * Non-relativistic orbital """ - struct Orbital{N <: AtomicLevels.MQ} <: AbstractOrbital + struct Orbital{N <: AtomicLevels.MQ} <: SpatialOrbital Label for an atomic orbital with a principal quantum number `n::N` and orbital angular momentum `ℓ`. @@ -66,7 +75,7 @@ julia> Orbital(:K, 2) Kd ``` """ -struct Orbital{N<:MQ} <: AbstractOrbital +struct Orbital{N<:MQ} <: SpatialOrbital n::N ℓ::Int function Orbital(n::Int, ℓ::Int) @@ -278,7 +287,7 @@ macro o_str(orb_str) parse(Orbital, orb_str) end -function orbitals_from_string(::Type{O}, orbs_str::AbstractString) where {O<:AbstractOrbital} +function orbitals_from_string(::Type{O}, orbs_str::AbstractString) where {O<:SpatialOrbital} map(split(orbs_str)) do orb_str m = match(r"^([0-9]+|.)\[([a-z]|[0-9]+)(-([a-z]|[0-9]+)){0,1}\]$", strip(orb_str)) m === nothing && throw(ArgumentError("Invalid orbitals string: $(orb_str)")) @@ -319,7 +328,7 @@ macro os_str(orbs_str) orbitals_from_string(Orbital, orbs_str) end -export AbstractOrbital, Orbital, +export AbstractOrbital, SpatialOrbital, Orbital, @o_str, @os_str, degeneracy, symmetry, isbound, isrelativistic, angular_momenta, angular_momentum_ranges diff --git a/src/relativistic_orbitals.jl b/src/relativistic_orbitals.jl index a318b58..564e1f7 100644 --- a/src/relativistic_orbitals.jl +++ b/src/relativistic_orbitals.jl @@ -46,7 +46,7 @@ function assert_orbital_ℓj(ℓ::Integer, j::Real) end """ - struct RelativisticOrbital{N <: AtomicLevels.MQ} <: AbstractOrbital + struct RelativisticOrbital{N <: AtomicLevels.MQ} <: SpatialOrbital Label for an atomic orbital with a principal quantum number `n::N` and well-defined total angular momentum ``j``. The angular component of the orbital is labelled by the ``(\\ell, j)`` @@ -109,7 +109,7 @@ julia> RelativisticOrbital(:K, 2, 3//2) Kd- ``` """ -struct RelativisticOrbital{N<:MQ} <: AbstractOrbital +struct RelativisticOrbital{N<:MQ} <: SpatialOrbital n::N κ::Int function RelativisticOrbital(n::Integer, κ::Integer) diff --git a/src/spin_orbitals.jl b/src/spin_orbitals.jl index 15d883b..06795a3 100644 --- a/src/spin_orbitals.jl +++ b/src/spin_orbitals.jl @@ -15,18 +15,15 @@ function parse_projection(m::AbstractString) end """ - struct SpinOrbital{O<:Orbital} <: AbstractOrbital + struct SpinOrbital{O<:Orbital,M<:Tuple} <: AbstractOrbital Spin orbitals are fully characterized orbitals, i.e. the projections of all angular momenta are specified. """ -struct SpinOrbital{O<:AbstractOrbital,M<:Tuple} <: AbstractOrbital +struct SpinOrbital{O<:SpatialOrbital,M<:Tuple} <: AbstractOrbital orb::O m::M - function SpinOrbital(orb::O, m::Tuple) where O - O <: SpinOrbital && - throw(ArgumentError("Cannot create SpinOrbital from $O")) - + function SpinOrbital(orb::O, m::Tuple) where {O<:SpatialOrbital} am = angular_momentum_ranges(orb) aml = angular_momentum_labels(orb) nam = length(am) @@ -72,9 +69,9 @@ end degeneracy(::SpinOrbital) = 1 # We cannot order spin-orbitals of differing orbital types -Base.isless(a::SpinOrbital{O,M}, b::SpinOrbital{O,N}) where {O<:AbstractOrbital,M,N} = false +Base.isless(a::SpinOrbital{<:SpatialOrbital,M}, b::SpinOrbital{<:SpatialOrbital,N}) where {M,N} = false -function Base.isless(a::SpinOrbital{<:O,M}, b::SpinOrbital{<:O,M}) where {O,M} +function Base.isless(a::SpinOrbital{O,M}, b::SpinOrbital{O,M}) where {O<:SpatialOrbital,M} a.orb < b.orb && return true a.orb > b.orb && return false @@ -131,7 +128,7 @@ end # * Orbital construction from strings -function Base.parse(::Type{O}, orb_str) where {OO<:AbstractOrbital,O<:SpinOrbital{OO}} +function Base.parse(::Type{O}, orb_str) where {OO<:SpatialOrbital,O<:SpinOrbital{OO}} m = match(r"^(.*)\((.*)\)$", orb_str) # For non-relativistic spin-orbitals, we also support specifying # the m_ℓ quantum number using Unicode subscripts and the spin @@ -231,7 +228,7 @@ julia> spin_orbitals(ro"2p") ``` """ -function spin_orbitals(orb::O) where {O<:AbstractOrbital} +function spin_orbitals(orb::SpatialOrbital) map(reduce(vcat, Iterators.product(angular_momentum_ranges(orb)...))) do m SpinOrbital(orb, m...) end |> sort diff --git a/src/terms.jl b/src/terms.jl index 71a2f0b..d49a846 100644 --- a/src/terms.jl +++ b/src/terms.jl @@ -296,7 +296,7 @@ julia> terms(c"[Ne] 3d3") ⁴F ``` """ -function terms(config::Configuration{O}) where {O<:AbstractOrbital} +function terms(config::Configuration) ts = map(config) do (orb,occ,state) terms(orb,occ) end