Notes: hydrogenic energies

Point nucleus

The energies for point nuclei (represented with PointNucleus) are calculated using analytic formulae.

For a non-relativisic particles (NRParticle) the energy is given by

\[E_n = - \frac{(Z\alpha)^2}{2 n^2} mc^2\]

and for relativistic particles (DiracParticle) by

\[m c^2 \left\{\left[1 + \left( \frac{Z\alpha}{n - |\kappa| + \sqrt{\kappa^2 - (Z\alpha)^2}} \right)^2 \right]^{-1/2} - 1 \right\}\]

The only thing to note is that for the relativistic case, we set the $E = 0$ for $Z = 0$, so that the convention for the energy scale for the relativistic and non-relativistic cases would be the same.

Uniform spherical shell

There are no analytical expressions for the energies for finite nuclei. However, in the case of the uniform spherical shell (UniformShellNucleus; aka. cut-off Coulomb, top-slice model), the potential is simple and the equations solvable in the ranges $[0, R]$ and $[R, \infty)$ separately.

Following the expressions in Andrae2000, we implement a root-finding based algorithm for determining the energies, in both the relativistic and non-relativistic case.

# We use this function below to showcase the performance of the implementation of hydrogenic_energies.
function plot_fnc_energies(p; n,
    Rs = [0.5, 1e-1, 5e-2, 2e-2, 1e-2, 7e-3, 3e-3, 1e-3, 1e-4],
    qnumbers...
)
    Z = range(1, isa(p, AtomicMiscellany.DiracParticle) ? α^-1 : 150, length=1001)
    emin = hydrogenic_energy.(p, Z; n = n, qnumbers...)
    emax = hydrogenic_energy.(p, Z; n = n + 1, qnumbers...)
    p1 = plot(hcat(Z,Z), hcat(emin,emax), c=:black, ls=:dash, legend=:bottomleft, label=false)
    p2 = plot(hcat(Z,Z), hcat(emin ./ abs.(emin), emax ./ abs.(emin)), label=false, c=:black, ls=:dash)
    for (i, R) = enumerate(Rs)
        es = map(Z) do Z
            try
                hydrogenic_energy(p, UniformShellNucleus(R), Z; n = n, qnumbers...)
            catch e
                return NaN
            end
        end
        plot!(p1, Z, es, label="R=$R", c = i)
        plot!(p2, Z, es./ abs.(emin), label=false, c = i)
    end
    ylabel!(p1, "E"); ylabel!(p2, "E / E(PNC)")
    xlabel!(p2, "Nuclear charge Z")
    plot(p1, p2, layout=(2,1), size=(800, 600))
    vline!([α^-1 α^-1], label=false, c=:black, ls=:dot)
    title!("n = $n, $(qnumbers...)", subplot=1)
end

Non-relativisic energies

Calculates the matching function $f$ derived from Andrae2000, equations (247-248, 256-258). $f$ is derived by multiplying the equation through with the denominators to remove any infinities these denominators might bring.

The function AtomicMiscellany.f_andrae_nonrel_topslice implements the following function, which is then fed into a root-finder:

\[\begin{aligned} f(E, r, Z, R, \ell) &= \beta j_\ell(x_{\rm{in}}) U(a, b, x_{\rm{out}}) \\ &+ 2 \beta a j_\ell(x_{\rm{in}}) U(a+1, b+1, x_{\rm{out}}) \\ &- \alpha j_{\ell+1}(x_{\rm{in}}) U(a, b, x_{\rm{out}}) ≡ 0 \end{aligned} \\[1em] \alpha_{n\ell} = \sqrt{2(E + Z/R)}, \quad x_{\rm{in}} = \alpha_{n\ell} r, \quad \beta_{n\ell} = \sqrt{-2E}, \quad x_{\rm{out}} = 2 \beta_{n\ell} r, \\[1em] a = \frac{b}{2} - \nu_{n\ell}, \quad b = 2(\ell + 1), \quad \nu_{n\ell} = Z/\beta_{n\ell}\]

