PAOFLOW.basis_gen.radial#

Radial Schroedinger solver for norm-conserving, USPP and PAW pseudopotentials.

The pseudo-atom Hamiltonian acting on the radial part \(u(r) = r R(r)\) of \(\psi_{lm}(r) = R(r) Y_{lm}\) is, for a given \((l, j)\) channel,

\[H u(r) = -\frac{1}{2} u''(r) + \left[V_{\mathrm{loc}}(r) + \frac{l(l+1)}{2r^2}\right] u(r) + r \sum_{i,j \in (l,j)} \beta_i(r) D_{ij} \langle\beta_j | R\rangle\]

where \(\langle\beta_j | R\rangle = \int \beta_j(r) R(r) r^2 \, dr\). Using the UPF-stored quantity \(a_i(r) = r \beta_i(r)\) the nonlocal kernel in \(u\)-space becomes the symmetric outer product

\[M(r, r') = \sum_{i,j} a_i(r) D_{ij} a_j(r').\]

For ultrasoft / PAW pseudopotentials the augmentation overlap operator

\[S = 1 + \sum_{ij} q_{ij} |\beta_i\rangle\langle\beta_j| \quad\rightarrow\quad S_{uu'} = I + \mathrm{d}r\, a Q a^T\]

is built from upf.qqq and the eigenproblem becomes the generalized \(H u = \varepsilon S u\) (solved with scipy.linalg.eigh(H, S)); the returned \(u(r)\) is then normalised to \(\langle u|S|u\rangle \, \mathrm{d}r = 1\). For NC pseudos (no augmentation) the path collapses to np.linalg.eigh(H) with the ordinary \(L^2\) normalisation.

We discretise on a uniform mesh \(r_k = k \, \mathrm{d}r\), \(k = 1, \ldots, N-1\), with \(u(0) = u(R_{\mathrm{box}}) = 0\), \(\mathrm{d}r = R_{\mathrm{box}} / N\). \(V_{\mathrm{loc}}\) and \(a_i\) are interpolated (cubic spline) from the UPF log mesh onto this uniform grid; outside each projector’s cutoff_radius \(a_i\) is zero.

For SO UPFs the projectors are filtered by matching \((l, j)\) – callers that request \((n, l)\) with j = None on an SO UPF get the \(j = l + 1/2\) channel by default (override via the j argument).

All quantities are in Hartree atomic units. Output radial functions are returned in the QE convention used by build_aewfc_basis: the returned wfc[k] equals \(r_k R(r_k)\) (i.e. \(u(r_k)\)), normalised so that \(\sum_k \mathrm{wfc}[k]^2 \, \mathrm{d}r = 1\).

Functions#

solve_radial_channel(upf, l[, j, r_box, n_points, ...])

Solve H u = eps u for a single (l, j) channel in a box.

pseudize_shell(upf, n, l[, j, r_box, n_points])

Return the (n, l[, j]) radial function on a uniform mesh.

is_frozen_core_shell(upf, n, l, eps[, j])

Heuristic: is (n, l[, j]) a spurious frozen-core request?

Module Contents#

PAOFLOW.basis_gen.radial.solve_radial_channel(upf, l, j=None, r_box=None, n_points=2000, n_states=6)[source]#

Solve H u = eps u for a single (l, j) channel in a box.

Parameters:
  • upf (PAOFLOW.inputs.read_upf.UPF) – Parsed pseudopotential (norm-conserving).

  • l (int) – Orbital angular momentum.

  • j (float, optional) – Total angular momentum. Required (or auto-defaulted) only for spin-orbit UPFs.

  • r_box (float, optional) – Confining box radius (Bohr). Default min(upf.r[-1], 10.0).

  • n_points (int) – Number of intervals on the uniform mesh; the eigenproblem has n_points - 1 interior unknowns.

  • n_states (int) – Number of lowest eigenstates to return.

Returns:

  • eps (ndarray, shape (n_states,)) – Eigenvalues in Hartree.

  • u (ndarray, shape (n_states, n_points - 1)) – Eigenfunctions u_n(r) = r * R_n(r) on the interior mesh, normalised so that sum u_n[k]**2 * dr = 1.

  • r (ndarray, shape (n_points - 1,)) – Interior mesh r_k = k * dr.

PAOFLOW.basis_gen.radial.pseudize_shell(upf, n, l, j=None, r_box=None, n_points=2000)[source]#

Return the (n, l[, j]) radial function on a uniform mesh.

Pseudopotentials do not bind the core states, so the principal quantum number cannot be identified with node_count + l + 1 (the all-electron rule). Instead the requested state is selected as the (n - n_lowest)-th eigenstate of the radial pseudo-atom Hamiltonian for this (l, j) channel, where n_lowest is

  • the smallest principal quantum number among the UPF PSWFC entries with matching (l, j), if any are present, or

  • the atomic-physics minimum n for that l (S->1, P->2, D->3, F->4) otherwise (augmentation channels that have no PSWFC counterpart).

For Pt ONCV-fr (l=0) PSWFC = 5S, 6S => n_lowest = 5, so pseudize_shell(upf, 5, 0) picks rank 0, (6, 0) rank 1, and (7, 0) rank 2. For Si ONCV (l=2) (no PSWFC D entry) n_lowest = 3, so (3, 2) picks rank 0.

Note

For PAW pseudopotentials with deep projector subspaces (e.g. an extra semi-core projector at a given l) the lowest-rank eigenstate of the generalized augmented problem can be a spurious deep “ghost” state at one or two Hartree below the physical valence level. The rank heuristic above does not distinguish ghosts from physical states; affected channels should be inspected by the caller (overlap with the stored PSWFC is a reliable filter).

PAOFLOW.basis_gen.radial.is_frozen_core_shell(upf, n, l, eps, j=None)[source]#

Heuristic: is (n, l[, j]) a spurious frozen-core request?

A pseudopotential that freezes a shell into the core (e.g. As 3d, whose 3d electrons are not in the As pseudo valence) carries no PSWFC for that shell and cannot bind it. Requesting such a shell makes pseudize_shell() return an unbound, diffuse box mode that is linearly dependent with the genuine polarization shells and corrupts the projection basis.

The request is flagged as frozen-core when all of the following hold:

  • the UPF has no PSWFC with the exact (n, l[, j]) label (a genuine semicore shell, e.g. Ga 3D, is present and so is never flagged);

  • n lies below the largest PSWFC principal quantum number nmax (above-valence polarization such as Ga 4D has n >= nmax and is never flagged); and

  • the solver eigenvalue is non-negative (the returned state is unbound, the signature of a confinement mode rather than a bound orbital).