Add support for arbitrary pseudopotential families to get_pseudo_orbitals - #94
Open
elinscott wants to merge 6 commits into
Open
Add support for arbitrary pseudopotential families to get_pseudo_orbitals#94elinscott wants to merge 6 commits into
elinscott wants to merge 6 commits into
Conversation
… tables get_pseudo_orbitals resolved pseudos exclusively by md5 against the bundled semicore tables, so any family without a table (SG15, custom pseudos, ...) failed outright with "Cannot find pseudopotential <X> with md5 ...". Instead of accreting per-family JSON files, resolve in three tiers: an explicit per-kind `overrides` mapping of `PseudoOrbitals` TypedDicts (threaded through Wannier90BaseWorkChain.get_builder_from_protocol as `pseudo_orbitals_overrides`), then the bundled tables, then an aufbau inference from the pseudo's z_valence (cross-checked against the UPF's atomic-wave-function angular momenta when readable, warning emitted, semicores left empty so nothing is silently excluded). The hard error remains only when inference is impossible. The inference and tier precedence are unit-tested in tests/utils/test_pseudo.py with duck-typed pseudo stubs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
elinscott
force-pushed
the
pseudo-orbitals-fallback
branch
from
July 8, 2026 12:28
751f408 to
20c7d37
Compare
Collaborator
Author
|
Note: tests expected to fail until #95 is merged |
Fully-relativistic pseudopotentials list one atomic wavefunction per j channel (l > 0 shells appear twice, j = l +/- 1/2), so the raw angular-momentum multiset never matches the per-shell aufbau inference and every FR pseudo outside the bundled tables was rejected. Count each l > 0 shell once per j pair before comparing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t the UPF _infer_pseudo_orbitals returned an unvalidated aufbau guess whenever the UPF cross-check could not run (unparseable content, empty or absent atomic wave functions), so out-of-table heavy and f-block pseudos silently produced wrong orbital sets (e.g. Au z_valence=11 -> ['4F','5D'], Pb z_valence=4 -> ['5D','6P']) instead of raising. The cross-check now reads the UPF angular momenta via upf-tools (added to dependencies), and any failure to read them returns None so get_pseudo_orbitals raises the pre-existing ValueError. Warning text now states the inference was validated against the UPF. Tests drive the cross-check with parseable synthetic UPF content: scalar- and fully-relativistic pass cases, a heavy-element rejection, and a fail-closed unparseable case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_infer_pseudo_orbitals guessed the valence orbitals from z_valence via an aufbau (reverse-Madelung) walk and then validated the guess against the UPF's angular momenta. The walk is wrong for f-block and heavy elements, where it mistakes deep-core f shells for valence (Au z_valence=19 -> ['4F','5D'] instead of 5S 5P 5D 6S; Pb -> ['5D','6P'] instead of 5D 6S 6P), so those pseudos failed validation and raised even though the UPF states its own valence. Replace the guess-then-validate path with _derive_pseudo_orbitals_from_upf, which reads the labels directly from each PP_CHI: the spectroscopic letter from the angular momentum l, and the principal quantum number from the label's leading digits. The label's n is preferred over the PP_CHI n attribute on purpose: some ultrasoft/PAW generators write the pseudo principal number there (radial nodes + l + 1), so a node-free 6s/6p/5d projector carries n=1/2/3 while the label keeps the true 6S/6P/5D. Reading the attribute produced nonsense such as Mg -> ['1S','2P'] and Au.pz-rrkjus -> ['2P','3D','1S']; using the label gives ['3S','3P'] and ['6P','5D','6S']. The attribute is used only when the label has no leading digit (empty or non-standard label). Fully-relativistic j-splits collapse to one label per unique (n, l) in file order. The aufbau walk is deleted; z_valence now only backs a soft occupation-sum sanity check that warns rather than rejects. Fail-closed behaviour is preserved: an unreadable or absent PP_PSWFC returns None so get_pseudo_orbitals raises the pre-existing overrides ValueError. Warning text now says the orbitals were read from the pseudopotential file. Verified by deriving orbitals for all 144 PseudoDojo nc-sr-05 SR UPFs (pswfcs match the curated md5 table and occupations sum to z_valence for every file), plus out-of-table ONCV (sr and FR), ultrasoft/PAW and old v1-format pseudos: heavy elements now come out physically correct, the FR j-split collapses, and a real ONCV pseudo with no PP_PSWFC (He) fails closed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A disagreement can indicate a valence orbital missing from PP_PSWFC, which previously produced only a warning while returning a possibly incomplete orbital set. Zero of 1202 real UPFs tested trip the check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
get_pseudo_orbitalsresolves pseudopotentials exclusively by md5 lookup against the bundled semicore tables, so any pseudo outside the curated families fails outright withCannot find pseudopotential <X> with md5 ...Change
Resolution now proceeds in three steps, and only the last resort is an error:
get_pseudo_orbitalsgains anoverridesmapping, threaded throughWannier90BaseWorkChain.get_builder_from_protocolaspseudo_orbitals_overrides, so a user can state{"Ti": {"pswfcs": ["3S", "3P", "4S", "3D"], "semicores": ["3S", "3P"]}}and bypass all lookup.PP_PSWFCblock (viaupf-tools): eachPP_CHIatomic wave function states its angular momentum and principal quantum number, so the pseudo wave function labels (3S,5D, ...) are read off rather than guessed. The UPF states its own valence, so there is no aufbau guessing.PP_CHIlabel, which carries the true atomicnacross generators; thePP_CHInattribute is deliberately not used for it, because some ultrasoft/PAW generators write the pseudo (node-based) principal number there (radial nodes + l + 1), so a node-free 6s/6p/5d projector is labelled n=1/2/3. When a label is empty or non-standard the label is reconstructed from the entry'sn/l.PP_CHIper j channel; these are collapsed to one label per unique (n, l), in file order.semicoreslist, and a warning is emitted stating that the orbitals were read from the pseudopotential file and how to override them.Raises
ValueErrorwhen the orbitals cannot be read from the file (no parseablePP_PSWFC, no atomic wave functions, an entry whose (n, l) cannot be recovered, or occupations that do not sum toz_valence), with a message telling the user how to provide this information explicitly viaoverrides.Notes
exclude_semicore: Truewith a UPF-derived entry excludes nothing (becausesemicoresis not read from the file).PP_PSWFCblock is read viaupf-tools, added as a dependency.PP_CHIoccupations must agree withz_valence(to numerical noise); a disagreement can mean a valence orbital is missing fromPP_PSWFC, so the derivation fails closed to theoverridesValueError.ValueErrorasking for anoverridesentry. Notably this includes the entire SG15 family (its UPFs ship an emptyPP_PSWFCblock), so SG15 still requires a bundled table entry or explicitoverrides; deriving from other UPF sections for such pseudos would be a separate follow-up.Testing
Unit tests in
tests/utils/test_pseudo.pycover the derivation (SR, FR j-collapse, label edge cases), the fail-closed paths, and the tier precedence, all on synthetic UPF content.The derivation was also swept over 1202 real UPFs (PseudoDojo SR+FR, SG15, plus assorted ONCV/ultrasoft/PAW/v1 files): zero wrong label sets — every derivation matched the curated tables or labels predicted independently from
PP_INPUTFILE, and the 202 SG15 files fail closed as intended.