# We can use this function to visualize f:
function plot_nonrel_fnc_f(; R, Z, ℓ)
    emin = hydrogenic_energy(NRElectron, Z; n = 1)
    # Excluding the first point, since at E = -Z/R, f is zero, which causes problems
    es = range(max(1.1emin, -Z/R), 0, length=502)[2:end-1]
    ys = (E -> AtomicMiscellany.f_andrae_nonrel_topslice(Z=Z, r=R, R=R, ℓ=ℓ, E = E)).(es)
    any(iszero.(ys)) && @warn "Zeros in ys"
    plot(es, abs.(ys), yaxis=:log10, legend=false, c = (y -> y>0 ? 2 : 1).(ys))
    absy_sorted = filter(!iszero, sort(abs.(ys)))
    ylims!(absy_sorted[1]/2, absy_sorted[490])
    e0s = map(ℓ .+ (1:6)) do n
        try
            hydrogenic_energy(NRElectron, UniformShellNucleus(R), Z; n = n, ℓ = ℓ, verbose=false)
        catch
            NaN
        end
    end
    xlabel!("E"); ylabel!("log |f|")
    title!("Z=$(fmt(".2f", Z)), ℓ=$ℓ, R=$(fmt(".2e", R))")
    vline!(e0s, c=:gray)
    vline!([hydrogenic_energy(NRElectron, Z; n = n) for n = 1:3], c=:black, ls=:dash)
    plot!(size=(800, 400))
end

Here is $f(E)$ for $Z = 1$ and $\ell = 0$, for two very different nuclear sizes. $R = 10^{-4}~\mathrm{a.u.}$ is approximately physical, whereas $R = 2.5~\mathrm{a.u.}$ is unphysically huge. However, the latter is useful for showing how the graph of the function shifts as we make the nucleus bigger and bigger.

The black dashed vertical lines indicate the hydrogenic energies for point nuclei, which can be determined using explicit formulae. As we need to specify a bracketing interval for the root-finder, we use those energies. This seems like a reasonable choice, as we know that the FNC energy will always we higher than the corresponding PNC energy, but unlikely to reach the PNC energy of the 2s, unless the parameters are very extreme.

plot(
    plot_nonrel_fnc_f(R = 1e-4, Z = 1, ℓ = 0),
    plot_nonrel_fnc_f(R = 2.5, Z = 1, ℓ = 0),
    layout = (2, 1), size=(800, 600),
)

To illustrate the potential problems with the bracketing intervals, let's see what happens (for a huge nucleus) when we increase $Z$ a lot. Eventually, we will hit the 2s energy. However, this should not really happen in any physically relevant cases.

a = @animate for Z = range(1, 550, length=101)
    plot_nonrel_fnc_f(R = 1e-2, Z = Z, ℓ = 0)
end
gif(a, fps=10)

To showcase the algorithm an its limits, here are the 1s energies for various nuclear sizes (R values). The black dashed lines once again indicate the bracketing interval of PNC energies.

plot_fnc_energies(NRElectron, n = 1, ℓ = 0)

Now, if we do the same thing for $n = 2$, we start seeing weird cases where

plot_fnc_energies(NRElectron, n = 2, ℓ = 0)

This can easily be understood by looking at the behaviour of $f$ as you increase $Z$ for a large nucleus (R = 0.1):

a = @animate for Z = range(25, 100, length=101)
    plot_nonrel_fnc_f(R = 0.1, Z = Z, ℓ = 0)
end
gif(a, fps=10)

At one point, the zero corresponding to the 1s energy enters the bracketing interval of the 2s. Root-finder, of course, can not distiguish that, so it just returns the 1s energy. Again though, this is only an issue if the value of $R$ or $Z$ gets extremely large.

We see the same happening for higher $\ell$ values too. One thing to note is that we also get in trouble for the lowest eigenvalue, because the $E = -Z/R$, below which $f(E)$ becomes complex, enters the $n=2$ bracketing interval.

