Handover — basissetdev (2026-05-14)

Read this first

You are picking up the basis-set development chat from a predecessor session. The work track is on a long-lived branch called basissetdev that never merges into main during v0.8.x (CLAUDE.md § 4 standing rule — paper-writing branch). Everything below was pushed to origin/basissetdev on 2026-05-14.

Three docs you should read before touching anything (all live on basissetdev, in docs/basisset_dev/):

  1. PLAN.md — the goal list (Goals 1-8) + branch mechanics.

  2. GOAL8_MPEI_TZVP.md — the active goal: HF-optimized basis set design doc with methodology table, architecture diagram, wall-time budget, Stage 0-4 acceptance gates.

  3. REQUIREMENTS-PERIODIC.md — the periodic-SCF feature punch list (R1-R12); Goal 8 deliberately avoids depending on most of it by using CRYSTAL14 as the SCF engine.

Two memory notes (user-level, at ~/.claude/projects/-Users-mpei-mpei-documents-vibeqc/memory/):

  1. reference_planetx_vibeqc_basis.md — the dedicated planetx checkout at /home/mpei/gitlab/vibeqc-basis/, the setup recipe, and the coexistence rule with other planetx checkouts.

  2. reference_crystal14_via_vq.md — the vq + run-crystal.sh recipe for CRYSTAL14 jobs on planetx.

State as of 2026-05-14

What’s done

Goal 1 (pob basis-set audit) — closed.

  • The S d-polarisation column-swap bug in pob-TZVP / pob-TZVP-rev2 was caught and fixed (commit 861e2d0).

Goal 2 (periodic-SCF feature handover) — closed.

  • REQUIREMENTS-PERIODIC.md shipped with R1-R12 + PW1PW citation.

Goal 3 (self-contained test-set inputs) — Phases 1+2+3 landed. 39 cubic compounds covered:

  • Phase 1 — 13 cubic ionics (PT2013 T4 + T14)

  • Phase 2 — 24 cubic semiconductors / carbides / nitrides / AFM TM oxides

  • Phase 3 — Cu₂O, Cu₃N

  • Each compound has a .py vibe-qc input + (for non-AFM) a .d12 CRYSTAL14 parity sidecar in examples/basisset_dev/inputs/.

  • Phase 4 (hex/tet) is blocked on REQUIREMENTS-PERIODIC R1 and stays parked.

Goal 7 (BSE library expansion) — 87 basis sets fetched from the Basis Set Exchange and bundled (commit 8964a98).

Phase 14a-14h libecpint integration — closed. Banner, splitter, total_ncore valence-electron accounting in SCF, the auto_ecp_centers helper, Python bridge between CRYSTAL ECP blocks and libecpint inline arrays. Phase 14g proper (the C++ binding for compute_ecp_matrix_inline) is parked for a focused C++ session — not on the Goal 8 critical path.

Goal 8 (mpei-TZVP) — design + plumbing, the active work track.

  • Design doc opened (GOAL8_MPEI_TZVP.md), 4 stages with explicit acceptance gates.

  • User-confirmed choices (2026-05-13):

    • Seed basis: pob-TZVP-rev2.

    • SCF engine: CRYSTAL14 on planetx via vq — recorded rationale (“too slow, no symmetry, periodic SCF still buggy”) in §3.1.

    • Optimizer: NLopt BOBYQA primary + iminuit MIGRAD/HESSE for error bars.

    • LD handling: hard exponent floor 0.15 (rev2 inheritance).

    • Outreach gate: Stage 2 (LiH one-compound MIGRAD) success triggers university outreach.

vibe-basis package spun up (2026-05-13) — separate top-level monorepo subdir, parallel to vibe-queue/. Owns the optimizer / orchestration; never imports vibeqc; independently pip install -e .-able with extras [nlopt|iminuit|scipy|vq|all]. CLI vb. Module layout:

vibe-basis/src/vibe_basis/
├── __init__.py                  __version__ = "0.1.0.dev0"
├── cli.py                       `vb --version`, `vb parse <backend> <out>`
├── backends/
│   ├── __init__.py
│   └── crystal14.py             parse_output / parse_output_file / emit_input
├── transports/
│   ├── __init__.py
│   ├── base.py                  Transport ABC + JobHandle/JobResult/wait/run
│   ├── vq.py                    VqTransport — planetx via vq CLI
│   └── local.py                 LocalTransport — laptop smoke
├── io/
│   ├── __init__.py
│   └── structures.py            39-compound DB (migrated from
│                                examples/basisset_dev/_structures.py)
└── recipes/
    └── __init__.py              (empty — pob_parity.py is the next file)

Test counts (last green: 6718d1b)

tests/basisset_dev/ (vibe-qc side): 38 passed, 2 skipped
vibe-basis/tests/                 : 61 passed
Combined                          : 99 passed, 2 skipped

vibe-basis tests run via:

cd vibe-basis && ../.venv/bin/python -m pytest -q

vibe-qc tests run from the repo root with .venv/bin/python -m pytest tests/basisset_dev/.

State of the planetx checkout

Per reference_planetx_vibeqc_basis.md:

  • Path: /home/mpei/gitlab/vibeqc-basis/ (basissetdev branch checked out).

  • venv: /home/mpei/gitlab/vibeqc-basis/.venv/ — Python 3.14, has vibe-basis installed editable with click + numpy + pytest.

  • Status as of last user contact: vb --version works, vb parse --help prints, 39 vibe-basis tests pass (count was 39 before the structure-DB migration; pull the latest and rerun, should be 61).

  • vibe-qc itself is not installed in that venv (and doesn’t need to be — vibe-basis is standalone).

  • The vq daemon (on planetx, out of vibeqc-queue/’s install) is the shared queue — submitting from vibeqc-basis/ uses it the same way any chat does.

Pull command for the next chat to land at the current tip

ssh planetx
cd /home/mpei/gitlab/vibeqc-basis
git pull --ff-only origin basissetdev
cd vibe-basis && ../.venv/bin/python -m pytest -q   # expect 61 passed

Rebase status (deferred)

basissetdev is ~160 commits behind origin/main as of this handover. The predecessor chat attempted a rebase at handover time; it hit a conflict on the very first basissetdev commit (861e2d0, the pob S d-polarisation fix on pob-tzvp.g94) and was aborted cleanly rather than fight 160 commits of conflict resolution while writing the handover. basissetdev’s tip is internally consistent and the test suite is green — the rebase is just deferred.

When you do rebase (recommended before writing Stage 0+ code, so you’re working against current main):

git fetch origin
git switch basissetdev
git pull --rebase origin main
# Expect ~5-10 conflicts. Known overlap zones:
#   * pob-tzvp.g94 (someone added Ne for a Ne-fcc regression
#     test on main; the S d-polarisation fix on basissetdev
#     touches a different region — keep both edits).
#   * CHANGELOG.md (basissetdev touches it; main touches it).
#   * cpp/src/{rhf,rks,uhf,uks}.cpp (basissetdev has the
#     total_ncore ECP work; main has the scf_mixing.hpp shared
#     helpers — keep both, per CLAUDE.md § 10 "no duplicate
#     implementations").
.venv/bin/python -m pytest tests/basisset_dev/ -q
(cd vibe-basis && ../.venv/bin/python -m pytest -q)
git push origin HEAD:basissetdev   # NOT --force; if it refuses,
                                    # something pushed concurrently
                                    # — fetch + repeat

If the rebase is too painful, an alternative is to cherry-pick just main’s “must-have” commits onto basissetdev rather than the full 160. For Goal 8 specifically, nothing on main is required: vibe-basis is standalone, the structure DB doesn’t depend on vibe-qc internals, and CRYSTAL14 is the SCF engine. The 160-behind is a hygiene gap, not a blocker — Stage 0 can run on the current tip.

If the new chat wants the optimizer extras, install incrementally as stages need them (avoids Python-3.14 wheel availability surprises on nlopt / iminuit blocking the whole setup):

# Stage 0 — needs vq
../.venv/bin/python -m pip install -e '.[vq]'

# Stage 1+ — needs nlopt + iminuit
../.venv/bin/python -m pip install -e '.[nlopt,iminuit]'