plot_fnc_energies(NRElectron, n = 2, ℓ = 1)
a = @animate for Z = range(25, 100, length=101)
    plot_nonrel_fnc_f(R = 0.1, Z = Z, ℓ = 1)
end
gif(a, fps=10)

However, again, for reasonable $R$ and $Z$ values, everything behaves reasonably. Also, correctly, we do not get any zeros at lower $n$ values for higher $\ell$ values:

plot(
    plot_nonrel_fnc_f(R = 1e-4, Z = 1, ℓ = 0),
    plot_nonrel_fnc_f(R = 1e-4, Z = 1, ℓ = 1),
    plot_nonrel_fnc_f(R = 1e-4, Z = 1, ℓ = 2),
    layout = (3, 1), size=(800, 900),
)

Relativistic energies

The relativistic energy is determined in essentially the same way as in the non-relativistic case. The relevant equation in Andrae2000 is (284), setting up the matching condition between the inner and outer parts of the orbital:

\[L^{\mathrm{in}}_{n\kappa}(R) - \frac{\gamma}{R} = - T^{\mathrm{out}}_{n\kappa}(R)\]

$L^{\mathrm{in}}_{n\kappa}(r)$ is the logarithmic derivative of the $P$ component of the inner part of the wavefunction (i.e. for $r \leq R$). According to equations (273) and (274), it can be written as

\[L^{\mathrm{in}}_{n\kappa}(r) = \frac{\ell + 1}{r} - T^{\mathrm{in}}_{n\kappa}(r) = \frac{\ell + 1}{r} - \alpha_{n\kappa} \frac{j_{\ell+1}(x_{\mathrm{in}})}{j_{\ell}(x_{\mathrm{in}})} \\[1em] \alpha_{n\kappa} = \sqrt{2 (E + Z/R) + [(E + Z/R)/c]^2}, \quad x_{\mathrm{in}} = \alpha_{n\kappa} r\]

$T^{\mathrm{out}}_{n\kappa}(r)$, which is related to the outer part of the orbital, is given in equation (283)

\[T^{\mathrm{out}}_{n\kappa}(r) = \beta_{n\kappa} + 2 \beta_{n\kappa} \frac{ a U(a+1, b+1, x_{\mathrm{out}}) + A \cdot (a+1) U(a+2, b+1, x_{\mathrm{out}}) }{ U(a, b, x_{\mathrm{out}}) + A \cdot U(a + 1, b, x_{\mathrm{out}}) } \\[1em] x_{\mathrm{out}} = 2 \beta_{n\kappa} r, \quad \beta_{n\kappa} = C^+ C^- / c, \quad C^{\pm} = \sqrt{c^2 \pm W_{n\kappa}} \\[1em] W_{n\kappa} = E + c^2, \quad v_{n\kappa} = \frac{1}{2} + \frac{Z W_{n\kappa}}{\beta_{n\kappa} c^2}, \quad A = \frac{Z}{\beta_{n\kappa}} + \kappa \\[1em] a = b/2 - v_{n\kappa}, \quad b = 2\gamma + 1, \quad \gamma = \sqrt{\kappa^2 - (Z/c)^2}\]

We can get rid of the denominators, which may cause some unwanted singularities, and write down the following condition

\[\begin{aligned} f(E) & = \left( \frac{\ell + 1 - \gamma}{R} + \beta_{n\kappa} \right) (U_{00} + A U_{10}) J_0 \\ & - \alpha_{n\kappa} (U_{00} + A U_{10}) J_1 \\ & + 2 \beta_{n\kappa} [a U_{11} + A (a + 1) U_{21}] ≡ 0 \end{aligned} \\[1em] J_i = j_{\ell + 1}(x_{\mathrm{in}}), \quad U_{ij} = U(a + i, b + j, x_{\mathrm{out}})\]

The special functions $j_\ell(x)$ and $U(a, b, x)$ are the spherical bessel and irregular confluent hypergeometric functions, respectively. They are evaluated using the corresponding functions from GSL (sf_bessel_jl, sf_hyperg_U), via GSL.jl.

# The following function can be used to visualize ``f``.
function plot_rel_fnc_f(; R, Z, κ, energies = nothing, plotfn=plot)
    es = if isnothing(energies)
        emin = hydrogenic_energy(DiracElectron, Z; n = 1, κ = κ)
        # Excluding the first point, since at E = -Z/R, f is zero, which causes problems
        es = range(max(1.1emin, -Z/R), 0, length=502)[2:end-1]
    else
        energies
    end
    ys = (E -> AtomicMiscellany.f_andrae_dirac_topslice(Z=Z, R=R, κ=κ, E = E)).(es)
    any(iszero.(ys)) && @warn "Zeros in ys"
    plotfn(es, abs.(ys), yaxis=:log10, legend=false, c = (y -> y>0 ? 2 : 1).(ys))
    if isnothing(energies)
        absy_sorted = filter(!iszero, sort(abs.(ys)))
        ylims!(absy_sorted[1]/2, absy_sorted[490])
    end
    e0s = map(AtomicMiscellany.κ2ℓ(κ) .+ (1:6)) do n
        try
            hydrogenic_energy(DiracElectron, UniformShellNucleus(R), Z; n = n, κ = κ, verbose=false)
        catch e
            @warn "Unable to find root for n=$n" error=(e, catch_backtrace())
            NaN
        end
    end
    xlabel!("E"); ylabel!("log |f|")
    title!("Z=$(fmt(".2f", Z)), κ=$κ, R=$(fmt(".2e", R))")
    vline!(e0s, c=:gray)
    vline!([hydrogenic_energy(DiracElectron, Z; n = n, κ = κ) for n = 1:6], c=:black, ls=:dash)
    plot!(size=(800, 400))
end

The general behavior of $f$ is very similar to the non-relativistic case. However, the expressions given in Andrae2000 require us to evaluate $\gamma = \sqrt{\kappa^2 - (Z/c)^2}$, so the expressions would become complex for $Z > α^{-1}$. For that reason we restrict ourselves to $Z \leq α^{-1}$.

plot(
    plot_rel_fnc_f(R = 1e-4, Z = 1, κ = -1),
    plot_rel_fnc_f(R = 5e-1, Z = 1, κ = -1),
    layout = (2, 1), size=(800, 600),
)
plot_fnc_energies(DiracElectron, n = 1, κ = -1)

As in the non-relativistic case, we run into issues with the lower energy zeros coming into the bracketing interval once the parameters become extreme enough (as we're limited in $Z$, this primarily means $R$ in this case).

plot_fnc_energies(DiracElectron, n = 2, κ = -1)
plot_fnc_energies(DiracElectron, n = 3, κ = -1)
plot_fnc_energies(DiracElectron, n = 2, κ = 1)

We do run into trouble if we try to find the zeros for higher angular momenta:

plot_fnc_energies(DiracElectron, n = 2, κ = -2)
plot_fnc_energies(DiracElectron, n = 3, κ = 2)

This, turns out, is due to the limited resolution of 64-bit floats. We can zoom in on around the 1s zero. We see that $f(E)$ evaluates to the same value for many $E$ values, implying a rounding problem. This, in turn, leads to the root-finder not being happy, since the bracketing interval does not encompass any positive $f(E)$ values.

let R = 1e-3, Z = 1, κ = -2,
    e0 = hydrogenic_energy(DiracElectron, Z; n = 2, κ = κ),
    (emin, emax) = e0 .+ (-1, 1) .* 0.000_000_000_05
    plot_rel_fnc_f(R=R, Z=Z, κ=κ, energies = range(emin, emax, length=151), plotfn=scatter)
    xlims!(emin, emax)
end

There are workarounds (modify the mathematical expressions for better numerical stability, use BigFloats), but for now this is simply a limitation of the current implementation.

Also, the non-relativistic version should also run into this issue at high enough $\ell$.


This page was generated using Literate.jl.