What’s next — the immediate task on resume

Goal 8 Stage 0 driver: vibe-basis/src/vibe_basis/recipes/pob_parity.py.

All four prerequisites are in place:

  1. ✅ Structure DB (vibe_basis.io.structures.STRUCTURES)

  2. ✅ Input emitter (vibe_basis.backends.crystal14.emit_input)

  3. ✅ Transport (vibe_basis.transports.VqTransport.run)

  4. ✅ Output parser (vibe_basis.backends.crystal14.parse_output_file)

The driver is conceptually small — it wires the pieces together and compares against PT2013 SI Table 2 reference energies (the HF totals already encoded as PT2013_T2_HF in examples/basisset_dev/_generator.py — should migrate that constant to vibe-basis too while you’re there, probably to vibe_basis.io.references or similar).

Pseudocode (the design doc is GOAL8_MPEI_TZVP.md §5 Stage 0):

def run_pob_parity(
    structures: list[Structure],            # default: 13 cubic ionics
    transport: Transport,                   # default: VqTransport()
    crystal_wrapper: Path,                  # path to run-crystal.sh on planetx
    workdir_root: Path,                     # dest for per-compound dirs
) -> StageReport:
    for s in structures:
        deck = emit_input(s, "pob-tzvp", "rhf")
        if deck is None: continue            # skip AFM / unsupported
        wd = workdir_root / s.name
        wd.mkdir(parents=True, exist_ok=True)
        (wd / f"{s.name}.d12").write_text(deck)
        cmd = ["bash", str(crystal_wrapper), f"{s.name}.d12"]
        result = transport.run(wd, cmd, wd / "fetched", cpus=14, wall_time_s=1800)
        parsed = parse_output_file(result.output_dir / f"{s.name}.out")
        delta = (parsed.energy - PT2013_T2_HF[s.name]) if parsed.ok else None
        report.add(s, parsed, delta)
    return report

Acceptance gate: Σᵢ|ΔEᵢ| < 0.1 mHa across the 13 cubic ionics. If it fails, the pipeline is wrong (basis-file encoding, SHRINK / TOLDEE / TOLINTEG defaults, or output parsing) — fix before proceeding to Stage 1+.

Note: 13 sequential submissions × ~30 s CRYSTAL14 wall = ~7 min total at --max-jobs 1. Cheap.

After Stage 0 passes, the order is:

  • Stage 1 — single-atom H HF BOBYQA (~30 evals, < 1 min wall)

  • Stage 2 — LiH single-compound HF BOBYQA + iminuit MIGRAD/HESSE (outreach gate, ~17 min wall)

  • Stage 3 — 13-compound joint HF fit (~6.7 h wall at --max-jobs 1)

  • Stage 4 — full test set, ATOMBSSE counterpoise (after R3)

All stages defined with acceptance gates in GOAL8_MPEI_TZVP.md §5.

Open todos (carried forward)

In rough priority order:

  1. Goal 8 Stage 0 driver (recipes/pob_parity.py) — see above.

  2. Goal 8 Stage 0 run on planetx — fire it; gate Σ|ΔE| < 0.1 mHa.

  3. parametrize.py in vibe-basis — basis ↔ flat log-space vector with hard bounds. Migrate from the in-vibe-qc scaffolding at python/vibeqc/basis_optimization/parametrise.py (still on basissetdev; was the Goal 4 design’s piece).

  4. optimize.py in vibe-basis — NLopt BOBYQA + iminuit MIGRAD/HESSE + scipy L-BFGS-B wrappers with a common OptResult.

  5. Goal 8 Stages 1-4 — the actual optimization runs.

  6. Phase 14g proper — C++ libecpint inline-primitive feed (deferred; focused C++ session).

  7. Goal 3 Phase 4 — hex/tet test set (blocked on R1).

  8. Goal 4 stages 2-4 — the pob-TZVP DFT reproduction via vibe-qc itself, deferred until vibe-qc periodic SCF reaches CRYSTAL14 parity. Goal 8 takes priority because HF unblocks today (no R4 PW1PW dep).

  9. Goal 5 — pob-*-jk aux basis design. Memory says this is DF-dev-chat territory; basissetdev coordinates but doesn’t own.

  10. Goal 6 — periodic metal basis (paper-worthy, parked).

Things you should NOT do

  • Don’t push basissetdev to origin/main. CLAUDE.md § 4 is explicit: basissetdev doesn’t merge into main during v0.8.x. If a discrete bug fix on basissetdev (like the S d-polarisation fix) ought to land on main, that’s a cherry-pick decision the maintainer makes — not a merge from this chat.

  • Don’t import vibeqc from vibe_basis. vibe-basis is deliberately standalone. The only allowed direction is vibe-qc vibe-basis (the _generator.py shim).

  • Don’t add import pyscf / psi4 / orca / crystal / any other QC program inside vibe-basis. CLAUDE.md § 10 applies recursively — vibe-basis treats CRYSTAL as an external program too; we shell out to vq submit ... bash run-crystal.sh ..., never link/import.

  • Don’t speculatively install vibe-basis[all] on planetx. Python 3.14 wheel availability for nlopt and iminuit is not guaranteed — install incrementally per the recipe above.

  • Don’t speculatively chase the “missing” gitignore for .release-status/. That’s the v0.8.0 release chat’s housekeeping; flagged in the predecessor chat’s reply but not basissetdev’s to fix.

Cross-chat coordination state

  • v0.8.0 release chat — owns .release-status/v0.8.0/ drop-box. basissetdev’s drop-box file (if it exists) documents this tip; the release chat reads it during Phase E.

  • periodic-SCF chat — owns the R1-R5 + R7 + R9 work in REQUIREMENTS-PERIODIC.md. They unblock Goal 8 Stage 4 (AFM via R3) and Goal 4 stages 2-4 (multi-k + PW1PW + lattice opt).

  • gradient-routing / cross-code parity chat (fix/grad-2e-uhfuks-routing branch) — owns HANDOVER_CROSS_CODE_PARITY.md (not on basissetdev). Predecessor chat received a misdirected v0.7.7 heads-up about a DF-gradient residual closure; redirected to the gradient chat without action. If you receive a similar ping, do the same.

  • DF dev chat — owns AutoAux + Goal 5 (pob-*-jk aux basis).

  • Documentation chat — owns docs/ cadence (CLAUDE.md § 5). When basissetdev features land on main eventually, the docs chat picks them up.

Where the work lives

  • Branch: origin/basissetdev (rebased onto fresh origin/main; do a git pull --rebase origin main when you start to absorb any new main commits).

  • Worktree on the laptop (this session’s): per the user’s worktree convention. The actual checkout the predecessor used is at .claude/worktrees/nostalgic-vaughan-1f3e11/ — feel free to ignore and start your own worktree.

  • Checkout on planetx: /home/mpei/gitlab/vibeqc-basis/, with .venv/ activated.

Files you’ll touch first

  • vibe-basis/src/vibe_basis/recipes/pob_parity.py — create.

  • vibe-basis/tests/test_pob_parity_recipe.py — create. Mock the transport (so tests run without vq / CRYSTAL14 / planetx access).

  • vibe-basis/src/vibe_basis/io/references.py — create, lift the PT2013_T2_HF dict from examples/basisset_dev/_generator.py.

  • vibe-basis/src/vibe_basis/cli.py — extend with a vb fit --recipe pob-parity ... verb that wraps the driver.

Sanity checklist when you start

# 1. Be on the right branch + tip.
git fetch origin
git checkout basissetdev      # or git switch basissetdev
git pull --rebase origin main

# 2. Confirm tests pass.
.venv/bin/python -m pytest tests/basisset_dev/ -q
(cd vibe-basis && ../.venv/bin/python -m pytest -q)

# 3. Confirm planetx vibe-basis install is current.
ssh planetx 'cd /home/mpei/gitlab/vibeqc-basis && git pull --ff-only origin basissetdev \
  && cd vibe-basis && ../.venv/bin/python -m pytest -q'

# 4. Re-read GOAL8_MPEI_TZVP.md §5 (Stage acceptance gates).

If any of those fail, fix before writing new code.

— predecessor chat (basissetdev), 2026-05-14